1use errors::{ExportGroupInfoError, ExportSecretError};
2use openmls_traits::{crypto::OpenMlsCrypto, signatures::Signer};
3
4use crate::{
5 ciphersuite::HpkePublicKey,
6 extensions::errors::InvalidExtensionError,
7 schedule::{EpochAuthenticator, ResumptionPskSecret},
8};
9#[cfg(feature = "extensions-draft")]
10use crate::{
11 component::ComponentId,
12 group::{PendingSafeExportSecretError, SafeExportSecretError},
13};
14
15#[cfg(feature = "virtual-clients-draft")]
16use crate::{
17 components::vc_derivation_info::{
18 EmulationEpochState, EmulatorEpochSecret, EpochId, VC_COMPONENT_ID,
19 },
20 components::vc_operation_tree::OperationSecretTree,
21 group::mls_group::errors::RegisterVcEmulationEpochError,
22};
23
24use super::*;
25
26impl MlsGroup {
27 pub fn export_secret<CryptoProvider: OpenMlsCrypto>(
35 &self,
36 crypto: &CryptoProvider,
37 label: &str,
38 context: &[u8],
39 key_length: usize,
40 ) -> Result<Vec<u8>, ExportSecretError> {
41 if key_length > u16::MAX as usize {
42 log::error!("Got a key that is larger than u16::MAX");
43 return Err(ExportSecretError::KeyLengthTooLong);
44 }
45
46 if self.is_active() {
47 Ok(self
48 .group_epoch_secrets
49 .exporter_secret()
50 .derive_exported_secret(self.ciphersuite(), crypto, label, context, key_length)
51 .map_err(LibraryError::unexpected_crypto_error)?)
52 } else {
53 Err(ExportSecretError::GroupStateError(
54 MlsGroupStateError::UseAfterEviction,
55 ))
56 }
57 }
58
59 #[cfg(feature = "extensions-draft")]
62 pub fn safe_export_secret<Crypto: OpenMlsCrypto, Storage: StorageProvider>(
63 &mut self,
64 crypto: &Crypto,
65 storage: &Storage,
66 component_id: ComponentId,
67 ) -> Result<Vec<u8>, SafeExportSecretError<Storage::Error>> {
68 if !self.is_active() {
69 return Err(SafeExportSecretError::GroupState(
70 MlsGroupStateError::UseAfterEviction,
71 ));
72 }
73 let group_id = self.public_group.group_id();
74 let ciphersuite = self.ciphersuite();
75 let Some(application_export_tree) = self.application_export_tree.as_mut() else {
76 return Err(SafeExportSecretError::Unsupported);
77 };
78 let component_secret =
79 application_export_tree.safe_export_secret(crypto, ciphersuite, component_id)?;
80 storage
81 .write_application_export_tree(group_id, application_export_tree)
82 .map_err(SafeExportSecretError::Storage)?;
83
84 Ok(component_secret.as_slice().to_vec())
85 }
86
87 #[cfg(feature = "extensions-draft")]
90 pub fn safe_export_secret_from_pending<Provider: StorageProvider>(
91 &mut self,
92 crypto: &impl OpenMlsCrypto,
93 storage: &Provider,
94 component_id: ComponentId,
95 ) -> Result<Vec<u8>, PendingSafeExportSecretError<Provider::Error>> {
96 let group_id = self.group_id().clone();
97 let MlsGroupState::PendingCommit(ref mut group_state) = self.group_state else {
98 return Err(PendingSafeExportSecretError::NoPendingCommit);
99 };
100 let PendingCommitState::Member(ref mut staged_commit) = **group_state else {
101 return Err(PendingSafeExportSecretError::NotGroupMember);
102 };
103 let secret = staged_commit.safe_export_secret(crypto, component_id)?;
104 storage
105 .write_group_state(&group_id, &self.group_state)
106 .map_err(PendingSafeExportSecretError::Storage)?;
107 Ok(secret.as_slice().to_vec())
108 }
109
110 #[cfg(feature = "virtual-clients-draft")]
128 pub fn register_vc_emulation_epoch<Crypto: OpenMlsCrypto, Storage: StorageProvider>(
129 &mut self,
130 crypto: &Crypto,
131 storage: &Storage,
132 ) -> Result<EpochId, RegisterVcEmulationEpochError<Storage::Error>> {
133 let ciphersuite = self.ciphersuite();
134 let leaf_index = self.own_leaf_index();
135 let emulation_group_size = self.public_group().tree_size();
136 let bytes = self.safe_export_secret(crypto, storage, VC_COMPONENT_ID)?;
137 let emulator_epoch_secret = EmulatorEpochSecret::new(&bytes);
138 let epoch_id = emulator_epoch_secret.derive_epoch_id(crypto, ciphersuite)?;
139 let epoch_encryption_key =
140 emulator_epoch_secret.derive_epoch_encryption_key(crypto, ciphersuite)?;
141 let epoch_base_secret =
142 emulator_epoch_secret.derive_epoch_base_secret(crypto, ciphersuite)?;
143 let reuse_guard_secret =
144 emulator_epoch_secret.derive_reuse_guard_secret(crypto, ciphersuite)?;
145 let generation_id_secret =
146 emulator_epoch_secret.derive_generation_id_secret(crypto, ciphersuite)?;
147 let operation_tree = OperationSecretTree::new(epoch_base_secret, emulation_group_size);
148 let state = EmulationEpochState::new(
149 leaf_index,
150 epoch_encryption_key,
151 reuse_guard_secret,
152 generation_id_secret,
153 emulation_group_size,
154 ciphersuite,
155 );
156
157 storage
158 .write_vc_operation_tree(&epoch_id, &operation_tree)
159 .map_err(|e| {
160 log::error!(
161 "vc: persist operation tree in register_vc_emulation_epoch failed: {e:?}"
162 );
163 RegisterVcEmulationEpochError::Storage(e)
164 })?;
165 storage
166 .write_vc_emulation_epoch_state(&epoch_id, &state)
167 .map_err(|e| {
168 log::error!(
169 "vc: persist emulation epoch state in register_vc_emulation_epoch failed: {e:?}"
170 );
171 RegisterVcEmulationEpochError::Storage(e)
172 })?;
173
174 Ok(epoch_id)
175 }
176
177 pub fn epoch_authenticator(&self) -> &EpochAuthenticator {
179 self.group_epoch_secrets().epoch_authenticator()
180 }
181
182 pub fn resumption_psk_secret(&self) -> &ResumptionPskSecret {
184 self.group_epoch_secrets().resumption_psk()
185 }
186
187 pub fn get_past_resumption_psk(&self, epoch: GroupEpoch) -> Option<&ResumptionPskSecret> {
190 self.resumption_psk_store.get(epoch)
191 }
192
193 pub fn export_group_info<CryptoProvider: OpenMlsCrypto>(
195 &self,
196 crypto: &CryptoProvider,
197 signer: &impl Signer,
198 with_ratchet_tree: bool,
199 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
200 self.export_group_info_with_additional_extensions(crypto, signer, with_ratchet_tree, None)
201 }
202
203 pub fn export_group_info_with_additional_extensions<CryptoProvider: OpenMlsCrypto>(
208 &self,
209 crypto: &CryptoProvider,
210 signer: &impl Signer,
211 with_ratchet_tree: bool,
212 additional_extensions: impl IntoIterator<Item = Extension>,
213 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
214 let extensions = {
215 let ratchet_tree_extension = || {
216 Extension::RatchetTree(RatchetTreeExtension::new(
217 self.public_group().export_ratchet_tree(),
218 ))
219 };
220
221 let external_pub_extension = || -> Result<Extension, ExportGroupInfoError> {
222 let external_pub = self
223 .group_epoch_secrets()
224 .external_secret()
225 .derive_external_keypair(crypto, self.ciphersuite())
226 .map_err(LibraryError::unexpected_crypto_error)?
227 .public;
228 Ok(Extension::ExternalPub(ExternalPubExtension::new(
229 HpkePublicKey::from(external_pub),
230 )))
231 };
232
233 let mut extensions = if with_ratchet_tree {
234 vec![ratchet_tree_extension(), external_pub_extension()?]
235 } else {
236 vec![external_pub_extension()?]
237 };
238
239 extensions.extend(
240 additional_extensions
241 .into_iter()
242 .map(|extension| {
243 if extension.as_ratchet_tree_extension().is_ok()
244 || extension.as_external_pub_extension().is_ok()
245 {
246 Err(InvalidExtensionError::CannotAddDirectlyToGroupInfo)
247 } else {
248 Ok(extension)
249 }
250 })
251 .collect::<Result<Vec<_>, _>>()?,
252 );
253
254 Extensions::from_vec(extensions)?
255 };
256
257 let group_info_tbs = GroupInfoTBS::new(
259 self.context().clone(),
260 extensions,
261 self.message_secrets()
262 .confirmation_key()
263 .tag(
264 crypto,
265 self.ciphersuite(),
266 self.context().confirmed_transcript_hash(),
267 )
268 .map_err(LibraryError::unexpected_crypto_error)?,
269 self.own_leaf_index(),
270 )?;
271
272 let group_info = group_info_tbs
274 .sign(signer)
275 .map_err(|_| LibraryError::custom("Signing failed"))?;
276 Ok(group_info.into())
277 }
278}