mls-spec 2.0.1

This crate is a repository of MLS / RFC9420-related data structures.
Documentation
use crate::{
    SensitiveBytes,
    defs::{CiphersuiteId, LeafIndex, ProposalType, ProtocolVersion},
    group::{GroupId, extensions::Extension},
    key_package::KeyPackage,
    key_schedule::{GroupContext, PreSharedKeyId},
    tree::leaf_node::LeafNode,
};

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    tls_codec::TlsSerialize,
    tls_codec::TlsDeserialize,
    tls_codec::TlsSize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
pub enum Proposal {
    #[tls_codec(discriminant = "ProposalType::ADD")]
    Add(AddProposal),
    #[tls_codec(discriminant = "ProposalType::UPDATE")]
    Update(UpdateProposal),
    #[tls_codec(discriminant = "ProposalType::REMOVE")]
    Remove(RemoveProposal),
    #[tls_codec(discriminant = "ProposalType::PSK")]
    PreSharedKey(PreSharedKeyProposal),
    #[tls_codec(discriminant = "ProposalType::REINIT")]
    ReInit(ReInitProposal),
    #[tls_codec(discriminant = "ProposalType::EXTERNAL_INIT")]
    ExternalInit(ExternalInitProposal),
    #[tls_codec(discriminant = "ProposalType::GROUP_CONTEXT_EXTENSIONS")]
    GroupContextExtensions(GroupContextExtensionsProposal),
    #[cfg(feature = "draft-ietf-mls-extensions")]
    #[tls_codec(discriminant = "ProposalType::APP_DATA_UPDATE")]
    AppDataUpdate(crate::drafts::mls_extensions::safe_application::AppDataUpdate),
    #[cfg(feature = "draft-ietf-mls-extensions")]
    #[tls_codec(discriminant = "ProposalType::APP_EPHEMERAL")]
    AppEphemeral(crate::drafts::mls_extensions::safe_application::AppEphemeral),
    #[cfg(feature = "draft-ietf-mls-extensions")]
    #[tls_codec(discriminant = "ProposalType::SELF_REMOVE")]
    SelfRemove(crate::drafts::mls_extensions::self_remove::SelfRemoveProposal),
}

impl Proposal {
    #[inline(always)]
    pub fn proposal_type(&self) -> ProposalType {
        self.into()
    }
}

impl From<&Proposal> for ProposalType {
    fn from(val: &Proposal) -> Self {
        match val {
            Proposal::Add(_) => ProposalType::new_unchecked(ProposalType::ADD),
            Proposal::Update(_) => ProposalType::new_unchecked(ProposalType::UPDATE),
            Proposal::Remove(_) => ProposalType::new_unchecked(ProposalType::REMOVE),
            Proposal::PreSharedKey(_) => ProposalType::new_unchecked(ProposalType::PSK),
            Proposal::ReInit(_) => ProposalType::new_unchecked(ProposalType::REINIT),
            Proposal::ExternalInit(_) => ProposalType::new_unchecked(ProposalType::EXTERNAL_INIT),
            Proposal::GroupContextExtensions(_) => {
                ProposalType::new_unchecked(ProposalType::GROUP_CONTEXT_EXTENSIONS)
            }
            #[cfg(feature = "draft-ietf-mls-extensions")]
            Proposal::AppDataUpdate(_) => {
                ProposalType::new_unchecked(ProposalType::APP_DATA_UPDATE)
            }
            #[cfg(feature = "draft-ietf-mls-extensions")]
            Proposal::AppEphemeral(_) => ProposalType::new_unchecked(ProposalType::APP_EPHEMERAL),
            #[cfg(feature = "draft-ietf-mls-extensions")]
            Proposal::SelfRemove(_) => ProposalType::new_unchecked(ProposalType::SELF_REMOVE),
        }
    }
}

impl Proposal {
    #[inline]
    pub fn needs_update_path(&self) -> bool {
        self.proposal_type().needs_update_path()
    }
}

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    tls_codec::TlsSerialize,
    tls_codec::TlsDeserialize,
    tls_codec::TlsSize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AddProposal {
    pub key_package: KeyPackage,
}

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    tls_codec::TlsSerialize,
    tls_codec::TlsDeserialize,
    tls_codec::TlsSize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UpdateProposal {
    pub leaf_node: LeafNode,
}

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    tls_codec::TlsSerialize,
    tls_codec::TlsDeserialize,
    tls_codec::TlsSize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RemoveProposal {
    pub removed: LeafIndex,
}

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    tls_codec::TlsSerialize,
    tls_codec::TlsDeserialize,
    tls_codec::TlsSize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PreSharedKeyProposal {
    pub psk: PreSharedKeyId,
}

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    tls_codec::TlsSerialize,
    tls_codec::TlsDeserialize,
    tls_codec::TlsSize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ReInitProposal {
    pub group_id: GroupId,
    pub version: ProtocolVersion,
    pub cipher_suite: CiphersuiteId,
    pub extensions: Vec<Extension>,
}

impl ReInitProposal {
    pub fn matches_group_context(&self, ctx: &GroupContext) -> bool {
        self.group_id == ctx.group_id()
            && self.version == ctx.version
            && self.cipher_suite == ctx.cipher_suite
            && self.extensions == ctx.extensions
    }
}

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    tls_codec::TlsSerialize,
    tls_codec::TlsDeserialize,
    tls_codec::TlsSize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ExternalInitProposal {
    pub kem_output: SensitiveBytes,
}

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    tls_codec::TlsSerialize,
    tls_codec::TlsDeserialize,
    tls_codec::TlsSize,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GroupContextExtensionsProposal {
    pub extensions: Vec<Extension>,
}