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    #[openmls_traits::maybe_async]
31    pub async fn next_vc_application_secret<Provider: OpenMlsProvider>(
32        &self,
33        provider: &Provider,
34        operation_context: &[u8],
35    ) -> Result<(VcApplicationSecretInfo, Vec<u8>), VirtualClientsError> {
36        let epoch_id =
37            require_newest_vc_derivation_epoch(provider.storage(), self.group_id()).await?;
38        let (state, mut operation_tree) = load_vc_epoch_state_and_tree(provider, &epoch_id).await?;
39        let (leaf_index, _epoch_encryption_key, emulation_ciphersuite) = state.into_parts();
40        let (generation, operation_secret) = operation_tree.next_operation_secret(
41            provider.crypto(),
42            emulation_ciphersuite,
43            &epoch_id,
44            leaf_index,
45            VirtualClientOperationType::Application,
46            operation_context,
47        )?;
48        provider
49            .storage()
50            .write_vc_operation_tree(&epoch_id, &operation_tree)
51            .await
52            .map_err(|e| {
53                log::error!(
54                    "vc: persist operation tree after allocating an application secret failed: {e:?}"
55                );
56                VirtualClientsError::StorageError
57            })?;
58        let info = VcApplicationSecretInfo {
59            epoch_id,
60            leaf_index,
61            generation,
62        };
63        Ok((info, operation_secret.as_slice().to_vec()))
64    }
65
66    /// Rederive the application secret a sibling emulator client of this
67    /// emulation group took from its own application ratchet, from the
68    /// [`VcApplicationSecretInfo`] it published and the `operation_context` the
69    /// application agreed on.
70    ///
71    /// The same concurrency requirement as for
72    /// [`Self::next_vc_application_secret`] applies.
73    #[openmls_traits::maybe_async]
74    pub async fn derive_vc_application_secret<Provider: OpenMlsProvider>(
75        &self,
76        provider: &Provider,
77        info: &VcApplicationSecretInfo,
78        operation_context: &[u8],
79    ) -> Result<Vec<u8>, VirtualClientsError> {
80        let (state, mut operation_tree) =
81            load_vc_epoch_state_and_tree(provider, &info.epoch_id).await?;
82        let (own_leaf_index, _epoch_encryption_key, emulation_ciphersuite) = state.into_parts();
83        if info.leaf_index == own_leaf_index {
84            log::error!("vc: application secret coordinates name the caller's own leaf index.");
85            return Err(VirtualClientsError::OwnLeafIndex);
86        }
87        let operation_secret = operation_tree.derive_operation_secret(
88            provider.crypto(),
89            emulation_ciphersuite,
90            &info.epoch_id,
91            info.leaf_index,
92            VirtualClientOperationType::Application,
93            info.generation,
94            operation_context,
95        )?;
96        provider
97            .storage()
98            .write_vc_operation_tree(&info.epoch_id, &operation_tree)
99            .await
100            .map_err(|e| {
101                log::error!(
102                    "vc: persist operation tree after rederiving an application secret failed: {e:?}"
103                );
104                VirtualClientsError::StorageError
105            })?;
106        Ok(operation_secret.as_slice().to_vec())
107    }
108}