Skip to main content

openmls/group/mls_group/
vc_application_secret.rs

1use crate::components::{
2    vc_application_secret::VcApplicationSecretInfo,
3    vc_derivation_info::{
4        load_vc_epoch_state_and_tree, require_newest_vc_derivation_epoch,
5        VirtualClientOperationType, VirtualClientsError,
6    },
7};
8
9use super::*;
10
11impl MlsGroup {
12    /// Use the `operation_context` to derive the next application secret from
13    /// the head of this client's own application ratchet in this emulation
14    /// group's newest derivation epoch.
15    ///
16    /// Returns the secret together with the [`VcApplicationSecretInfo`] a
17    /// sibling emulator client needs to rederive it with
18    /// [`Self::derive_vc_application_secret`]. The secret has the same length
19    /// as the emulation group's KDF hash.
20    ///
21    /// # Warning
22    ///
23    /// This consumes a generation of the operation secret tree, which is shared
24    /// by every operation of the virtual client. Two concurrent calls against
25    /// the same derivation epoch can allocate the same generation twice, so an
26    /// application that derives from several threads must serialize its calls.
27    /// See the `# Concurrency` note on [`OperationSecretTree`].
28    ///
29    /// [`OperationSecretTree`]: crate::components::vc_operation_tree::OperationSecretTree
30    pub fn next_vc_application_secret<Provider: OpenMlsProvider>(
31        &self,
32        provider: &Provider,
33        operation_context: &[u8],
34    ) -> Result<(VcApplicationSecretInfo, Vec<u8>), VirtualClientsError> {
35        let epoch_id = require_newest_vc_derivation_epoch(provider.storage(), self.group_id())?;
36        let (state, mut operation_tree) = load_vc_epoch_state_and_tree(provider, &epoch_id)?;
37        let (leaf_index, _epoch_encryption_key, emulation_ciphersuite) = state.into_parts();
38        let (generation, operation_secret) = operation_tree.next_operation_secret(
39            provider.crypto(),
40            emulation_ciphersuite,
41            &epoch_id,
42            leaf_index,
43            VirtualClientOperationType::Application,
44            operation_context,
45        )?;
46        provider
47            .storage()
48            .write_vc_operation_tree(&epoch_id, &operation_tree)
49            .map_err(|e| {
50                log::error!(
51                    "vc: persist operation tree after allocating an application secret failed: {e:?}"
52                );
53                VirtualClientsError::StorageError
54            })?;
55        let info = VcApplicationSecretInfo {
56            epoch_id,
57            leaf_index,
58            generation,
59        };
60        Ok((info, operation_secret.as_slice().to_vec()))
61    }
62
63    /// Rederive the application secret a sibling emulator client of this
64    /// emulation group took from its own application ratchet, from the
65    /// [`VcApplicationSecretInfo`] it published and the `operation_context` the
66    /// application agreed on.
67    ///
68    /// The same concurrency requirement as for
69    /// [`Self::next_vc_application_secret`] applies.
70    pub fn derive_vc_application_secret<Provider: OpenMlsProvider>(
71        &self,
72        provider: &Provider,
73        info: &VcApplicationSecretInfo,
74        operation_context: &[u8],
75    ) -> Result<Vec<u8>, VirtualClientsError> {
76        let (state, mut operation_tree) = load_vc_epoch_state_and_tree(provider, &info.epoch_id)?;
77        let (own_leaf_index, _epoch_encryption_key, emulation_ciphersuite) = state.into_parts();
78        if info.leaf_index == own_leaf_index {
79            log::error!("vc: application secret coordinates name the caller's own leaf index.");
80            return Err(VirtualClientsError::OwnLeafIndex);
81        }
82        let operation_secret = operation_tree.derive_operation_secret(
83            provider.crypto(),
84            emulation_ciphersuite,
85            &info.epoch_id,
86            info.leaf_index,
87            VirtualClientOperationType::Application,
88            info.generation,
89            operation_context,
90        )?;
91        provider
92            .storage()
93            .write_vc_operation_tree(&info.epoch_id, &operation_tree)
94            .map_err(|e| {
95                log::error!(
96                    "vc: persist operation tree after rederiving an application secret failed: {e:?}"
97                );
98                VirtualClientsError::StorageError
99            })?;
100        Ok(operation_secret.as_slice().to_vec())
101    }
102}