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}