Skip to main content

openmls/group/mls_group/
migration_import.rs

1use super::*;
2
3/// A group and all of the group-associated data it owns, deserialized from a
4/// previous OpenMLS version for migration. The mirror of the older version's
5/// `MlsGroup::export_for_migration`.
6#[cfg_attr(
7    all(feature = "test-utils", feature = "migration-import"),
8    derive(serde::Serialize)
9)]
10#[derive(serde::Deserialize)]
11pub struct GroupMigrationBundle {
12    group: MlsGroup,
13    epoch_encryption_key_pairs: Vec<EncryptionKeyPair>,
14    update_encryption_key_pairs: Vec<EncryptionKeyPair>,
15}
16
17impl MlsGroup {
18    /// Store a group as part of a storage migration.
19    ///
20    /// This replaces the group's persisted state for its [`GroupId`]: the
21    /// single-value tables are overwritten, and the accumulate-style tables (the
22    /// own leaf nodes and the proposal queue) are cleared before being rewritten.
23    ///
24    /// **NOTE**: This helper only addresses entries under the current key encoding. When the key
25    /// encoding differs between versions (e.g. postcard → JSON), old-format entries
26    /// live under different keys and are not changed; remove those with the
27    /// older version's `MlsGroup::delete`.
28    pub(crate) fn store_for_migration<Storage: crate::storage::StorageProvider>(
29        &self,
30        storage: &Storage,
31    ) -> Result<(), Storage::Error> {
32        self.public_group.store_for_migration(storage)?;
33        storage.write_group_epoch_secrets(self.group_id(), &self.group_epoch_secrets)?;
34        storage.write_own_leaf_index(self.group_id(), &self.own_leaf_index)?;
35        storage.write_message_secrets(self.group_id(), &self.message_secrets_store)?;
36        storage.write_resumption_psk_store(self.group_id(), &self.resumption_psk_store)?;
37        storage.write_mls_join_config(self.group_id(), &self.mls_group_config)?;
38
39        // clear `own_leaf_nodes`, and rewrite one-by-one
40        storage.delete_own_leaf_nodes(self.group_id())?;
41        for leaf_node in self.own_leaf_nodes.iter() {
42            storage.append_own_leaf_node(self.group_id(), leaf_node)?;
43        }
44
45        storage.write_group_state(self.group_id(), &self.group_state)?;
46        #[cfg(feature = "extensions-draft")]
47        match &self.application_export_tree {
48            Some(application_export_tree) => {
49                storage.write_application_export_tree(self.group_id(), application_export_tree)?;
50            }
51            // No tree to write (migrating in from a version/config without
52            // `extensions-draft`, or a migration whose source has none). Delete
53            // any stale entry so the group's persisted state is fully replaced
54            // rather than left pointing at a previously stored tree.
55            None => {
56                storage
57                    .delete_application_export_tree::<_, ApplicationExportTree>(self.group_id())?;
58            }
59        }
60
61        Ok(())
62    }
63}
64
65impl GroupMigrationBundle {
66    /// Store the migrated group and all of its group-associated data in the
67    /// current storage format.
68    ///
69    /// **NOTE**: This replaces any existing state for the group's [`GroupId`], under the same [`GroupId`] encoding.
70    ///
71    /// Single-value tables are overwritten and the accumulate-style data
72    /// (own leaf nodes, proposal queue) are cleared before writing.
73    ///
74    /// Old-format entries under a different key encoding are not changed.
75    pub fn store<Storage: crate::storage::StorageProvider>(
76        &self,
77        storage: &Storage,
78    ) -> Result<(), Storage::Error> {
79        // Group state (config, tree, context, proposals, secrets, ...).
80        self.group.store_for_migration(storage)?;
81
82        // The group's own encryption key pairs for the current epoch.
83        storage.write_encryption_epoch_key_pairs(
84            self.group.group_id(),
85            &self.group.epoch(),
86            self.group.own_leaf_index().u32(),
87            &self.epoch_encryption_key_pairs,
88        )?;
89
90        // Encryption key pairs for pending (uncommitted) update leaf nodes.
91        for key_pair in &self.update_encryption_key_pairs {
92            key_pair.write(storage)?;
93        }
94
95        Ok(())
96    }
97}