use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider};
use tls_codec::{Serialize, TlsByteVecU8, TlsDeserialize, TlsSerialize, TlsSize, TlsVecU32};
use crate::{
ciphersuite::{
hash_ref::KeyPackageRef,
signable::{Signable, SignedStruct, Verifiable, VerifiedStruct},
HpkePublicKey, Signature,
},
error::LibraryError,
extensions::Extension,
group::*,
versions::ProtocolVersion,
};
#[derive(PartialEq, Debug, TlsSerialize, TlsSize)]
pub struct PublicGroupState {
pub(crate) version: ProtocolVersion,
pub(crate) ciphersuite: Ciphersuite,
pub(crate) group_id: GroupId,
pub(crate) epoch: GroupEpoch,
pub(crate) tree_hash: TlsByteVecU8,
pub(crate) interim_transcript_hash: TlsByteVecU8,
pub(crate) confirmed_transcript_hash: TlsByteVecU8,
pub(crate) group_context_extensions: TlsVecU32<Extension>,
pub(crate) other_extensions: TlsVecU32<Extension>,
pub(crate) external_pub: HpkePublicKey,
pub(crate) signer: KeyPackageRef,
pub(crate) signature: Signature,
}
#[derive(Debug, Clone, TlsSize, TlsDeserialize, TlsSerialize)]
pub struct VerifiablePublicGroupState {
tbs: PublicGroupStateTbs,
signature: Signature,
}
impl VerifiablePublicGroupState {
pub(crate) fn version(&self) -> ProtocolVersion {
self.tbs.version
}
pub(crate) fn ciphersuite(&self) -> Ciphersuite {
self.tbs.ciphersuite
}
pub(crate) fn tree_hash(&self) -> &[u8] {
self.tbs.tree_hash.as_slice()
}
pub(crate) fn signer(&self) -> &KeyPackageRef {
&self.tbs.signer
}
pub(crate) fn other_extensions(&self) -> &[Extension] {
self.tbs.other_extensions.as_slice()
}
}
mod private_mod {
#[derive(Default)]
pub struct Seal;
}
impl VerifiedStruct<VerifiablePublicGroupState> for PublicGroupState {
fn from_verifiable(v: VerifiablePublicGroupState, _seal: Self::SealingType) -> Self {
Self {
version: v.tbs.version,
ciphersuite: v.tbs.ciphersuite,
group_id: v.tbs.group_id,
epoch: v.tbs.epoch,
tree_hash: v.tbs.tree_hash,
interim_transcript_hash: v.tbs.interim_transcript_hash,
confirmed_transcript_hash: v.tbs.confirmed_transcript_hash,
group_context_extensions: v.tbs.group_context_extensions,
other_extensions: v.tbs.other_extensions,
external_pub: v.tbs.external_pub,
signer: v.tbs.signer,
signature: v.signature,
}
}
type SealingType = private_mod::Seal;
}
impl SignedStruct<PublicGroupStateTbs> for PublicGroupState {
fn from_payload(tbs: PublicGroupStateTbs, signature: Signature) -> Self {
Self {
version: tbs.version,
ciphersuite: tbs.ciphersuite,
group_id: tbs.group_id,
epoch: tbs.epoch,
tree_hash: tbs.tree_hash,
interim_transcript_hash: tbs.interim_transcript_hash,
confirmed_transcript_hash: tbs.confirmed_transcript_hash,
group_context_extensions: tbs.group_context_extensions,
other_extensions: tbs.other_extensions,
external_pub: tbs.external_pub,
signer: tbs.signer,
signature,
}
}
}
impl<'a> Verifiable for VerifiablePublicGroupState {
fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
self.tbs.tls_serialize_detached()
}
fn signature(&self) -> &Signature {
&self.signature
}
}
#[derive(TlsSize, TlsSerialize, TlsDeserialize, Debug, Clone)]
pub(crate) struct PublicGroupStateTbs {
pub(crate) version: ProtocolVersion,
pub(crate) ciphersuite: Ciphersuite,
pub(crate) group_id: GroupId,
pub(crate) epoch: GroupEpoch,
pub(crate) tree_hash: TlsByteVecU8,
pub(crate) interim_transcript_hash: TlsByteVecU8,
pub(crate) confirmed_transcript_hash: TlsByteVecU8,
pub(crate) group_context_extensions: TlsVecU32<Extension>,
pub(crate) other_extensions: TlsVecU32<Extension>,
pub(crate) external_pub: HpkePublicKey,
pub(crate) signer: KeyPackageRef,
}
impl PublicGroupStateTbs {
pub(crate) fn new(
backend: &impl OpenMlsCryptoProvider,
core_group: &CoreGroup,
) -> Result<Self, LibraryError> {
let ciphersuite = core_group.ciphersuite();
let external_pub = core_group
.group_epoch_secrets()
.external_secret()
.derive_external_keypair(backend.crypto(), ciphersuite)
.public;
let group_id = core_group.group_id().clone();
let epoch = core_group.context().epoch();
let tree_hash = core_group.treesync().tree_hash().into();
let interim_transcript_hash = core_group.interim_transcript_hash().into();
let confirmed_transcript_hash = core_group.confirmed_transcript_hash().into();
let other_extensions = core_group.other_extensions().into();
Ok(PublicGroupStateTbs {
version: core_group.version(),
group_id,
epoch,
tree_hash,
interim_transcript_hash,
confirmed_transcript_hash,
group_context_extensions: core_group.group_context_extensions().into(),
other_extensions,
external_pub: external_pub.into(),
ciphersuite,
signer: *core_group
.key_package_ref()
.ok_or_else(|| LibraryError::custom("missing key package ref"))?,
})
}
}
impl Signable for PublicGroupStateTbs {
type SignedOutput = PublicGroupState;
fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
self.tls_serialize_detached()
}
}