libsignal_protocol/storage/traits.rs
1//
2// Copyright 2020-2022 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6//! Traits defining several stores used throughout the Signal Protocol.
7
8use async_trait::async_trait;
9use uuid::Uuid;
10
11use crate::error::Result;
12use crate::sender_keys::SenderKeyRecord;
13use crate::state::{
14 KyberPreKeyId, KyberPreKeyRecord, PreKeyId, PreKeyRecord, SessionRecord, SignedPreKeyId,
15 SignedPreKeyRecord,
16};
17use crate::{IdentityKey, IdentityKeyPair, ProtocolAddress, PublicKey};
18
19// TODO: consider moving this enum into utils.rs?
20/// Each Signal message can be considered to have exactly two participants, a sender and receiver.
21///
22/// [IdentityKeyStore::is_trusted_identity] uses this to ensure the identity provided is configured
23/// for the appropriate role.
24#[derive(Debug, Clone, Eq, PartialEq)]
25pub enum Direction {
26 /// We are in the context of sending a message.
27 Sending,
28 /// We are in the context of receiving a message.
29 Receiving,
30}
31
32/// The result of saving a new identity key for a protocol address.
33#[derive(Copy, Clone, Debug, Eq, PartialEq, derive_more::TryFrom)]
34#[repr(C)]
35#[try_from(repr)]
36pub enum IdentityChange {
37 /// The protocol address didn't have an identity key or had the same key.
38 NewOrUnchanged,
39 /// The new identity key replaced a different key for the protocol address.
40 ReplacedExisting,
41}
42
43/// Interface defining the identity store, which may be in-memory, on-disk, etc.
44///
45/// Signal clients usually use the identity store in a [TOFU] manner, but this is not required.
46///
47/// [TOFU]: https://en.wikipedia.org/wiki/Trust_on_first_use
48#[async_trait(?Send)]
49pub trait IdentityKeyStore {
50 /// Return the single specific identity the store is assumed to represent, with private key.
51 async fn get_identity_key_pair(&self) -> Result<IdentityKeyPair>;
52
53 /// Return a [u32] specific to this store instance.
54 ///
55 /// This local registration id is separate from the per-device identifier used in
56 /// [ProtocolAddress] and should not change run over run.
57 ///
58 /// If the same *device* is unregistered, then registers again, the [ProtocolAddress::device_id]
59 /// may be the same, but the store registration id returned by this method should
60 /// be regenerated.
61 async fn get_local_registration_id(&self) -> Result<u32>;
62
63 /// Record an identity into the store. The identity is then considered "trusted".
64 ///
65 /// The return value represents whether an existing identity was replaced.
66 async fn save_identity(
67 &mut self,
68 address: &ProtocolAddress,
69 identity: &IdentityKey,
70 ) -> Result<IdentityChange>;
71
72 /// Return whether an identity is trusted for the role specified by `direction`.
73 async fn is_trusted_identity(
74 &self,
75 address: &ProtocolAddress,
76 identity: &IdentityKey,
77 direction: Direction,
78 ) -> Result<bool>;
79
80 /// Return the public identity for the given `address`, if known.
81 async fn get_identity(&self, address: &ProtocolAddress) -> Result<Option<IdentityKey>>;
82}
83
84/// Interface for storing pre-keys downloaded from a server.
85#[async_trait(?Send)]
86pub trait PreKeyStore {
87 /// Look up the pre-key corresponding to `prekey_id`.
88 async fn get_pre_key(&self, prekey_id: PreKeyId) -> Result<PreKeyRecord>;
89
90 /// Set the entry for `prekey_id` to the value of `record`.
91 async fn save_pre_key(&mut self, prekey_id: PreKeyId, record: &PreKeyRecord) -> Result<()>;
92
93 /// Remove the entry for `prekey_id`.
94 async fn remove_pre_key(&mut self, prekey_id: PreKeyId) -> Result<()>;
95}
96
97/// Interface for storing signed pre-keys downloaded from a server.
98#[async_trait(?Send)]
99pub trait SignedPreKeyStore {
100 /// Look up the signed pre-key corresponding to `signed_prekey_id`.
101 async fn get_signed_pre_key(
102 &self,
103 signed_prekey_id: SignedPreKeyId,
104 ) -> Result<SignedPreKeyRecord>;
105
106 /// Set the entry for `signed_prekey_id` to the value of `record`.
107 async fn save_signed_pre_key(
108 &mut self,
109 signed_prekey_id: SignedPreKeyId,
110 record: &SignedPreKeyRecord,
111 ) -> Result<()>;
112}
113
114/// Interface for storing signed Kyber pre-keys downloaded from a server.
115///
116/// NB: libsignal makes no distinction between one-time and last-resort pre-keys.
117#[async_trait(?Send)]
118pub trait KyberPreKeyStore {
119 /// Look up the signed kyber pre-key corresponding to `kyber_prekey_id`.
120 async fn get_kyber_pre_key(&self, kyber_prekey_id: KyberPreKeyId) -> Result<KyberPreKeyRecord>;
121
122 /// Set the entry for `kyber_prekey_id` to the value of `record`.
123 async fn save_kyber_pre_key(
124 &mut self,
125 kyber_prekey_id: KyberPreKeyId,
126 record: &KyberPreKeyRecord,
127 ) -> Result<()>;
128
129 /// Mark the entry for `kyber_prekey_id` as "used".
130 ///
131 /// A one-time Kyber pre-key should be deleted after this point. A last-resort pre-key should
132 /// not immediately be deleted, but should check whether the same combination of pre-keys was
133 /// used with the given base key before, and produce an error if so.
134 async fn mark_kyber_pre_key_used(
135 &mut self,
136 kyber_prekey_id: KyberPreKeyId,
137 ec_prekey_id: SignedPreKeyId,
138 base_key: &PublicKey,
139 ) -> Result<()>;
140}
141
142/// Interface for a Signal client instance to store a session associated with another particular
143/// separate Signal client instance.
144///
145/// This [SessionRecord] object between a pair of Signal clients is used to drive the state for the
146/// forward-secret message chain in the [Double Ratchet] protocol.
147///
148/// [Double Ratchet]: https://signal.org/docs/specifications/doubleratchet/
149#[async_trait(?Send)]
150pub trait SessionStore {
151 /// Look up the session corresponding to `address`.
152 async fn load_session(&self, address: &ProtocolAddress) -> Result<Option<SessionRecord>>;
153
154 /// Set the entry for `address` to the value of `record`.
155 async fn store_session(
156 &mut self,
157 address: &ProtocolAddress,
158 record: &SessionRecord,
159 ) -> Result<()>;
160}
161
162/// Interface for storing sender key records, allowing multiple keys per user.
163#[async_trait(?Send)]
164pub trait SenderKeyStore {
165 /// Assign `record` to the entry for `(sender, distribution_id)`.
166 async fn store_sender_key(
167 &mut self,
168 sender: &ProtocolAddress,
169 distribution_id: Uuid,
170 // TODO: pass this by value!
171 record: &SenderKeyRecord,
172 ) -> Result<()>;
173
174 /// Look up the entry corresponding to `(sender, distribution_id)`.
175 async fn load_sender_key(
176 &mut self,
177 sender: &ProtocolAddress,
178 distribution_id: Uuid,
179 ) -> Result<Option<SenderKeyRecord>>;
180}
181
182/// Mixes in all the store interfaces defined in this module.
183pub trait ProtocolStore:
184 SessionStore + PreKeyStore + SignedPreKeyStore + KyberPreKeyStore + IdentityKeyStore
185{
186}
187
188impl IdentityChange {
189 /// Convenience constructor from a boolean `changed` flag.
190 ///
191 /// Returns [`IdentityChange::ReplacedExisting`] if `changed` is `true`,
192 /// otherwise [`IdentityChange::NewOrUnchanged`].
193 pub fn from_changed(changed: bool) -> Self {
194 if changed {
195 Self::ReplacedExisting
196 } else {
197 Self::NewOrUnchanged
198 }
199 }
200}