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 schedule::{EpochAuthenticator, ResumptionPskSecret},
9};
10
11use super::*;
12
13impl MlsGroup {
14 pub fn export_secret<CryptoProvider: OpenMlsCrypto>(
22 &self,
23 crypto: &CryptoProvider,
24 label: &str,
25 context: &[u8],
26 key_length: usize,
27 ) -> Result<Vec<u8>, ExportSecretError> {
28 if key_length > u16::MAX as usize {
29 log::error!("Got a key that is larger than u16::MAX");
30 return Err(ExportSecretError::KeyLengthTooLong);
31 }
32
33 if self.is_active() {
34 Ok(self
35 .group_epoch_secrets
36 .exporter_secret()
37 .derive_exported_secret(self.ciphersuite(), crypto, label, context, key_length)
38 .map_err(LibraryError::unexpected_crypto_error)?)
39 } else {
40 Err(ExportSecretError::GroupStateError(
41 MlsGroupStateError::UseAfterEviction,
42 ))
43 }
44 }
45
46 #[cfg(feature = "extensions-draft-08")]
49 pub fn safe_export_secret<Crypto: OpenMlsCrypto, Storage: StorageProvider>(
50 &mut self,
51 crypto: &Crypto,
52 storage: &Storage,
53 component_id: u16,
54 ) -> Result<Vec<u8>, SafeExportSecretError<Storage::Error>> {
55 if !self.is_active() {
56 return Err(SafeExportSecretError::GroupState(
57 MlsGroupStateError::UseAfterEviction,
58 ));
59 }
60 let group_id = self.public_group.group_id();
61 let ciphersuite = self.ciphersuite();
62 let Some(application_export_tree) = self.application_export_tree.as_mut() else {
63 return Err(SafeExportSecretError::Unsupported);
64 };
65 let component_secret =
66 application_export_tree.safe_export_secret(crypto, ciphersuite, component_id)?;
67 storage
68 .write_application_export_tree(group_id, application_export_tree)
69 .map_err(SafeExportSecretError::Storage)?;
70
71 Ok(component_secret.as_slice().to_vec())
72 }
73
74 #[cfg(feature = "extensions-draft-08")]
77 pub fn safe_export_secret_from_pending<Provider: StorageProvider>(
78 &mut self,
79 crypto: &impl OpenMlsCrypto,
80 storage: &Provider,
81 component_id: u16,
82 ) -> Result<Vec<u8>, PendingSafeExportSecretError<Provider::Error>> {
83 let group_id = self.group_id().clone();
84 let MlsGroupState::PendingCommit(ref mut group_state) = self.group_state else {
85 return Err(PendingSafeExportSecretError::NoPendingCommit);
86 };
87 let PendingCommitState::Member(ref mut staged_commit) = **group_state else {
88 return Err(PendingSafeExportSecretError::NotGroupMember);
89 };
90 let secret = staged_commit.safe_export_secret(crypto, component_id)?;
91 storage
92 .write_group_state(&group_id, &self.group_state)
93 .map_err(PendingSafeExportSecretError::Storage)?;
94 Ok(secret.as_slice().to_vec())
95 }
96
97 pub fn epoch_authenticator(&self) -> &EpochAuthenticator {
99 self.group_epoch_secrets().epoch_authenticator()
100 }
101
102 pub fn resumption_psk_secret(&self) -> &ResumptionPskSecret {
104 self.group_epoch_secrets().resumption_psk()
105 }
106
107 pub fn get_past_resumption_psk(&self, epoch: GroupEpoch) -> Option<&ResumptionPskSecret> {
110 self.resumption_psk_store.get(epoch)
111 }
112
113 pub fn export_group_info<CryptoProvider: OpenMlsCrypto>(
115 &self,
116 crypto: &CryptoProvider,
117 signer: &impl Signer,
118 with_ratchet_tree: bool,
119 ) -> Result<MlsMessageOut, ExportGroupInfoError> {
120 let extensions = {
121 let ratchet_tree_extension = || {
122 Extension::RatchetTree(RatchetTreeExtension::new(
123 self.public_group().export_ratchet_tree(),
124 ))
125 };
126
127 let external_pub_extension = || -> Result<Extension, ExportGroupInfoError> {
128 let external_pub = self
129 .group_epoch_secrets()
130 .external_secret()
131 .derive_external_keypair(crypto, self.ciphersuite())
132 .map_err(LibraryError::unexpected_crypto_error)?
133 .public;
134 Ok(Extension::ExternalPub(ExternalPubExtension::new(
135 HpkePublicKey::from(external_pub),
136 )))
137 };
138
139 if with_ratchet_tree {
140 Extensions::from_vec(vec![ratchet_tree_extension(), external_pub_extension()?])
141 .map_err(|_| {
142 LibraryError::custom(
143 "There should not have been duplicate extensions here.",
144 )
145 })?
146 } else {
147 Extensions::single(external_pub_extension()?)
148 }
149 };
150
151 let group_info_tbs = GroupInfoTBS::new(
153 self.context().clone(),
154 extensions,
155 self.message_secrets()
156 .confirmation_key()
157 .tag(
158 crypto,
159 self.ciphersuite(),
160 self.context().confirmed_transcript_hash(),
161 )
162 .map_err(LibraryError::unexpected_crypto_error)?,
163 self.own_leaf_index(),
164 );
165
166 let group_info = group_info_tbs
168 .sign(signer)
169 .map_err(|_| LibraryError::custom("Signing failed"))?;
170 Ok(group_info.into())
171 }
172}