use std::io::Read;
use openmls_traits::{crypto::OpenMlsCrypto, types::Ciphersuite};
use tls_codec::Serialize as TlsSerializeTrait;
use super::{mls_auth_content::*, mls_content_in::*, *};
use crate::{
ciphersuite::signable::{SignedStruct, Verifiable, VerifiedStruct},
credentials::CredentialWithKey,
group::errors::ValidationError,
messages::proposals_in::ProposalIn,
versions::ProtocolVersion,
};
#[cfg(feature = "extensions-draft-08")]
use crate::messages::proposals_in::ProposalOrRefIn;
#[cfg(doc)]
use super::{PrivateMessageIn, PublicMessageIn};
#[derive(PartialEq, Debug, Clone, TlsSize)]
pub(crate) struct AuthenticatedContentIn {
pub(super) wire_format: WireFormat,
pub(super) content: FramedContentIn,
pub(super) auth: FramedContentAuthData,
}
impl AuthenticatedContentIn {
pub(crate) fn validate(
self,
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
sender_context: Option<SenderContext>,
protocol_version: ProtocolVersion,
) -> Result<AuthenticatedContent, ValidationError> {
Ok(AuthenticatedContent {
wire_format: self.wire_format,
content: self.content.validate(
ciphersuite,
crypto,
sender_context,
protocol_version,
)?,
auth: self.auth,
})
}
}
#[cfg(any(feature = "test-utils", test))]
impl AuthenticatedContentIn {
pub(crate) fn content(&self) -> &FramedContentBodyIn {
&self.content.body
}
}
impl tls_codec::Deserialize for AuthenticatedContentIn {
fn tls_deserialize<R: Read>(bytes: &mut R) -> Result<Self, Error>
where
Self: Sized,
{
let wire_format = WireFormat::tls_deserialize(bytes)?;
let content = FramedContentIn::tls_deserialize(bytes)?;
let auth = FramedContentAuthData::deserialize(bytes, content.body.content_type())?;
Ok(Self {
wire_format,
content,
auth,
})
}
}
#[cfg(any(feature = "test-utils", test))]
impl From<VerifiableAuthenticatedContentIn> for AuthenticatedContentIn {
fn from(v: VerifiableAuthenticatedContentIn) -> Self {
AuthenticatedContentIn {
wire_format: v.tbs.wire_format,
content: v.tbs.content,
auth: v.auth,
}
}
}
#[derive(PartialEq, Debug, Clone)]
pub(crate) struct VerifiableAuthenticatedContentIn {
tbs: FramedContentTbsIn,
auth: FramedContentAuthData,
}
impl VerifiableAuthenticatedContentIn {
pub(crate) fn new(
wire_format: WireFormat,
content: FramedContentIn,
serialized_context: impl Into<Option<Vec<u8>>>,
auth: FramedContentAuthData,
) -> Self {
let tbs = FramedContentTbsIn {
version: ProtocolVersion::default(),
wire_format,
content,
serialized_context: serialized_context.into(),
};
Self { tbs, auth }
}
pub fn sender(&self) -> &Sender {
&self.tbs.content.sender
}
pub(crate) fn epoch(&self) -> GroupEpoch {
self.tbs.content.epoch
}
pub(crate) fn new_member_credential(&self) -> Result<CredentialWithKey, ValidationError> {
match self.tbs.content.sender {
Sender::NewMemberCommit => {
match &self.tbs.content.body {
FramedContentBodyIn::Commit(commit) => commit
.unverified_credential()
.ok_or(ValidationError::NoPath),
_ => Err(ValidationError::NotACommit),
}
}
Sender::NewMemberProposal => {
match &self.tbs.content.body {
FramedContentBodyIn::Proposal(ProposalIn::Add(add_proposal)) => {
Ok(add_proposal.unverified_credential())
}
_ => Err(ValidationError::NotAnExternalAddProposal),
}
}
_ => Err(ValidationError::UnknownMember),
}
}
pub(crate) fn wire_format(&self) -> WireFormat {
self.tbs.wire_format
}
pub(crate) fn confirmation_tag(&self) -> Option<&ConfirmationTag> {
self.auth.confirmation_tag.as_ref()
}
pub(crate) fn content_type(&self) -> ContentType {
self.tbs.content.body.content_type()
}
#[cfg(feature = "extensions-draft-08")]
pub(crate) fn committed_proposals(&self) -> Option<&[ProposalOrRefIn]> {
self.tbs.content.proposals()
}
}
impl Verifiable for VerifiableAuthenticatedContentIn {
type VerifiedStruct = AuthenticatedContentIn;
fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
self.tbs.tls_serialize_detached()
}
fn signature(&self) -> &Signature {
&self.auth.signature
}
fn label(&self) -> &str {
"FramedContentTBS"
}
fn verify(
self,
crypto: &impl OpenMlsCrypto,
pk: &OpenMlsSignaturePublicKey,
) -> Result<Self::VerifiedStruct, signable::SignatureError> {
self.verify_no_out(crypto, pk)?;
Ok(AuthenticatedContentIn {
wire_format: self.tbs.wire_format,
content: self.tbs.content,
auth: self.auth,
})
}
}
impl VerifiedStruct for AuthenticatedContentIn {}
impl SignedStruct<FramedContentTbsIn> for AuthenticatedContentIn {
fn from_payload(
tbs: FramedContentTbsIn,
signature: Signature,
_serialized_payload: Vec<u8>,
) -> Self {
let auth = FramedContentAuthData {
signature,
confirmation_tag: None,
};
Self {
wire_format: tbs.wire_format,
content: tbs.content,
auth,
}
}
}
#[cfg(any(feature = "test-utils", test))]
impl From<AuthenticatedContentIn> for AuthenticatedContent {
fn from(v: AuthenticatedContentIn) -> Self {
AuthenticatedContent {
wire_format: v.wire_format,
content: v.content.into(),
auth: v.auth,
}
}
}
impl From<AuthenticatedContent> for AuthenticatedContentIn {
fn from(v: AuthenticatedContent) -> Self {
AuthenticatedContentIn {
wire_format: v.wire_format,
content: v.content.into(),
auth: v.auth,
}
}
}