Skip to main content

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