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