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, RegisteredVcEmulationEpoch,
19 VC_COMPONENT_ID,
20 },
21 components::vc_operation_tree::OperationSecretTree,
22 group::mls_group::errors::RegisterVcEmulationEpochError,
23};
24
25use super::*;
26
27impl MlsGroup {
28 pub fn export_secret<CryptoProvider: OpenMlsCrypto>(
36 &self,
37 crypto: &CryptoProvider,
38 label: &str,
39 context: &[u8],
40 key_length: usize,
41 ) -> Result<Vec<u8>, ExportSecretError> {
42 if key_length > u16::MAX as usize {
43 log::error!("Got a key that is larger than u16::MAX");
44 return Err(ExportSecretError::KeyLengthTooLong);
45 }
46
47 if self.is_active() {
48 Ok(self
49 .group_epoch_secrets
50 .exporter_secret()
51 .derive_exported_secret(self.ciphersuite(), crypto, label, context, key_length)
52 .map_err(LibraryError::unexpected_crypto_error)?)
53 } else {
54 Err(ExportSecretError::GroupStateError(
55 MlsGroupStateError::UseAfterEviction,
56 ))
57 }
58 }
59
60 #[cfg(feature = "extensions-draft")]
63 pub fn safe_export_secret<Crypto: OpenMlsCrypto, Storage: StorageProvider>(
64 &mut self,
65 crypto: &Crypto,
66 storage: &Storage,
67 component_id: ComponentId,
68 ) -> Result<Vec<u8>, SafeExportSecretError<Storage::Error>> {
69 if !self.is_active() {
70 return Err(SafeExportSecretError::GroupState(
71 MlsGroupStateError::UseAfterEviction,
72 ));
73 }
74 let group_id = self.public_group.group_id();
75 let ciphersuite = self.ciphersuite();
76 let Some(application_export_tree) = self.application_export_tree.as_mut() else {
77 return Err(SafeExportSecretError::Unsupported);
78 };
79 let component_secret =
80 application_export_tree.safe_export_secret(crypto, ciphersuite, component_id)?;
81 storage
82 .write_application_export_tree(group_id, application_export_tree)
83 .map_err(SafeExportSecretError::Storage)?;
84
85 Ok(component_secret.as_slice().to_vec())
86 }
87
88 #[cfg(feature = "extensions-draft")]
91 pub fn safe_export_secret_from_pending<Provider: StorageProvider>(
92 &mut self,
93 crypto: &impl OpenMlsCrypto,
94 storage: &Provider,
95 component_id: ComponentId,
96 ) -> Result<Vec<u8>, PendingSafeExportSecretError<Provider::Error>> {
97 let group_id = self.group_id().clone();
98 let MlsGroupState::PendingCommit(ref mut group_state) = self.group_state else {
99 return Err(PendingSafeExportSecretError::NoPendingCommit);
100 };
101 let PendingCommitState::Member(ref mut staged_commit) = **group_state else {
102 return Err(PendingSafeExportSecretError::NotGroupMember);
103 };
104 let secret = staged_commit.safe_export_secret(crypto, component_id)?;
105 storage
106 .write_group_state(&group_id, &self.group_state)
107 .map_err(PendingSafeExportSecretError::Storage)?;
108 Ok(secret.as_slice().to_vec())
109 }
110
111 #[cfg(feature = "virtual-clients-draft")]
137 pub fn register_vc_emulation_epoch<Crypto: OpenMlsCrypto, Storage: StorageProvider>(
138 &mut self,
139 crypto: &Crypto,
140 storage: &Storage,
141 ) -> Result<EpochId, RegisterVcEmulationEpochError<Storage::Error>> {
142 let registered: Option<RegisteredVcEmulationEpoch> = storage
146 .registered_vc_emulation_epoch(self.group_id())
147 .map_err(|e| {
148 log::error!(
149 "vc: load registered emulation epoch in register_vc_emulation_epoch \
150 failed: {e:?}"
151 );
152 RegisterVcEmulationEpochError::Storage(e)
153 })?;
154 if let Some(registered) = registered {
155 if registered.group_epoch == self.epoch() {
156 return Ok(registered.epoch_id);
157 }
158 }
159
160 let ciphersuite = self.ciphersuite();
161 let leaf_index = self.own_leaf_index();
162 let emulation_group_size = self.public_group().tree_size();
163 let bytes = self.safe_export_secret(crypto, storage, VC_COMPONENT_ID)?;
164 let emulator_epoch_secret = EmulatorEpochSecret::new(&bytes);
165 let epoch_id = emulator_epoch_secret.derive_epoch_id(crypto, ciphersuite)?;
166 let epoch_encryption_key =
167 emulator_epoch_secret.derive_epoch_encryption_key(crypto, ciphersuite)?;
168 let epoch_base_secret =
169 emulator_epoch_secret.derive_epoch_base_secret(crypto, ciphersuite)?;
170 let reuse_guard_secret =
171 emulator_epoch_secret.derive_reuse_guard_secret(crypto, ciphersuite)?;
172 let generation_id_secret =
173 emulator_epoch_secret.derive_generation_id_secret(crypto, ciphersuite)?;
174 let operation_tree = OperationSecretTree::new(epoch_base_secret, emulation_group_size);
175 let state = EmulationEpochState::new(
176 leaf_index,
177 epoch_encryption_key,
178 reuse_guard_secret,
179 generation_id_secret,
180 emulation_group_size,
181 ciphersuite,
182 );
183 let registered = RegisteredVcEmulationEpoch {
184 group_epoch: self.epoch(),
185 epoch_id,
186 };
187
188 storage
189 .write_vc_operation_tree(®istered.epoch_id, &operation_tree)
190 .map_err(|e| {
191 log::error!(
192 "vc: persist operation tree in register_vc_emulation_epoch failed: {e:?}"
193 );
194 RegisterVcEmulationEpochError::Storage(e)
195 })?;
196 storage
197 .write_vc_emulation_epoch_state(®istered.epoch_id, &state)
198 .map_err(|e| {
199 log::error!(
200 "vc: persist emulation epoch state in register_vc_emulation_epoch failed: {e:?}"
201 );
202 RegisterVcEmulationEpochError::Storage(e)
203 })?;
204 storage
205 .write_registered_vc_emulation_epoch(self.group_id(), ®istered)
206 .map_err(|e| {
207 log::error!(
208 "vc: record registered emulation epoch in register_vc_emulation_epoch \
209 failed: {e:?}"
210 );
211 RegisterVcEmulationEpochError::Storage(e)
212 })?;
213
214 Ok(registered.epoch_id)
215 }
216
217 pub fn epoch_authenticator(&self) -> &EpochAuthenticator {
219 self.group_epoch_secrets().epoch_authenticator()
220 }
221
222 pub fn resumption_psk_secret(&self) -> &ResumptionPskSecret {
224 self.group_epoch_secrets().resumption_psk()
225 }
226
227 pub fn get_past_resumption_psk(&self, epoch: GroupEpoch) -> Option<&ResumptionPskSecret> {
230 self.resumption_psk_store.get(epoch)
231 }
232
233 pub fn export_group_info<CryptoProvider: OpenMlsCrypto>(
235 &self,
236 crypto: &CryptoProvider,
237 signer: &impl Signer,
238 with_ratchet_tree: bool,
239 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
240 self.export_group_info_with_additional_extensions(crypto, signer, with_ratchet_tree, None)
241 }
242
243 pub fn export_group_info_with_additional_extensions<CryptoProvider: OpenMlsCrypto>(
248 &self,
249 crypto: &CryptoProvider,
250 signer: &impl Signer,
251 with_ratchet_tree: bool,
252 additional_extensions: impl IntoIterator<Item = Extension>,
253 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
254 let extensions = {
255 let ratchet_tree_extension = || {
256 Extension::RatchetTree(RatchetTreeExtension::new(
257 self.public_group().export_ratchet_tree(),
258 ))
259 };
260
261 let external_pub_extension = || -> Result<Extension, ExportGroupInfoError> {
262 let external_pub = self
263 .group_epoch_secrets()
264 .external_secret()
265 .derive_external_keypair(crypto, self.ciphersuite())
266 .map_err(LibraryError::unexpected_crypto_error)?
267 .public;
268 Ok(Extension::ExternalPub(ExternalPubExtension::new(
269 HpkePublicKey::from(external_pub),
270 )))
271 };
272
273 let mut extensions = if with_ratchet_tree {
274 vec![ratchet_tree_extension(), external_pub_extension()?]
275 } else {
276 vec![external_pub_extension()?]
277 };
278
279 extensions.extend(
280 additional_extensions
281 .into_iter()
282 .map(|extension| {
283 if extension.as_ratchet_tree_extension().is_ok()
284 || extension.as_external_pub_extension().is_ok()
285 {
286 Err(InvalidExtensionError::CannotAddDirectlyToGroupInfo)
287 } else {
288 Ok(extension)
289 }
290 })
291 .collect::<Result<Vec<_>, _>>()?,
292 );
293
294 Extensions::from_vec(extensions)?
295 };
296
297 let group_info_tbs = GroupInfoTBS::new(
299 self.context().clone(),
300 extensions,
301 self.message_secrets()
302 .confirmation_key()
303 .tag(
304 crypto,
305 self.ciphersuite(),
306 self.context().confirmed_transcript_hash(),
307 )
308 .map_err(LibraryError::unexpected_crypto_error)?,
309 self.own_leaf_index(),
310 )?;
311
312 let group_info = group_info_tbs
314 .sign(signer)
315 .map_err(|_| LibraryError::custom("Signing failed"))?;
316 Ok(group_info.into())
317 }
318}