use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider};
use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
use thiserror::Error;
use tls_codec::{Deserialize, Serialize, TlsDeserialize, TlsSerialize, TlsSize};
use crate::{
binary_tree::LeafNodeIndex,
ciphersuite::{
signable::{Signable, SignedStruct, Verifiable, VerifiedStruct},
AeadKey, AeadNonce, Signature,
},
extensions::Extensions,
group::{GroupContext, GroupId},
messages::ConfirmationTag,
};
const SIGNATURE_GROUP_INFO_LABEL: &str = "GroupInfoTBS";
#[derive(Debug, PartialEq, Clone, TlsDeserialize, TlsSize)]
#[cfg_attr(any(test, feature = "test-utils"), derive(TlsSerialize))]
pub struct VerifiableGroupInfo {
payload: GroupInfoTBS,
signature: Signature,
}
#[derive(Error, Debug, PartialEq, Clone)]
pub enum GroupInfoError {
#[error("Decryption failed.")]
DecryptionFailed,
#[error("Malformed.")]
Malformed,
}
impl VerifiableGroupInfo {
pub fn new(
group_context: GroupContext,
extensions: Extensions,
confirmation_tag: ConfirmationTag,
signer: LeafNodeIndex,
signature: Signature,
) -> Self {
let payload = GroupInfoTBS {
group_context,
extensions,
confirmation_tag,
signer,
};
Self { payload, signature }
}
pub(crate) fn try_from_ciphertext(
skey: &AeadKey,
nonce: &AeadNonce,
ciphertext: &[u8],
context: &[u8],
backend: &impl OpenMlsCryptoProvider,
) -> Result<Self, GroupInfoError> {
let verifiable_group_info_plaintext = skey
.aead_open(backend, ciphertext, context, nonce)
.map_err(|_| GroupInfoError::DecryptionFailed)?;
let mut verifiable_group_info_plaintext_slice = verifiable_group_info_plaintext.as_slice();
let verifiable_group_info =
VerifiableGroupInfo::tls_deserialize(&mut verifiable_group_info_plaintext_slice)
.map_err(|_| GroupInfoError::Malformed)?;
if !verifiable_group_info_plaintext_slice.is_empty() {
return Err(GroupInfoError::Malformed);
}
Ok(verifiable_group_info)
}
pub fn ciphersuite(&self) -> Ciphersuite {
self.payload.group_context.ciphersuite()
}
pub(crate) fn signer(&self) -> LeafNodeIndex {
self.payload.signer
}
pub(crate) fn extensions(&self) -> &Extensions {
&self.payload.extensions
}
pub(crate) fn group_id(&self) -> &GroupId {
self.payload.group_context.group_id()
}
}
#[cfg(test)]
impl VerifiableGroupInfo {
pub(crate) fn payload_mut(&mut self) -> &mut GroupInfoTBS {
&mut self.payload
}
pub(crate) fn break_signature(&mut self) {
self.signature.modify(b"");
}
}
#[cfg(any(feature = "test-utils", test))]
impl From<VerifiableGroupInfo> for GroupInfo {
fn from(vgi: VerifiableGroupInfo) -> Self {
GroupInfo {
payload: vgi.payload,
signature: vgi.signature,
}
}
}
#[derive(Debug, PartialEq, Clone, TlsSerialize, TlsSize, SerdeSerialize, SerdeDeserialize)]
#[cfg_attr(feature = "test-utils", derive(TlsDeserialize))]
pub struct GroupInfo {
payload: GroupInfoTBS,
signature: Signature,
}
impl GroupInfo {
pub fn group_context(&self) -> &GroupContext {
&self.payload.group_context
}
pub(crate) fn extensions(&self) -> &Extensions {
&self.payload.extensions
}
pub(crate) fn confirmation_tag(&self) -> &ConfirmationTag {
&self.payload.confirmation_tag
}
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn into_verifiable_group_info(self) -> VerifiableGroupInfo {
VerifiableGroupInfo {
payload: GroupInfoTBS {
group_context: self.payload.group_context,
extensions: self.payload.extensions,
confirmation_tag: self.payload.confirmation_tag,
signer: self.payload.signer,
},
signature: self.signature,
}
}
}
impl From<GroupInfo> for GroupContext {
fn from(value: GroupInfo) -> Self {
value.payload.group_context
}
}
#[derive(
Debug, PartialEq, Clone, TlsDeserialize, TlsSerialize, TlsSize, SerdeSerialize, SerdeDeserialize,
)]
pub(crate) struct GroupInfoTBS {
group_context: GroupContext,
extensions: Extensions,
confirmation_tag: ConfirmationTag,
signer: LeafNodeIndex,
}
impl GroupInfoTBS {
pub(crate) fn new(
group_context: GroupContext,
extensions: Extensions,
confirmation_tag: ConfirmationTag,
signer: LeafNodeIndex,
) -> Self {
Self {
group_context,
extensions,
confirmation_tag,
signer,
}
}
}
#[cfg(test)]
impl GroupInfoTBS {
pub(crate) fn group_context_mut(&mut self) -> &mut GroupContext {
&mut self.group_context
}
}
impl Signable for GroupInfoTBS {
type SignedOutput = GroupInfo;
fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
self.tls_serialize_detached()
}
fn label(&self) -> &str {
SIGNATURE_GROUP_INFO_LABEL
}
}
impl SignedStruct<GroupInfoTBS> for GroupInfo {
fn from_payload(payload: GroupInfoTBS, signature: Signature) -> Self {
Self { payload, signature }
}
}
impl Verifiable for VerifiableGroupInfo {
fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
self.payload.tls_serialize_detached()
}
fn signature(&self) -> &Signature {
&self.signature
}
fn label(&self) -> &str {
SIGNATURE_GROUP_INFO_LABEL
}
}
impl VerifiedStruct<VerifiableGroupInfo> for GroupInfo {
type SealingType = private_mod::Seal;
fn from_verifiable(v: VerifiableGroupInfo, _seal: Self::SealingType) -> Self {
Self {
payload: v.payload,
signature: v.signature,
}
}
}
mod private_mod {
#[derive(Default)]
pub struct Seal;
}