use openmls_traits::crypto::OpenMlsCrypto;
use openmls_traits::types::Ciphersuite;
use tls_codec::{
Deserialize, Serialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
};
use super::{
codec::deserialize_ciphertext_content, mls_auth_content::FramedContentAuthData,
mls_auth_content_in::VerifiableAuthenticatedContentIn, mls_content_in::FramedContentBodyIn,
};
use crate::{
binary_tree::array_representation::LeafNodeIndex,
error::LibraryError,
framing::mls_content_in::FramedContentIn,
tree::{secret_tree::SecretType, sender_ratchet::SenderRatchetConfiguration},
};
use super::*;
#[derive(
Debug, PartialEq, Eq, Clone, TlsSerialize, TlsSize, TlsDeserialize, TlsDeserializeBytes,
)]
pub struct PrivateMessageIn {
group_id: GroupId,
epoch: GroupEpoch,
content_type: ContentType,
authenticated_data: VLBytes,
encrypted_sender_data: VLBytes,
ciphertext: VLBytes,
}
pub(crate) struct DecryptedContent {
pub(crate) verifiable: VerifiableAuthenticatedContentIn,
#[cfg(feature = "virtual-clients-draft")]
pub(crate) emulator_sender_leaf_index: Option<LeafNodeIndex>,
}
impl PrivateMessageIn {
pub fn aad(&self) -> &[u8] {
self.authenticated_data.as_slice()
}
pub(crate) fn sender_data(
&self,
message_secrets: &MessageSecrets,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
) -> Result<MlsSenderData, MessageDecryptionError> {
log::debug!("Decrypting PrivateMessage");
let sender_data_key = message_secrets
.sender_data_secret()
.derive_aead_key(crypto, ciphersuite, self.ciphertext.as_slice())
.map_err(LibraryError::unexpected_crypto_error)?;
let sender_data_nonce = message_secrets
.sender_data_secret()
.derive_aead_nonce(ciphersuite, crypto, self.ciphertext.as_slice())
.map_err(LibraryError::unexpected_crypto_error)?;
let mls_sender_data_aad =
MlsSenderDataAad::new(self.group_id.clone(), self.epoch, self.content_type);
let mls_sender_data_aad_bytes = mls_sender_data_aad
.tls_serialize_detached()
.map_err(LibraryError::missing_bound_check)?;
log_crypto!(
trace,
"Decryption key for sender data: {sender_data_key:x?}"
);
log_crypto!(trace, "Decryption of sender data mls_sender_data_aad_bytes: {mls_sender_data_aad_bytes:x?} - sender_data_nonce: {sender_data_nonce:x?}");
let sender_data_bytes = sender_data_key
.aead_open(
crypto,
self.encrypted_sender_data.as_slice(),
&mls_sender_data_aad_bytes,
&sender_data_nonce,
)
.map_err(|_| {
log::error!("Sender data decryption error");
MessageDecryptionError::AeadError
})?;
log::trace!(" Successfully decrypted sender data.");
MlsSenderData::tls_deserialize(&mut sender_data_bytes.as_slice())
.map_err(|_| MessageDecryptionError::MalformedContent)
}
#[inline]
fn decrypt(
&self,
crypto: &impl OpenMlsCrypto,
ratchet_key: AeadKey,
ratchet_nonce: &AeadNonce,
) -> Result<PrivateMessageContentIn, MessageDecryptionError> {
let private_message_content_aad_bytes = PrivateContentAad {
group_id: self.group_id.clone(),
epoch: self.epoch,
content_type: self.content_type,
authenticated_data: VLByteSlice(self.authenticated_data.as_slice()),
}
.tls_serialize_detached()
.map_err(LibraryError::missing_bound_check)?;
log_crypto!(
trace,
"Decryption key for private message: {ratchet_key:x?}"
);
log_crypto!(trace, "Decryption of private message private_message_content_aad_bytes: {private_message_content_aad_bytes:x?} - ratchet_nonce: {ratchet_nonce:x?}");
log::trace!("Decrypting ciphertext {:x?}", self.ciphertext);
let private_message_content_bytes = ratchet_key
.aead_open(
crypto,
self.ciphertext.as_slice(),
&private_message_content_aad_bytes,
ratchet_nonce,
)
.map_err(|_| {
log::error!(" Ciphertext decryption error");
MessageDecryptionError::AeadError
})?;
log_content!(
trace,
" Successfully decrypted PublicMessage bytes: {:x?}",
private_message_content_bytes
);
deserialize_ciphertext_content(
&mut private_message_content_bytes.as_slice(),
self.content_type(),
)
.map_err(|_| MessageDecryptionError::MalformedContent)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn to_verifiable_content(
&self,
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
message_secrets: &mut MessageSecrets,
sender_index: LeafNodeIndex,
sender_ratchet_configuration: &SenderRatchetConfiguration,
sender_data: MlsSenderData,
#[cfg(feature = "virtual-clients-draft")] emulator_ctx: Option<
&crate::framing::private_message::EmulatorReuseGuardCtx<'_>,
>,
) -> Result<DecryptedContent, MessageDecryptionError> {
let secret_type = SecretType::from(&self.content_type);
let (ratchet_key, ratchet_nonce) = message_secrets
.secret_tree_mut()
.secret_for_decryption(
ciphersuite,
crypto,
sender_index,
secret_type,
sender_data.generation,
sender_ratchet_configuration,
)
.map_err(|e| {
log::error!(
" Ciphertext generation out of bounds {}\n\t{e:?}",
sender_data.generation
);
MessageDecryptionError::SecretTreeError(e)
})?;
#[cfg(feature = "virtual-clients-draft")]
let emulator_sender_leaf_index = if let Some(ctx) = emulator_ctx {
let prp_key = ctx.reuse_guard_secret.derive_prp_key(
crypto,
ctx.emulation_ciphersuite,
ratchet_nonce.raw_bytes(),
)?;
let x = crypto
.ff1_aes128_decrypt(
&prp_key,
u32::from_be_bytes(sender_data.reuse_guard.bytes()),
)
.map_err(|e| {
log::error!("vc: FF1 inversion of reuse_guard failed: {e:?}");
crate::components::vc_derivation_info::VirtualClientsError::CryptoError(
openmls_traits::types::CryptoError::CryptoLibraryError,
)
})?;
let n_e = u64::from(ctx.emulation_group_size.leaf_count());
if n_e == 0 {
log::error!("vc: emulation_group_size is zero (corrupt state)");
return Err(LibraryError::custom(
"EmulationEpochState has zero emulation_group_size",
)
.into());
}
Some(LeafNodeIndex::new((u64::from(x) % n_e) as u32))
} else {
None
};
let prepared_nonce = ratchet_nonce.xor_with_reuse_guard(&sender_data.reuse_guard);
let private_message_content = self.decrypt(crypto, ratchet_key, &prepared_nonce)?;
let sender = Sender::from_sender_data(sender_data);
log_content!(
trace,
" Successfully decoded PublicMessage with: {:x?}",
private_message_content.content
);
let verifiable = VerifiableAuthenticatedContentIn::new(
WireFormat::PrivateMessage,
FramedContentIn {
group_id: self.group_id.clone(),
epoch: self.epoch,
sender,
authenticated_data: self.authenticated_data.clone(),
body: private_message_content.content,
},
Some(message_secrets.serialized_context().to_vec()),
private_message_content.auth,
);
Ok(DecryptedContent {
verifiable,
#[cfg(feature = "virtual-clients-draft")]
emulator_sender_leaf_index,
})
}
pub fn group_id(&self) -> &GroupId {
&self.group_id
}
pub fn epoch(&self) -> GroupEpoch {
self.epoch
}
pub fn content_type(&self) -> ContentType {
self.content_type
}
#[cfg(test)]
pub(crate) fn set_ciphertext(&mut self, ciphertext: Vec<u8>) {
self.ciphertext = ciphertext.into();
}
}
#[derive(Debug, Clone)]
pub(crate) struct PrivateMessageContentIn {
pub(crate) content: FramedContentBodyIn,
pub(crate) auth: FramedContentAuthData,
}
impl From<PrivateMessage> for PrivateMessageIn {
fn from(value: PrivateMessage) -> Self {
Self {
group_id: value.group_id,
epoch: value.epoch,
content_type: value.content_type,
authenticated_data: value.authenticated_data,
encrypted_sender_data: value.encrypted_sender_data,
ciphertext: value.ciphertext,
}
}
}
#[cfg(any(feature = "test-utils", test))]
impl From<PrivateMessageIn> for PrivateMessage {
fn from(value: PrivateMessageIn) -> Self {
Self {
group_id: value.group_id,
epoch: value.epoch,
content_type: value.content_type,
authenticated_data: value.authenticated_data,
encrypted_sender_data: value.encrypted_sender_data,
ciphertext: value.ciphertext,
}
}
}