openmls/
storage.rs

1//! OpenMLS Storage
2//!
3//! This module serves two purposes:
4//!
5//! - It implements the Key, Entity and type traits from `openmls_traits::storage::traits`.
6//! - It defines traits that specialize the Storage and Provider traits from `openmls_traits`.
7//!   This way, the Rust compiler knows that the concrete types match when we use the Provider in
8//!   the code.
9
10use openmls_traits::storage::{traits, Entity, Key, CURRENT_VERSION};
11
12use crate::binary_tree::LeafNodeIndex;
13use crate::group::proposal_store::QueuedProposal;
14use crate::group::{MlsGroupJoinConfig, MlsGroupState};
15use crate::{
16    ciphersuite::hash_ref::ProposalRef,
17    group::{GroupContext, GroupId, InterimTranscriptHash},
18    messages::ConfirmationTag,
19    treesync::{LeafNode, TreeSync},
20};
21use crate::{
22    group::{past_secrets::MessageSecretsStore, GroupEpoch},
23    prelude::KeyPackageBundle,
24    schedule::{
25        psk::{store::ResumptionPskStore, PskBundle},
26        GroupEpochSecrets, Psk,
27    },
28    treesync::{node::encryption_keys::EncryptionKeyPair, EncryptionKey},
29};
30
31#[cfg(test)]
32pub mod kat_storage_stability;
33
34/// A convenience trait for the current version of the storage.
35/// Throughout the code, this one should be used instead of `openmls_traits::storage::StorageProvider`.
36pub trait StorageProvider: openmls_traits::storage::StorageProvider<CURRENT_VERSION> {}
37
38/// A convenience trait for the current version of the public storage.
39/// Throughout the code, this one should be used instead of `openmls_traits::public_storage::PublicStorageProvider`.
40pub trait PublicStorageProvider:
41    openmls_traits::public_storage::PublicStorageProvider<
42    CURRENT_VERSION,
43    PublicError = <Self as PublicStorageProvider>::Error,
44>
45{
46    /// An opaque error returned by all methods on this trait.
47    /// Matches `PublicError` from `openmls_traits::storage::PublicStorageProvider`.
48    type Error: core::fmt::Debug + std::error::Error;
49}
50
51impl<P: openmls_traits::storage::StorageProvider<CURRENT_VERSION>> StorageProvider for P {}
52
53impl<P: openmls_traits::public_storage::PublicStorageProvider<CURRENT_VERSION>>
54    PublicStorageProvider for P
55{
56    type Error = P::PublicError;
57}
58
59/// A convenience trait for the OpenMLS provider that defines the storage provider
60/// for the current version of storage.
61/// Throughout the code, this one should be used instead of `openmls_traits::OpenMlsProvider`.
62pub trait OpenMlsProvider:
63    openmls_traits::OpenMlsProvider<StorageProvider = Self::Storage>
64{
65    /// The storage to use
66    type Storage: StorageProvider<Error = Self::StorageError>;
67    /// The storage error type
68    type StorageError: std::error::Error;
69}
70
71impl<
72        Error: std::error::Error,
73        SP: StorageProvider<Error = Error>,
74        OP: openmls_traits::OpenMlsProvider<StorageProvider = SP>,
75    > OpenMlsProvider for OP
76{
77    type Storage = SP;
78    type StorageError = Error;
79}
80
81// Implementations for the Entity and Key traits
82
83impl Entity<CURRENT_VERSION> for QueuedProposal {}
84impl traits::QueuedProposal<CURRENT_VERSION> for QueuedProposal {}
85
86impl Entity<CURRENT_VERSION> for TreeSync {}
87impl traits::TreeSync<CURRENT_VERSION> for TreeSync {}
88
89impl Key<CURRENT_VERSION> for GroupId {}
90impl traits::GroupId<CURRENT_VERSION> for GroupId {}
91
92impl Key<CURRENT_VERSION> for ProposalRef {}
93impl Entity<CURRENT_VERSION> for ProposalRef {}
94impl traits::ProposalRef<CURRENT_VERSION> for ProposalRef {}
95impl traits::HashReference<CURRENT_VERSION> for ProposalRef {}
96
97impl Entity<CURRENT_VERSION> for GroupContext {}
98impl traits::GroupContext<CURRENT_VERSION> for GroupContext {}
99
100impl Entity<CURRENT_VERSION> for InterimTranscriptHash {}
101impl traits::InterimTranscriptHash<CURRENT_VERSION> for InterimTranscriptHash {}
102
103impl Entity<CURRENT_VERSION> for ConfirmationTag {}
104impl traits::ConfirmationTag<CURRENT_VERSION> for ConfirmationTag {}
105
106impl Entity<CURRENT_VERSION> for KeyPackageBundle {}
107impl traits::KeyPackage<CURRENT_VERSION> for KeyPackageBundle {}
108
109impl Key<CURRENT_VERSION> for EncryptionKey {}
110impl traits::EncryptionKey<CURRENT_VERSION> for EncryptionKey {}
111
112impl Entity<CURRENT_VERSION> for EncryptionKeyPair {}
113impl traits::HpkeKeyPair<CURRENT_VERSION> for EncryptionKeyPair {}
114
115impl Entity<CURRENT_VERSION> for LeafNodeIndex {}
116impl traits::LeafNodeIndex<CURRENT_VERSION> for LeafNodeIndex {}
117
118impl Entity<CURRENT_VERSION> for GroupEpochSecrets {}
119impl traits::GroupEpochSecrets<CURRENT_VERSION> for GroupEpochSecrets {}
120
121impl Entity<CURRENT_VERSION> for MessageSecretsStore {}
122impl traits::MessageSecrets<CURRENT_VERSION> for MessageSecretsStore {}
123
124impl Entity<CURRENT_VERSION> for ResumptionPskStore {}
125impl traits::ResumptionPskStore<CURRENT_VERSION> for ResumptionPskStore {}
126
127impl Entity<CURRENT_VERSION> for MlsGroupJoinConfig {}
128impl traits::MlsGroupJoinConfig<CURRENT_VERSION> for MlsGroupJoinConfig {}
129
130impl Entity<CURRENT_VERSION> for MlsGroupState {}
131impl traits::GroupState<CURRENT_VERSION> for MlsGroupState {}
132
133impl Entity<CURRENT_VERSION> for LeafNode {}
134impl traits::LeafNode<CURRENT_VERSION> for LeafNode {}
135
136// Crypto
137
138impl Key<CURRENT_VERSION> for GroupEpoch {}
139impl traits::EpochKey<CURRENT_VERSION> for GroupEpoch {}
140
141impl Key<CURRENT_VERSION> for Psk {}
142impl traits::PskId<CURRENT_VERSION> for Psk {}
143
144impl Entity<CURRENT_VERSION> for PskBundle {}
145impl traits::PskBundle<CURRENT_VERSION> for PskBundle {}
146
147#[cfg(test)]
148mod test {
149    use crate::{
150        group::mls_group::tests_and_kats::utils::setup_client, prelude::KeyPackageBuilder,
151    };
152
153    use super::*;
154
155    use openmls_rust_crypto::{MemoryStorage, OpenMlsRustCrypto};
156    use openmls_traits::{
157        storage::{traits as type_traits, StorageProvider, V_TEST},
158        types::{Ciphersuite, HpkePrivateKey},
159        OpenMlsProvider,
160    };
161    use serde::{Deserialize, Serialize};
162
163    // Test upgrade path
164    // Assume we have a new key package bundle representation.
165    #[derive(Serialize, Deserialize)]
166    struct NewKeyPackageBundle {
167        ciphersuite: Ciphersuite,
168        key_package: crate::key_packages::KeyPackage,
169        private_init_key: HpkePrivateKey,
170        private_encryption_key: crate::treesync::node::encryption_keys::EncryptionPrivateKey,
171    }
172
173    impl Entity<V_TEST> for NewKeyPackageBundle {}
174    impl type_traits::KeyPackage<V_TEST> for NewKeyPackageBundle {}
175
176    impl Key<V_TEST> for EncryptionKey {}
177    impl type_traits::EncryptionKey<V_TEST> for EncryptionKey {}
178
179    impl Entity<V_TEST> for EncryptionKeyPair {}
180    impl type_traits::HpkeKeyPair<V_TEST> for EncryptionKeyPair {}
181
182    impl Key<V_TEST> for ProposalRef {}
183    impl type_traits::HashReference<V_TEST> for ProposalRef {}
184
185    #[test]
186    fn key_packages_key_upgrade() {
187        // Store an old version
188        let provider = OpenMlsRustCrypto::default();
189
190        let (credential_with_key, _kpb, signer, _pk) = setup_client(
191            "Alice",
192            Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519,
193            &provider,
194        );
195
196        // build and store key package bundle
197        let key_package_bundle = KeyPackageBuilder::new()
198            .build(
199                Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519,
200                &provider,
201                &signer,
202                credential_with_key,
203            )
204            .unwrap();
205
206        let key_package = key_package_bundle.key_package();
207        let key_package_ref = key_package.hash_ref(provider.crypto()).unwrap();
208
209        // TODO #1566: Serialize the old storage. This should become a kat test file
210
211        // ---- migration starts here ----
212        let new_storage_provider = MemoryStorage::default();
213
214        // first, read the old data
215        let read_key_package_bundle: crate::prelude::KeyPackageBundle =
216            <MemoryStorage as StorageProvider<CURRENT_VERSION>>::key_package(
217                provider.storage(),
218                &key_package_ref,
219            )
220            .unwrap()
221            .unwrap();
222
223        // then, build the new data from the old data
224        let new_key_package_bundle = NewKeyPackageBundle {
225            ciphersuite: read_key_package_bundle.key_package().ciphersuite(),
226            key_package: read_key_package_bundle.key_package().clone(),
227            private_init_key: read_key_package_bundle.init_private_key().clone(),
228            private_encryption_key: read_key_package_bundle.private_encryption_key.clone(),
229        };
230
231        // insert the data in the new format
232        <MemoryStorage as StorageProvider<V_TEST>>::write_key_package(
233            &new_storage_provider,
234            &key_package_ref,
235            &new_key_package_bundle,
236        )
237        .unwrap();
238
239        // read the new value from storage
240        let read_new_key_package_bundle: NewKeyPackageBundle =
241            <MemoryStorage as StorageProvider<V_TEST>>::key_package(
242                &new_storage_provider,
243                &key_package_ref,
244            )
245            .unwrap()
246            .unwrap();
247
248        // compare it to the old_storage
249
250        assert_eq!(
251            &read_new_key_package_bundle.key_package,
252            key_package_bundle.key_package()
253        );
254        assert_eq!(
255            read_new_key_package_bundle.ciphersuite,
256            key_package_bundle.key_package().ciphersuite()
257        );
258        assert_eq!(
259            &read_new_key_package_bundle.private_encryption_key,
260            &key_package_bundle.private_encryption_key
261        );
262        assert_eq!(
263            &read_new_key_package_bundle.private_init_key,
264            &key_package_bundle.private_init_key
265        );
266    }
267}