openmls/group/mls_group/
exporting.rs

1use errors::{ExportGroupInfoError, ExportSecretError};
2use openmls_traits::{crypto::OpenMlsCrypto, signatures::Signer};
3
4use crate::{
5    ciphersuite::HpkePublicKey,
6    schedule::{EpochAuthenticator, ResumptionPskSecret},
7};
8
9use super::*;
10
11impl MlsGroup {
12    // === Export secrets ===
13
14    /// Exports a secret from the current epoch.
15    /// Returns [`ExportSecretError::KeyLengthTooLong`] if the requested
16    /// key length is too long.
17    /// Returns [`ExportSecretError::GroupStateError(MlsGroupStateError::UseAfterEviction)`](MlsGroupStateError::UseAfterEviction)
18    /// if the group is not active.
19    pub fn export_secret<CryptoProvider: OpenMlsCrypto>(
20        &self,
21        crypto: &CryptoProvider,
22        label: &str,
23        context: &[u8],
24        key_length: usize,
25    ) -> Result<Vec<u8>, ExportSecretError> {
26        if key_length > u16::MAX as usize {
27            log::error!("Got a key that is larger than u16::MAX");
28            return Err(ExportSecretError::KeyLengthTooLong);
29        }
30
31        if self.is_active() {
32            Ok(self
33                .group_epoch_secrets
34                .exporter_secret()
35                .derive_exported_secret(self.ciphersuite(), crypto, label, context, key_length)
36                .map_err(LibraryError::unexpected_crypto_error)?)
37        } else {
38            Err(ExportSecretError::GroupStateError(
39                MlsGroupStateError::UseAfterEviction,
40            ))
41        }
42    }
43
44    /// Returns the epoch authenticator of the current epoch.
45    pub fn epoch_authenticator(&self) -> &EpochAuthenticator {
46        self.group_epoch_secrets().epoch_authenticator()
47    }
48
49    /// Returns the resumption PSK secret of the current epoch.
50    pub fn resumption_psk_secret(&self) -> &ResumptionPskSecret {
51        self.group_epoch_secrets().resumption_psk()
52    }
53
54    /// Returns a resumption psk for a given epoch. If no resumption psk
55    /// is available for that epoch,  `None` is returned.
56    pub fn get_past_resumption_psk(&self, epoch: GroupEpoch) -> Option<&ResumptionPskSecret> {
57        self.resumption_psk_store.get(epoch)
58    }
59
60    /// Export a group info object for this group.
61    pub fn export_group_info<CryptoProvider: OpenMlsCrypto>(
62        &self,
63        crypto: &CryptoProvider,
64        signer: &impl Signer,
65        with_ratchet_tree: bool,
66    ) -> Result<MlsMessageOut, ExportGroupInfoError> {
67        let extensions = {
68            let ratchet_tree_extension = || {
69                Extension::RatchetTree(RatchetTreeExtension::new(
70                    self.public_group().export_ratchet_tree(),
71                ))
72            };
73
74            let external_pub_extension = || -> Result<Extension, ExportGroupInfoError> {
75                let external_pub = self
76                    .group_epoch_secrets()
77                    .external_secret()
78                    .derive_external_keypair(crypto, self.ciphersuite())
79                    .map_err(LibraryError::unexpected_crypto_error)?
80                    .public;
81                Ok(Extension::ExternalPub(ExternalPubExtension::new(
82                    HpkePublicKey::from(external_pub),
83                )))
84            };
85
86            if with_ratchet_tree {
87                Extensions::from_vec(vec![ratchet_tree_extension(), external_pub_extension()?])
88                    .map_err(|_| {
89                        LibraryError::custom(
90                            "There should not have been duplicate extensions here.",
91                        )
92                    })?
93            } else {
94                Extensions::single(external_pub_extension()?)
95            }
96        };
97
98        // Create to-be-signed group info.
99        let group_info_tbs = GroupInfoTBS::new(
100            self.context().clone(),
101            extensions,
102            self.message_secrets()
103                .confirmation_key()
104                .tag(
105                    crypto,
106                    self.ciphersuite(),
107                    self.context().confirmed_transcript_hash(),
108                )
109                .map_err(LibraryError::unexpected_crypto_error)?,
110            self.own_leaf_index(),
111        );
112
113        // Sign to-be-signed group info.
114        let group_info = group_info_tbs
115            .sign(signer)
116            .map_err(|_| LibraryError::custom("Signing failed"))?;
117        Ok(group_info.into())
118    }
119}