use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite};
use proposal_store::QueuedProposal;
use crate::{
binary_tree::LeafNodeIndex,
ciphersuite::signable::Verifiable,
error::LibraryError,
extensions::ExternalSendersExtension,
group::{errors::ValidationError, mls_group::staged_commit::StagedCommit},
tree::sender_ratchet::SenderRatchetConfiguration,
versions::ProtocolVersion,
};
#[cfg(feature = "extensions-draft-08")]
use crate::messages::proposals_in::ProposalOrRefIn;
use super::{
mls_auth_content::AuthenticatedContent,
mls_auth_content_in::{AuthenticatedContentIn, VerifiableAuthenticatedContentIn},
private_message_in::PrivateMessageIn,
public_message_in::PublicMessageIn,
*,
};
#[derive(Debug)]
pub(crate) struct DecryptedMessage {
verifiable_content: VerifiableAuthenticatedContentIn,
}
impl DecryptedMessage {
pub(crate) fn from_inbound_public_message<'a>(
public_message: PublicMessageIn,
message_secrets_option: impl Into<Option<&'a MessageSecrets>>,
serialized_context: Vec<u8>,
crypto: &impl OpenMlsCrypto,
ciphersuite: Ciphersuite,
) -> Result<Self, ValidationError> {
if public_message.sender().is_member() {
if public_message.membership_tag().is_none() {
return Err(ValidationError::MissingMembershipTag);
}
if let Some(message_secrets) = message_secrets_option.into() {
public_message.verify_membership(
crypto,
ciphersuite,
message_secrets.membership_key(),
message_secrets.serialized_context(),
)?;
}
}
let verifiable_content = public_message.into_verifiable_content(serialized_context);
Self::from_verifiable_content(verifiable_content)
}
pub(crate) fn from_inbound_ciphertext(
ciphertext: PrivateMessageIn,
crypto: &impl OpenMlsCrypto,
group: &mut MlsGroup,
sender_ratchet_configuration: &SenderRatchetConfiguration,
) -> Result<Self, ValidationError> {
let ciphersuite = group.ciphersuite();
let (message_secrets, _old_leaves) = group
.message_secrets_and_leaves_mut(ciphertext.epoch())
.map_err(MessageDecryptionError::SecretTreeError)?;
let sender_data = ciphertext.sender_data(message_secrets, crypto, ciphersuite)?;
if sender_data.leaf_index == group.own_leaf_index() {
return Err(ValidationError::CannotDecryptOwnMessage);
}
let message_secrets = group
.message_secrets_mut(ciphertext.epoch())
.map_err(|_| MessageDecryptionError::AeadError)?;
let verifiable_content = ciphertext.to_verifiable_content(
ciphersuite,
crypto,
message_secrets,
sender_data.leaf_index,
sender_ratchet_configuration,
sender_data,
)?;
Self::from_verifiable_content(verifiable_content)
}
fn from_verifiable_content(
verifiable_content: VerifiableAuthenticatedContentIn,
) -> Result<Self, ValidationError> {
if verifiable_content.content_type() == ContentType::Commit
&& verifiable_content.confirmation_tag().is_none()
{
return Err(ValidationError::MissingConfirmationTag);
}
if verifiable_content.content_type() == ContentType::Application {
if verifiable_content.wire_format() != WireFormat::PrivateMessage {
return Err(ValidationError::UnencryptedApplicationMessage);
} else if !verifiable_content.sender().is_member() {
return Err(LibraryError::custom("Expected sender to be member.").into());
}
}
Ok(DecryptedMessage { verifiable_content })
}
pub(crate) fn credential(
&self,
look_up_credential_with_key: impl Fn(LeafNodeIndex) -> Option<CredentialWithKey>,
external_senders: Option<&ExternalSendersExtension>,
) -> Result<CredentialWithKey, ValidationError> {
let sender = self.sender();
match sender {
Sender::Member(leaf_index) => {
look_up_credential_with_key(*leaf_index).ok_or(ValidationError::UnknownMember)
}
Sender::External(index) => {
let sender = external_senders
.ok_or(ValidationError::NoExternalSendersExtension)?
.get(index.index())
.ok_or(ValidationError::UnauthorizedExternalSender)?;
Ok(CredentialWithKey {
credential: sender.credential().clone(),
signature_key: sender.signature_key().clone(),
})
}
Sender::NewMemberCommit | Sender::NewMemberProposal => {
self.verifiable_content.new_member_credential()
}
}
}
pub fn sender(&self) -> &Sender {
self.verifiable_content.sender()
}
pub(crate) fn verifiable_content(&self) -> &VerifiableAuthenticatedContentIn {
&self.verifiable_content
}
}
#[derive(Debug, Clone)]
pub(crate) enum SenderContext {
Member((GroupId, LeafNodeIndex)),
ExternalCommit {
group_id: GroupId,
leftmost_blank_index: LeafNodeIndex,
self_removes_in_store: Vec<SelfRemoveInStore>,
},
}
#[derive(Debug, Clone)]
pub struct UnverifiedMessage {
verifiable_content: VerifiableAuthenticatedContentIn,
credential: Credential,
sender_pk: OpenMlsSignaturePublicKey,
sender_context: Option<SenderContext>,
}
impl UnverifiedMessage {
pub(crate) fn from_decrypted_message(
decrypted_message: DecryptedMessage,
credential: Credential,
sender_pk: OpenMlsSignaturePublicKey,
sender_context: Option<SenderContext>,
) -> Self {
UnverifiedMessage {
verifiable_content: decrypted_message.verifiable_content,
credential,
sender_pk,
sender_context,
}
}
pub(crate) fn verify(
self,
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
protocol_version: ProtocolVersion,
) -> Result<(AuthenticatedContent, Credential), ValidationError> {
let content: AuthenticatedContentIn = self
.verifiable_content
.verify(crypto, &self.sender_pk)
.map_err(|_| ValidationError::InvalidSignature)?;
let content =
content.validate(ciphersuite, crypto, self.sender_context, protocol_version)?;
Ok((content, self.credential))
}
#[cfg(feature = "extensions-draft-08")]
pub fn committed_proposals(&self) -> Option<&[ProposalOrRefIn]> {
self.verifiable_content.committed_proposals()
}
}
#[derive(Debug)]
pub struct ProcessedMessage {
group_id: GroupId,
epoch: GroupEpoch,
sender: Sender,
authenticated_data: Vec<u8>,
content: ProcessedMessageContent,
credential: Credential,
}
impl ProcessedMessage {
pub(crate) fn new(
group_id: GroupId,
epoch: GroupEpoch,
sender: Sender,
authenticated_data: Vec<u8>,
content: ProcessedMessageContent,
credential: Credential,
) -> Self {
Self {
group_id,
epoch,
sender,
authenticated_data,
content,
credential,
}
}
pub fn group_id(&self) -> &GroupId {
&self.group_id
}
pub fn epoch(&self) -> GroupEpoch {
self.epoch
}
pub fn sender(&self) -> &Sender {
&self.sender
}
pub fn aad(&self) -> &[u8] {
&self.authenticated_data
}
pub fn content(&self) -> &ProcessedMessageContent {
&self.content
}
pub fn into_content(self) -> ProcessedMessageContent {
self.content
}
pub fn credential(&self) -> &Credential {
&self.credential
}
#[cfg(feature = "extensions-draft-08")]
pub fn safe_export_secret<Crypto: OpenMlsCrypto>(
&mut self,
crypto: &Crypto,
component_id: u16,
) -> Result<Vec<u8>, ProcessedMessageSafeExportSecretError> {
if let ProcessedMessageContent::StagedCommitMessage(ref mut staged_commit) =
&mut self.content
{
let secret = staged_commit.safe_export_secret(crypto, component_id)?;
Ok(secret)
} else {
Err(ProcessedMessageSafeExportSecretError::NotACommit)
}
}
}
#[derive(Debug)]
pub enum ProcessedMessageContent {
ApplicationMessage(ApplicationMessage),
ProposalMessage(Box<QueuedProposal>),
ExternalJoinProposalMessage(Box<QueuedProposal>),
StagedCommitMessage(Box<StagedCommit>),
}
#[derive(Debug, PartialEq, Eq)]
pub struct ApplicationMessage {
bytes: Vec<u8>,
}
impl ApplicationMessage {
pub(crate) fn new(bytes: Vec<u8>) -> Self {
Self { bytes }
}
pub fn into_bytes(self) -> Vec<u8> {
self.bytes
}
}