1use std::{
2 fmt::{Debug, Formatter},
3 marker::PhantomData,
4};
5
6use errors::{ExportGroupInfoError, ExportSecretError};
7use openmls_traits::{crypto::OpenMlsCrypto, signatures::Signer};
8use zeroize::ZeroizeOnDrop;
9
10use crate::{
11 ciphersuite::{HpkePublicKey, Secret},
12 extensions::errors::InvalidExtensionError,
13 group::reinit::ReInitInfo,
14 schedule::{EpochAuthenticator, ResumptionPskSecret},
15};
16#[cfg(feature = "extensions-draft")]
17use crate::{
18 component::ComponentId,
19 group::{PendingSafeExportSecretError, SafeExportSecretError},
20};
21
22use super::*;
23
24pub struct ExportedSecret<T> {
29 secret: Secret,
30 _marker: PhantomData<T>,
31}
32
33pub struct GroupExport;
35
36pub struct StagedCommitExport;
38
39pub struct StagedWelcomeExport;
41
42pub struct ProcessedWelcomeExport;
44
45#[cfg(feature = "extensions-draft")]
47pub struct GroupSafeExport;
48
49#[cfg(feature = "extensions-draft")]
52pub struct PendingSafeExport;
53
54#[cfg(feature = "extensions-draft")]
59pub struct StagedCommitSafeExport;
60
61impl<T> ExportedSecret<T> {
62 pub(crate) fn new(secret: Secret) -> Self {
63 Self {
64 secret,
65 _marker: PhantomData,
66 }
67 }
68
69 pub fn as_slice(&self) -> &[u8] {
71 self.secret.as_slice()
72 }
73}
74
75#[cfg(feature = "extensions-draft")]
76impl ExportedSecret<StagedCommitSafeExport> {
77 pub(crate) fn into_pending_safe_export(self) -> ExportedSecret<PendingSafeExport> {
80 ExportedSecret::new(self.secret)
81 }
82}
83
84impl<T> AsRef<[u8]> for ExportedSecret<T> {
85 fn as_ref(&self) -> &[u8] {
86 self.secret.as_slice()
87 }
88}
89
90impl<T> ZeroizeOnDrop for ExportedSecret<T> {}
92
93impl<T> Debug for ExportedSecret<T> {
94 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
95 f.debug_struct("ExportedSecret")
96 .field("secret", &self.secret)
97 .finish()
98 }
99}
100
101impl<T, U> PartialEq<ExportedSecret<U>> for ExportedSecret<T> {
102 fn eq(&self, other: &ExportedSecret<U>) -> bool {
104 self.secret == other.secret
105 }
106}
107
108impl MlsGroup {
109 pub fn export_secret<CryptoProvider: OpenMlsCrypto>(
117 &self,
118 crypto: &CryptoProvider,
119 label: &str,
120 context: &[u8],
121 key_length: usize,
122 ) -> Result<ExportedSecret<GroupExport>, ExportSecretError> {
123 if key_length > u16::MAX as usize {
124 log::error!("Got a key that is larger than u16::MAX");
125 return Err(ExportSecretError::KeyLengthTooLong);
126 }
127
128 if self.is_active() {
129 Ok(ExportedSecret::new(
130 self.group_epoch_secrets
131 .exporter_secret()
132 .derive_exported_secret(self.ciphersuite(), crypto, label, context, key_length)
133 .map_err(LibraryError::unexpected_crypto_error)?,
134 ))
135 } else {
136 Err(ExportSecretError::GroupStateError(
137 MlsGroupStateError::UseAfterEviction,
138 ))
139 }
140 }
141
142 #[cfg(feature = "extensions-draft")]
145 #[openmls_traits::maybe_async]
146 pub async fn safe_export_secret<Crypto: OpenMlsCrypto, Storage: StorageProvider>(
147 &mut self,
148 crypto: &Crypto,
149 storage: &Storage,
150 component_id: ComponentId,
151 ) -> Result<ExportedSecret<GroupSafeExport>, SafeExportSecretError<Storage::Error>> {
152 if !self.is_active() {
153 return Err(SafeExportSecretError::GroupState(
154 MlsGroupStateError::UseAfterEviction,
155 ));
156 }
157 let group_id = self.public_group.group_id();
158 let ciphersuite = self.ciphersuite();
159 let Some(application_export_tree) = self.application_export_tree.as_mut() else {
160 return Err(SafeExportSecretError::Unsupported);
161 };
162 let component_secret =
163 application_export_tree.safe_export_secret(crypto, ciphersuite, component_id)?;
164 storage
165 .write_application_export_tree(group_id, application_export_tree)
166 .await
167 .map_err(SafeExportSecretError::Storage)?;
168
169 Ok(ExportedSecret::new(component_secret))
170 }
171
172 #[cfg(feature = "extensions-draft")]
175 #[openmls_traits::maybe_async]
176 pub async fn safe_export_secret_from_pending<Provider: StorageProvider>(
177 &mut self,
178 crypto: &impl OpenMlsCrypto,
179 storage: &Provider,
180 component_id: ComponentId,
181 ) -> Result<ExportedSecret<PendingSafeExport>, PendingSafeExportSecretError<Provider::Error>>
182 {
183 let group_id = self.group_id().clone();
184 let MlsGroupState::PendingCommit(ref mut group_state) = self.group_state else {
185 return Err(PendingSafeExportSecretError::NoPendingCommit);
186 };
187 let PendingCommitState::Member(ref mut staged_commit) = **group_state else {
188 return Err(PendingSafeExportSecretError::NotGroupMember);
189 };
190 let secret = staged_commit.safe_export_secret(crypto, component_id)?;
191 storage
192 .write_group_state(&group_id, &self.group_state)
193 .await
194 .map_err(PendingSafeExportSecretError::Storage)?;
195 Ok(secret.into_pending_safe_export())
196 }
197
198 pub fn epoch_authenticator(&self) -> &EpochAuthenticator {
200 self.group_epoch_secrets().epoch_authenticator()
201 }
202
203 pub fn resumption_psk_secret(&self) -> &ResumptionPskSecret {
205 self.group_epoch_secrets().resumption_psk()
206 }
207
208 pub fn branch_info(&self) -> BranchInfo {
221 BranchInfo {
222 version: self.version(),
223 ciphersuite: self.ciphersuite(),
224 group_id: self.group_id().clone(),
225 epoch: self.epoch(),
226 resumption_psk_secret: self.resumption_psk_secret().clone(),
227 member_credentials: self.members().map(|m| m.credential).collect(),
228 }
229 }
230
231 pub fn reinit_info(&self, proposal: ReInitProposal) -> Option<ReInitInfo> {
249 if !matches!(self.group_state, MlsGroupState::Inactive) {
254 return None;
255 }
256
257 Some(ReInitInfo {
258 proposal,
259 old_group_id: self.group_id().clone(),
260 old_group_epoch: self.epoch(),
261 resumption_psk_secret: self.resumption_psk_secret().clone(),
262 member_credentials: self.members().map(|member| member.credential).collect(),
263 })
264 }
265
266 pub fn get_past_resumption_psk(&self, epoch: GroupEpoch) -> Option<&ResumptionPskSecret> {
269 self.resumption_psk_store.get(epoch)
270 }
271
272 pub fn export_group_info<CryptoProvider: OpenMlsCrypto>(
274 &self,
275 crypto: &CryptoProvider,
276 signer: &impl Signer,
277 with_ratchet_tree: bool,
278 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
279 self.export_group_info_with_additional_extensions(crypto, signer, with_ratchet_tree, None)
280 }
281
282 pub fn export_group_info_with_additional_extensions<CryptoProvider: OpenMlsCrypto>(
287 &self,
288 crypto: &CryptoProvider,
289 signer: &impl Signer,
290 with_ratchet_tree: bool,
291 additional_extensions: impl IntoIterator<Item = Extension>,
292 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
293 let extensions = {
294 let ratchet_tree_extension = || {
295 Extension::RatchetTree(RatchetTreeExtension::new(
296 self.public_group().export_ratchet_tree(),
297 ))
298 };
299
300 let external_pub_extension = || -> Result<Extension, ExportGroupInfoError> {
301 let external_pub = self
302 .group_epoch_secrets()
303 .external_secret()
304 .derive_external_keypair(crypto, self.ciphersuite())
305 .map_err(LibraryError::unexpected_crypto_error)?
306 .public;
307 Ok(Extension::ExternalPub(ExternalPubExtension::new(
308 HpkePublicKey::from(external_pub),
309 )))
310 };
311
312 let mut extensions = if with_ratchet_tree {
313 vec![ratchet_tree_extension(), external_pub_extension()?]
314 } else {
315 vec![external_pub_extension()?]
316 };
317
318 extensions.extend(
319 additional_extensions
320 .into_iter()
321 .map(|extension| {
322 if extension.as_ratchet_tree_extension().is_ok()
323 || extension.as_external_pub_extension().is_ok()
324 {
325 Err(InvalidExtensionError::CannotAddDirectlyToGroupInfo)
326 } else {
327 Ok(extension)
328 }
329 })
330 .collect::<Result<Vec<_>, _>>()?,
331 );
332
333 Extensions::from_vec(extensions)?
334 };
335
336 let group_info_tbs = GroupInfoTBS::new(
338 self.context().clone(),
339 extensions,
340 self.message_secrets()
341 .confirmation_key()
342 .tag(
343 crypto,
344 self.ciphersuite(),
345 self.context().confirmed_transcript_hash(),
346 )
347 .map_err(LibraryError::unexpected_crypto_error)?,
348 self.own_leaf_index(),
349 )?;
350
351 let group_info = group_info_tbs
353 .sign(signer)
354 .map_err(|_| LibraryError::custom("Signing failed"))?;
355 Ok(group_info.into())
356 }
357}