use openmls_traits::{crypto::OpenMlsCrypto, types::*};
use serde::{Deserialize, Serialize};
use tls_codec::{TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize};
use crate::{
binary_tree::array_representation::{LeafNodeIndex, TreeSize},
ciphersuite::{AeadKey, AeadNonce, HpkePrivateKey, Mac, Secret},
error::LibraryError,
framing::{mls_content::AuthenticatedContentTbm, MembershipTag},
group::GroupContext,
messages::{ConfirmationTag, PathSecret},
tree::secret_tree::SecretTree,
versions::ProtocolVersion,
};
pub mod errors;
pub mod psk;
pub(crate) mod message_secrets;
use errors::*;
use message_secrets::MessageSecrets;
use openmls_traits::random::OpenMlsRand;
use psk::PskSecret;
#[cfg(any(feature = "test-utils", test))]
pub mod tests_and_kats;
pub use psk::{ExternalPsk, PreSharedKeyId, Psk};
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(Eq, PartialEq))]
pub struct ResumptionPskSecret {
secret: Secret,
}
impl ResumptionPskSecret {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: &EpochSecret,
) -> Result<Self, CryptoError> {
let secret = epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "resumption")?;
Ok(Self { secret })
}
pub fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(Eq, PartialEq, Clone))]
pub struct EpochAuthenticator {
secret: Secret,
}
impl EpochAuthenticator {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: &EpochSecret,
) -> Result<Self, CryptoError> {
let secret = epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "authentication")?;
Ok(Self { secret })
}
pub fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
#[cfg_attr(any(feature = "test-utils", test), derive(Clone))]
pub(crate) struct CommitSecret {
secret: Secret,
}
impl From<PathSecret> for CommitSecret {
fn from(path_secret: PathSecret) -> Self {
CommitSecret {
secret: path_secret.secret(),
}
}
}
impl CommitSecret {
pub(crate) fn zero_secret(ciphersuite: Ciphersuite) -> Self {
CommitSecret {
secret: Secret::zero(ciphersuite),
}
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsRand) -> Self {
Self {
secret: Secret::random(ciphersuite, rng).expect("Not enough randomness."),
}
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))]
pub(crate) struct InitSecret {
secret: Secret,
}
impl From<Secret> for InitSecret {
fn from(secret: Secret) -> Self {
Self { secret }
}
}
fn hpke_info_from_version(version: ProtocolVersion) -> &'static str {
match version {
ProtocolVersion::Mls10 => "MLS 1.0 external init secret",
_ => "<OpenMLS reserved; Don't use this.>",
}
}
impl InitSecret {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: EpochSecret,
) -> Result<Self, CryptoError> {
let secret = epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "init")?;
log_crypto!(trace, "Init secret: {:x?}", secret);
Ok(InitSecret { secret })
}
pub(crate) fn random(
ciphersuite: Ciphersuite,
rand: &impl OpenMlsRand,
) -> Result<Self, CryptoError> {
Ok(InitSecret {
secret: Secret::random(ciphersuite, rand)?,
})
}
pub(crate) fn from_group_context(
crypto: &impl OpenMlsCrypto,
group_context: &GroupContext,
external_pub: &[u8],
) -> Result<(Self, Vec<u8>), KeyScheduleError> {
let ciphersuite = group_context.ciphersuite();
let version = group_context.protocol_version();
let (kem_output, raw_init_secret) = crypto.hpke_setup_sender_and_export(
ciphersuite.hpke_config(),
external_pub,
&[],
hpke_info_from_version(version).as_bytes(),
ciphersuite.hash_length(),
)?;
Ok((
InitSecret {
secret: Secret::from_slice(&raw_init_secret),
},
kem_output,
))
}
pub(crate) fn from_kem_output(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
version: ProtocolVersion,
external_priv: &HpkePrivateKey,
kem_output: &[u8],
) -> Result<Self, LibraryError> {
let raw_init_secret = crypto
.hpke_setup_receiver_and_export(
ciphersuite.hpke_config(),
kem_output,
external_priv,
&[],
hpke_info_from_version(version).as_bytes(),
ciphersuite.hash_length(),
)
.map_err(LibraryError::unexpected_crypto_error)?;
Ok(InitSecret {
secret: Secret::from_slice(&raw_init_secret),
})
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn clone(&self) -> Self {
Self {
secret: self.secret.clone(),
}
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
}
#[derive(Debug, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize)]
pub(crate) struct JoinerSecret {
secret: Secret,
}
impl JoinerSecret {
pub(crate) fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
commit_secret_option: impl Into<Option<CommitSecret>>,
init_secret: &InitSecret,
serialized_group_context: &[u8],
) -> Result<Self, CryptoError> {
let intermediate_secret = init_secret.secret.hkdf_extract(
crypto,
ciphersuite,
commit_secret_option.into().as_ref().map(|cs| &cs.secret),
)?;
let secret = intermediate_secret.kdf_expand_label(
crypto,
ciphersuite,
"joiner",
serialized_group_context,
ciphersuite.hash_length(),
)?;
log_crypto!(trace, "Joiner secret: {:x?}", secret);
Ok(JoinerSecret { secret })
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
#[cfg(test)]
pub(crate) fn random(ciphersuite: Ciphersuite, rand: &impl OpenMlsRand) -> Self {
Self {
secret: Secret::random(ciphersuite, rand).expect("Not enough randomness."),
}
}
}
#[derive(Debug, PartialEq)]
enum State {
Initial,
Context,
Done,
}
pub(crate) struct KeySchedule {
ciphersuite: Ciphersuite,
intermediate_secret: Option<IntermediateSecret>,
epoch_secret: Option<EpochSecret>,
state: State,
}
impl KeySchedule {
pub(crate) fn init(
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
joiner_secret: &JoinerSecret,
psk: PskSecret,
) -> Result<Self, LibraryError> {
log::debug!("Initializing the key schedule with {:?} ...", ciphersuite);
log_crypto!(
trace,
" joiner_secret: {:x?}",
joiner_secret.secret.as_slice()
);
let intermediate_secret = IntermediateSecret::new(crypto, ciphersuite, joiner_secret, psk)
.map_err(LibraryError::unexpected_crypto_error)?;
Ok(Self {
ciphersuite,
intermediate_secret: Some(intermediate_secret),
epoch_secret: None,
state: State::Initial,
})
}
pub(crate) fn welcome(
&self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
) -> Result<WelcomeSecret, KeyScheduleError> {
if self.state != State::Initial || self.intermediate_secret.is_none() {
log::error!("Trying to derive a welcome secret while not in the initial state.");
return Err(KeyScheduleError::InvalidState(ErrorState::Init));
}
let intermediate_secret = self
.intermediate_secret
.as_ref()
.ok_or_else(|| LibraryError::custom("state machine error"))?;
Ok(WelcomeSecret::new(
crypto,
ciphersuite,
intermediate_secret,
)?)
}
pub(crate) fn add_context(
&mut self,
crypto: &impl OpenMlsCrypto,
serialized_group_context: &[u8],
) -> Result<(), KeyScheduleError> {
log::trace!(
"Adding context to key schedule. {:?}",
serialized_group_context
);
if self.state != State::Initial || self.intermediate_secret.is_none() {
log::error!(
"Trying to add context to the key schedule while not in the initial state."
);
return Err(KeyScheduleError::InvalidState(ErrorState::Init));
}
self.state = State::Context;
let intermediate_secret = self
.intermediate_secret
.take()
.ok_or_else(|| LibraryError::custom("state machine error"))?;
log_crypto!(
trace,
" intermediate_secret: {:x?}",
intermediate_secret.secret.as_slice()
);
self.epoch_secret = Some(EpochSecret::new(
self.ciphersuite,
crypto,
intermediate_secret,
serialized_group_context,
)?);
self.intermediate_secret = None;
Ok(())
}
pub(crate) fn epoch_secrets(
&mut self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
) -> Result<EpochSecrets, KeyScheduleError> {
if self.state != State::Context || self.epoch_secret.is_none() {
log::error!("Trying to derive the epoch secrets while not in the right state.");
return Err(KeyScheduleError::InvalidState(ErrorState::Context));
}
self.state = State::Done;
let epoch_secret = match self.epoch_secret.take() {
Some(epoch_secret) => epoch_secret,
None => return Err(LibraryError::custom("state machine error").into()),
};
Ok(EpochSecrets::new(crypto, ciphersuite, epoch_secret)?)
}
}
struct IntermediateSecret {
secret: Secret,
}
impl IntermediateSecret {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
joiner_secret: &JoinerSecret,
psk: PskSecret,
) -> Result<Self, CryptoError> {
log_crypto!(trace, "PSK input: {:x?}", psk.as_slice());
let secret = joiner_secret
.secret
.hkdf_extract(crypto, ciphersuite, psk.secret())?;
log_crypto!(trace, "Intermediate secret: {:x?}", secret);
Ok(Self { secret })
}
}
pub(crate) struct WelcomeSecret {
secret: Secret,
}
impl WelcomeSecret {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
intermediate_secret: &IntermediateSecret,
) -> Result<Self, CryptoError> {
let secret = intermediate_secret
.secret
.derive_secret(crypto, ciphersuite, "welcome")?;
log_crypto!(trace, "Welcome secret: {:x?}", secret);
Ok(WelcomeSecret { secret })
}
pub(crate) fn derive_welcome_key_nonce(
self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
) -> Result<(AeadKey, AeadNonce), CryptoError> {
let welcome_nonce = self.derive_aead_nonce(crypto, ciphersuite)?;
let welcome_key = self.derive_aead_key(crypto, ciphersuite)?;
Ok((welcome_key, welcome_nonce))
}
fn derive_aead_key(
&self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
) -> Result<AeadKey, CryptoError> {
log::trace!("WelcomeSecret.derive_aead_key with {}", ciphersuite);
let aead_secret = self.secret.kdf_expand_label(
crypto,
ciphersuite,
"key",
b"",
ciphersuite.aead_key_length(),
)?;
Ok(AeadKey::from_secret(aead_secret, ciphersuite))
}
fn derive_aead_nonce(
&self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
) -> Result<AeadNonce, CryptoError> {
let nonce_secret = self.secret.kdf_expand_label(
crypto,
ciphersuite,
"nonce",
b"",
ciphersuite.aead_nonce_length(),
)?;
Ok(AeadNonce::from_secret(nonce_secret))
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
}
struct EpochSecret {
secret: Secret,
}
impl EpochSecret {
fn new(
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
intermediate_secret: IntermediateSecret,
serialized_group_context: &[u8],
) -> Result<Self, CryptoError> {
let secret = intermediate_secret.secret.kdf_expand_label(
crypto,
ciphersuite,
"epoch",
serialized_group_context,
ciphersuite.hash_length(),
)?;
log_crypto!(trace, "Epoch secret: {:x?}", secret);
Ok(EpochSecret { secret })
}
}
#[cfg_attr(test, derive(Clone))]
pub(crate) struct EncryptionSecret {
secret: Secret,
}
impl EncryptionSecret {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: &EpochSecret,
) -> Result<Self, CryptoError> {
Ok(EncryptionSecret {
secret: epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "encryption")?,
})
}
pub(crate) fn create_secret_tree(
self,
treesize: TreeSize,
own_index: LeafNodeIndex,
) -> SecretTree {
SecretTree::new(self, treesize, own_index)
}
pub(crate) fn consume_secret(self) -> Secret {
self.secret
}
#[cfg(test)]
pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsRand) -> Self {
EncryptionSecret {
secret: Secret::random(ciphersuite, rng).expect("Not enough randomness."),
}
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn from_slice(bytes: &[u8]) -> Self {
Self {
secret: Secret::from_slice(bytes),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))]
pub(crate) struct ExporterSecret {
secret: Secret,
}
impl ExporterSecret {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: &EpochSecret,
) -> Result<Self, CryptoError> {
let secret = epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "exporter")?;
Ok(ExporterSecret { secret })
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
pub(crate) fn derive_exported_secret(
&self,
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
label: &str,
context: &[u8],
key_length: usize,
) -> Result<Vec<u8>, CryptoError> {
let context_hash = &crypto.hash(ciphersuite.hash_algorithm(), context)?;
Ok(self
.secret
.derive_secret(crypto, ciphersuite, label)?
.kdf_expand_label(crypto, ciphersuite, "exported", context_hash, key_length)?
.as_slice()
.to_vec())
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))]
pub(crate) struct ExternalSecret {
secret: Secret,
}
impl ExternalSecret {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: &EpochSecret,
) -> Result<Self, CryptoError> {
let secret = epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "external")?;
Ok(Self { secret })
}
pub(crate) fn derive_external_keypair(
&self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
) -> Result<HpkeKeyPair, CryptoError> {
crypto.derive_hpke_keypair(ciphersuite.hpke_config(), self.secret.as_slice())
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))]
pub(crate) struct ConfirmationKey {
secret: Secret,
}
impl ConfirmationKey {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: &EpochSecret,
) -> Result<Self, CryptoError> {
log::debug!("Computing confirmation key.");
log_crypto!(
trace,
" epoch_secret {:x?}",
epoch_secret.secret.as_slice()
);
let secret = epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "confirm")?;
Ok(Self { secret })
}
pub(crate) fn tag(
&self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
confirmed_transcript_hash: &[u8],
) -> Result<ConfirmationTag, CryptoError> {
log::debug!("Computing confirmation tag.");
log_crypto!(trace, " confirmation key {:x?}", self.secret.as_slice());
log_crypto!(trace, " transcript hash {:x?}", confirmed_transcript_hash);
Ok(ConfirmationTag(Mac::new(
crypto,
ciphersuite,
&self.secret,
confirmed_transcript_hash,
)?))
}
}
#[cfg(test)]
impl ConfirmationKey {
pub(crate) fn from_secret(secret: Secret) -> Self {
Self { secret }
}
}
#[cfg(any(feature = "test-utils", test))]
impl ConfirmationKey {
pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsRand) -> Self {
Self {
secret: Secret::random(ciphersuite, rng).expect("Not enough randomness."),
}
}
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))]
pub(crate) struct MembershipKey {
secret: Secret,
}
impl MembershipKey {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: &EpochSecret,
) -> Result<Self, CryptoError> {
let secret = epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "membership")?;
Ok(Self { secret })
}
pub(crate) fn tag_message(
&self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
tbm_payload: AuthenticatedContentTbm,
) -> Result<MembershipTag, LibraryError> {
Ok(MembershipTag(
Mac::new(
crypto,
ciphersuite,
&self.secret,
&tbm_payload
.into_bytes()
.map_err(LibraryError::missing_bound_check)?,
)
.map_err(LibraryError::unexpected_crypto_error)?,
))
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn from_secret(secret: Secret) -> Self {
Self { secret }
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsRand) -> Self {
Self {
secret: Secret::random(ciphersuite, rng).expect("Not enough randomness."),
}
}
}
fn ciphertext_sample(ciphersuite: Ciphersuite, ciphertext: &[u8]) -> &[u8] {
let sample_length = ciphersuite.hash_length();
log::debug!("Getting ciphertext sample of length {:?}", sample_length);
if ciphertext.len() <= sample_length {
ciphertext
} else {
&ciphertext[0..sample_length]
}
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(
any(feature = "test-utils", feature = "crypto-debug", test),
derive(Debug, Clone, PartialEq)
)]
pub(crate) struct SenderDataSecret {
secret: Secret,
}
impl SenderDataSecret {
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: &EpochSecret,
) -> Result<Self, CryptoError> {
let secret = epoch_secret
.secret
.derive_secret(crypto, ciphersuite, "sender data")?;
Ok(SenderDataSecret { secret })
}
pub(crate) fn derive_aead_key(
&self,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
ciphertext: &[u8],
) -> Result<AeadKey, CryptoError> {
let ciphertext_sample = ciphertext_sample(ciphersuite, ciphertext);
log::debug!(
"SenderDataSecret::derive_aead_key ciphertext sample: {:x?}",
ciphertext_sample
);
let secret = self.secret.kdf_expand_label(
crypto,
ciphersuite,
"key",
ciphertext_sample,
ciphersuite.aead_key_length(),
)?;
Ok(AeadKey::from_secret(secret, ciphersuite))
}
pub(crate) fn derive_aead_nonce(
&self,
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
ciphertext: &[u8],
) -> Result<AeadNonce, CryptoError> {
let ciphertext_sample = ciphertext_sample(ciphersuite, ciphertext);
log::debug!(
"SenderDataSecret::derive_aead_nonce ciphertext sample: {:x?}",
ciphertext_sample
);
let nonce_secret = self.secret.kdf_expand_label(
crypto,
ciphersuite,
"nonce",
ciphertext_sample,
ciphersuite.aead_nonce_length(),
)?;
Ok(AeadNonce::from_secret(nonce_secret))
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn random(ciphersuite: Ciphersuite, rng: &impl OpenMlsRand) -> Self {
Self {
secret: Secret::random(ciphersuite, rng).expect("Not enough randomness."),
}
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn as_slice(&self) -> &[u8] {
self.secret.as_slice()
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn from_slice(bytes: &[u8]) -> Self {
Self {
secret: Secret::from_slice(bytes),
}
}
}
pub(crate) struct EpochSecrets {
init_secret: InitSecret,
sender_data_secret: SenderDataSecret,
encryption_secret: EncryptionSecret,
exporter_secret: ExporterSecret,
epoch_authenticator: EpochAuthenticator,
external_secret: ExternalSecret,
confirmation_key: ConfirmationKey,
membership_key: MembershipKey,
resumption_psk: ResumptionPskSecret,
}
impl std::fmt::Debug for EpochSecrets {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("EpochSecrets { *** }")
}
}
#[cfg(not(test))]
impl PartialEq for EpochSecrets {
fn eq(&self, _other: &Self) -> bool {
false
}
}
#[cfg(test)]
impl PartialEq for EpochSecrets {
fn eq(&self, other: &Self) -> bool {
self.sender_data_secret == other.sender_data_secret
&& self.exporter_secret == other.exporter_secret
&& self.epoch_authenticator == other.epoch_authenticator
&& self.external_secret == other.external_secret
&& self.confirmation_key == other.confirmation_key
&& self.membership_key == other.membership_key
&& self.resumption_psk == other.resumption_psk
}
}
impl EpochSecrets {
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn sender_data_secret(&self) -> &SenderDataSecret {
&self.sender_data_secret
}
pub(crate) fn confirmation_key(&self) -> &ConfirmationKey {
&self.confirmation_key
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn epoch_authenticator(&self) -> &EpochAuthenticator {
&self.epoch_authenticator
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn exporter_secret(&self) -> &ExporterSecret {
&self.exporter_secret
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn membership_key(&self) -> &MembershipKey {
&self.membership_key
}
pub(crate) fn external_secret(&self) -> &ExternalSecret {
&self.external_secret
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn resumption_psk(&self) -> &ResumptionPskSecret {
&self.resumption_psk
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn init_secret(&self) -> &InitSecret {
&self.init_secret
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn encryption_secret(&self) -> &EncryptionSecret {
&self.encryption_secret
}
fn new(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
epoch_secret: EpochSecret,
) -> Result<Self, CryptoError> {
log::debug!(
"Computing EpochSecrets from epoch secret with {}",
ciphersuite
);
log_crypto!(
trace,
" epoch_secret: {:x?}",
epoch_secret.secret.as_slice()
);
let sender_data_secret = SenderDataSecret::new(crypto, ciphersuite, &epoch_secret)?;
let encryption_secret = EncryptionSecret::new(crypto, ciphersuite, &epoch_secret)?;
let exporter_secret = ExporterSecret::new(crypto, ciphersuite, &epoch_secret)?;
let epoch_authenticator = EpochAuthenticator::new(crypto, ciphersuite, &epoch_secret)?;
let external_secret = ExternalSecret::new(crypto, ciphersuite, &epoch_secret)?;
let confirmation_key = ConfirmationKey::new(crypto, ciphersuite, &epoch_secret)?;
let membership_key = MembershipKey::new(crypto, ciphersuite, &epoch_secret)?;
let resumption_psk = ResumptionPskSecret::new(crypto, ciphersuite, &epoch_secret)?;
log::trace!(" Computing init secret.");
let init_secret = InitSecret::new(crypto, ciphersuite, epoch_secret)?;
Ok(EpochSecrets {
init_secret,
sender_data_secret,
encryption_secret,
exporter_secret,
epoch_authenticator,
external_secret,
confirmation_key,
membership_key,
resumption_psk,
})
}
pub(crate) fn with_init_secret(
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
init_secret: InitSecret,
) -> Result<Self, CryptoError> {
let epoch_secret = EpochSecret {
secret: Secret::zero(ciphersuite),
};
let mut epoch_secrets = Self::new(crypto, ciphersuite, epoch_secret)?;
epoch_secrets.init_secret = init_secret;
Ok(epoch_secrets)
}
pub(crate) fn split_secrets(
self,
serialized_context: Vec<u8>,
treesize: TreeSize,
own_index: LeafNodeIndex,
) -> (GroupEpochSecrets, MessageSecrets) {
let secret_tree = self
.encryption_secret
.create_secret_tree(treesize, own_index);
(
GroupEpochSecrets {
init_secret: self.init_secret,
exporter_secret: self.exporter_secret,
epoch_authenticator: self.epoch_authenticator,
external_secret: self.external_secret,
resumption_psk: self.resumption_psk,
},
MessageSecrets::new(
self.sender_data_secret,
self.membership_key,
self.confirmation_key,
serialized_context,
secret_tree,
),
)
}
}
#[derive(Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))]
pub(crate) struct GroupEpochSecrets {
init_secret: InitSecret,
exporter_secret: ExporterSecret,
epoch_authenticator: EpochAuthenticator,
external_secret: ExternalSecret,
resumption_psk: ResumptionPskSecret,
}
impl std::fmt::Debug for GroupEpochSecrets {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("GroupEpochSecrets { *** }")
}
}
#[cfg(not(any(test, feature = "test-utils")))]
impl PartialEq for GroupEpochSecrets {
fn eq(&self, _other: &Self) -> bool {
false
}
}
impl GroupEpochSecrets {
pub(crate) fn init_secret(&self) -> &InitSecret {
&self.init_secret
}
pub(crate) fn epoch_authenticator(&self) -> &EpochAuthenticator {
&self.epoch_authenticator
}
pub(crate) fn exporter_secret(&self) -> &ExporterSecret {
&self.exporter_secret
}
pub(crate) fn external_secret(&self) -> &ExternalSecret {
&self.external_secret
}
pub(crate) fn resumption_psk(&self) -> &ResumptionPskSecret {
&self.resumption_psk
}
}