openmls/group/mls_group/
exporting.rs

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