use openmls_traits::crypto::OpenMlsCrypto;
use openmls_traits::types::Ciphersuite;
use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
use thiserror::Error;
use tls_codec::{
Deserialize, Serialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
};
use crate::{
binary_tree::LeafNodeIndex,
ciphersuite::{
signable::{Signable, SignedStruct, Verifiable, VerifiedStruct},
AeadKey, AeadNonce, Signature,
},
extensions::Extensions,
group::{GroupContext, GroupEpoch, GroupId},
messages::ConfirmationTag,
};
const SIGNATURE_GROUP_INFO_LABEL: &str = "GroupInfoTBS";
#[derive(Debug, PartialEq, Clone, TlsDeserialize, TlsDeserializeBytes, 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],
crypto: &impl OpenMlsCrypto,
) -> Result<Self, GroupInfoError> {
let verifiable_group_info_plaintext = skey
.aead_open(crypto, 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 fn extensions(&self) -> &Extensions {
&self.payload.extensions
}
pub fn group_id(&self) -> &GroupId {
self.payload.group_context.group_id()
}
pub fn epoch(&self) -> GroupEpoch {
self.payload.group_context.epoch()
}
}
#[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 fn extensions(&self) -> &Extensions {
&self.payload.extensions
}
pub fn signature(&self) -> &Signature {
&self.signature
}
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,
}
}
}
#[derive(
Debug,
PartialEq,
Clone,
TlsDeserialize,
TlsDeserializeBytes,
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 {
type VerifiedStruct = GroupInfo;
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
}
fn verify(
self,
crypto: &impl OpenMlsCrypto,
pk: &crate::ciphersuite::OpenMlsSignaturePublicKey,
) -> Result<Self::VerifiedStruct, crate::ciphersuite::signable::SignatureError> {
self.verify_no_out(crypto, pk)?;
Ok(GroupInfo {
payload: self.payload,
signature: self.signature,
})
}
}
impl VerifiedStruct for GroupInfo {}