openmls/group/mls_group/
exporting.rs1use errors::{ExportGroupInfoError, ExportSecretError};
2use openmls_traits::{crypto::OpenMlsCrypto, signatures::Signer};
3
4#[cfg(feature = "extensions-draft-08")]
5use crate::group::{PendingSafeExportSecretError, SafeExportSecretError};
6use crate::{
7 ciphersuite::HpkePublicKey,
8 extensions::errors::InvalidExtensionError,
9 schedule::{EpochAuthenticator, ResumptionPskSecret},
10};
11
12use super::*;
13
14impl MlsGroup {
15 pub fn export_secret<CryptoProvider: OpenMlsCrypto>(
23 &self,
24 crypto: &CryptoProvider,
25 label: &str,
26 context: &[u8],
27 key_length: usize,
28 ) -> Result<Vec<u8>, ExportSecretError> {
29 if key_length > u16::MAX as usize {
30 log::error!("Got a key that is larger than u16::MAX");
31 return Err(ExportSecretError::KeyLengthTooLong);
32 }
33
34 if self.is_active() {
35 Ok(self
36 .group_epoch_secrets
37 .exporter_secret()
38 .derive_exported_secret(self.ciphersuite(), crypto, label, context, key_length)
39 .map_err(LibraryError::unexpected_crypto_error)?)
40 } else {
41 Err(ExportSecretError::GroupStateError(
42 MlsGroupStateError::UseAfterEviction,
43 ))
44 }
45 }
46
47 #[cfg(feature = "extensions-draft-08")]
50 pub fn safe_export_secret<Crypto: OpenMlsCrypto, Storage: StorageProvider>(
51 &mut self,
52 crypto: &Crypto,
53 storage: &Storage,
54 component_id: u16,
55 ) -> Result<Vec<u8>, SafeExportSecretError<Storage::Error>> {
56 if !self.is_active() {
57 return Err(SafeExportSecretError::GroupState(
58 MlsGroupStateError::UseAfterEviction,
59 ));
60 }
61 let group_id = self.public_group.group_id();
62 let ciphersuite = self.ciphersuite();
63 let Some(application_export_tree) = self.application_export_tree.as_mut() else {
64 return Err(SafeExportSecretError::Unsupported);
65 };
66 let component_secret =
67 application_export_tree.safe_export_secret(crypto, ciphersuite, component_id)?;
68 storage
69 .write_application_export_tree(group_id, application_export_tree)
70 .map_err(SafeExportSecretError::Storage)?;
71
72 Ok(component_secret.as_slice().to_vec())
73 }
74
75 #[cfg(feature = "extensions-draft-08")]
78 pub fn safe_export_secret_from_pending<Provider: StorageProvider>(
79 &mut self,
80 crypto: &impl OpenMlsCrypto,
81 storage: &Provider,
82 component_id: u16,
83 ) -> Result<Vec<u8>, PendingSafeExportSecretError<Provider::Error>> {
84 let group_id = self.group_id().clone();
85 let MlsGroupState::PendingCommit(ref mut group_state) = self.group_state else {
86 return Err(PendingSafeExportSecretError::NoPendingCommit);
87 };
88 let PendingCommitState::Member(ref mut staged_commit) = **group_state else {
89 return Err(PendingSafeExportSecretError::NotGroupMember);
90 };
91 let secret = staged_commit.safe_export_secret(crypto, component_id)?;
92 storage
93 .write_group_state(&group_id, &self.group_state)
94 .map_err(PendingSafeExportSecretError::Storage)?;
95 Ok(secret.as_slice().to_vec())
96 }
97
98 pub fn epoch_authenticator(&self) -> &EpochAuthenticator {
100 self.group_epoch_secrets().epoch_authenticator()
101 }
102
103 pub fn resumption_psk_secret(&self) -> &ResumptionPskSecret {
105 self.group_epoch_secrets().resumption_psk()
106 }
107
108 pub fn get_past_resumption_psk(&self, epoch: GroupEpoch) -> Option<&ResumptionPskSecret> {
111 self.resumption_psk_store.get(epoch)
112 }
113
114 pub fn export_group_info<CryptoProvider: OpenMlsCrypto>(
116 &self,
117 crypto: &CryptoProvider,
118 signer: &impl Signer,
119 with_ratchet_tree: bool,
120 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
121 self.export_group_info_with_additional_extensions(crypto, signer, with_ratchet_tree, None)
122 }
123
124 pub fn export_group_info_with_additional_extensions<CryptoProvider: OpenMlsCrypto>(
129 &self,
130 crypto: &CryptoProvider,
131 signer: &impl Signer,
132 with_ratchet_tree: bool,
133 additional_extensions: impl IntoIterator<Item = Extension>,
134 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
135 let extensions = {
136 let ratchet_tree_extension = || {
137 Extension::RatchetTree(RatchetTreeExtension::new(
138 self.public_group().export_ratchet_tree(),
139 ))
140 };
141
142 let external_pub_extension = || -> Result<Extension, ExportGroupInfoError> {
143 let external_pub = self
144 .group_epoch_secrets()
145 .external_secret()
146 .derive_external_keypair(crypto, self.ciphersuite())
147 .map_err(LibraryError::unexpected_crypto_error)?
148 .public;
149 Ok(Extension::ExternalPub(ExternalPubExtension::new(
150 HpkePublicKey::from(external_pub),
151 )))
152 };
153
154 let mut extensions = if with_ratchet_tree {
155 vec![ratchet_tree_extension(), external_pub_extension()?]
156 } else {
157 vec![external_pub_extension()?]
158 };
159
160 extensions.extend(
161 additional_extensions
162 .into_iter()
163 .map(|extension| {
164 if extension.as_ratchet_tree_extension().is_ok()
165 || extension.as_external_pub_extension().is_ok()
166 {
167 Err(InvalidExtensionError::CannotAddDirectlyToGroupInfo)
168 } else {
169 Ok(extension)
170 }
171 })
172 .collect::<Result<Vec<_>, _>>()?,
173 );
174
175 Extensions::from_vec(extensions)?
176 };
177
178 let group_info_tbs = GroupInfoTBS::new(
180 self.context().clone(),
181 extensions,
182 self.message_secrets()
183 .confirmation_key()
184 .tag(
185 crypto,
186 self.ciphersuite(),
187 self.context().confirmed_transcript_hash(),
188 )
189 .map_err(LibraryError::unexpected_crypto_error)?,
190 self.own_leaf_index(),
191 )?;
192
193 let group_info = group_info_tbs
195 .sign(signer)
196 .map_err(|_| LibraryError::custom("Signing failed"))?;
197 Ok(group_info.into())
198 }
199}