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};
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 /// This would mean different things for one-time and last-resort Kyber keys.
131 async fn mark_kyber_pre_key_used(&mut self, kyber_prekey_id: KyberPreKeyId) -> Result<()>;
132}
133
134/// Interface for a Signal client instance to store a session associated with another particular
135/// separate Signal client instance.
136///
137/// This [SessionRecord] object between a pair of Signal clients is used to drive the state for the
138/// forward-secret message chain in the [Double Ratchet] protocol.
139///
140/// [Double Ratchet]: https://signal.org/docs/specifications/doubleratchet/
141#[async_trait(?Send)]
142pub trait SessionStore {
143 /// Look up the session corresponding to `address`.
144 async fn load_session(&self, address: &ProtocolAddress) -> Result<Option<SessionRecord>>;
145
146 /// Set the entry for `address` to the value of `record`.
147 async fn store_session(
148 &mut self,
149 address: &ProtocolAddress,
150 record: &SessionRecord,
151 ) -> Result<()>;
152}
153
154/// Interface for storing sender key records, allowing multiple keys per user.
155#[async_trait(?Send)]
156pub trait SenderKeyStore {
157 /// Assign `record` to the entry for `(sender, distribution_id)`.
158 async fn store_sender_key(
159 &mut self,
160 sender: &ProtocolAddress,
161 distribution_id: Uuid,
162 // TODO: pass this by value!
163 record: &SenderKeyRecord,
164 ) -> Result<()>;
165
166 /// Look up the entry corresponding to `(sender, distribution_id)`.
167 async fn load_sender_key(
168 &mut self,
169 sender: &ProtocolAddress,
170 distribution_id: Uuid,
171 ) -> Result<Option<SenderKeyRecord>>;
172}
173
174/// Mixes in all the store interfaces defined in this module.
175pub trait ProtocolStore:
176 SessionStore + PreKeyStore + SignedPreKeyStore + KyberPreKeyStore + IdentityKeyStore
177{
178}
179
180impl IdentityChange {
181 /// Convenience constructor from a boolean `changed` flag.
182 ///
183 /// Returns [`IdentityChange::ReplacedExisting`] if `changed` is `true`,
184 /// otherwise [`IdentityChange::NewOrUnchanged`].
185 pub fn from_changed(changed: bool) -> Self {
186 if changed {
187 Self::ReplacedExisting
188 } else {
189 Self::NewOrUnchanged
190 }
191 }
192}