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