use crate::{
ciphersuite::hash_ref::{KeyPackageRef, ProposalRef},
error::LibraryError,
extensions::Extension,
group::GroupId,
key_packages::*,
schedule::psk::*,
versions::ProtocolVersion,
};
use openmls_traits::{types::Ciphersuite, OpenMlsCryptoProvider};
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use tls_codec::{
Serialize as TlsSerializeTrait, TlsByteVecU16, TlsDeserialize, TlsSerialize, TlsSize, TlsVecU32,
};
#[derive(
PartialEq, Clone, Copy, Debug, TlsSerialize, TlsDeserialize, TlsSize, Serialize, Deserialize,
)]
#[repr(u16)]
#[allow(missing_docs)]
pub enum ProposalType {
Add = 1,
Update = 2,
Remove = 3,
Presharedkey = 4,
Reinit = 5,
ExternalInit = 6,
AppAck = 7,
GroupContextExtensions = 8,
}
impl ProposalType {
pub fn is_supported(&self) -> bool {
match self {
ProposalType::Add
| ProposalType::Update
| ProposalType::Remove
| ProposalType::Presharedkey
| ProposalType::Reinit
| ProposalType::ExternalInit => true,
ProposalType::AppAck => false,
ProposalType::GroupContextExtensions => true,
}
}
}
impl TryFrom<u16> for ProposalType {
type Error = &'static str;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
1 => Ok(ProposalType::Add),
2 => Ok(ProposalType::Update),
3 => Ok(ProposalType::Remove),
4 => Ok(ProposalType::Presharedkey),
5 => Ok(ProposalType::Reinit),
6 => Ok(ProposalType::ExternalInit),
7 => Ok(ProposalType::AppAck),
8 => Ok(ProposalType::GroupContextExtensions),
_ => Err("Unknown proposal type."),
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[allow(missing_docs)]
pub enum Proposal {
Add(AddProposal),
Update(UpdateProposal),
Remove(RemoveProposal),
PreSharedKey(PreSharedKeyProposal),
ReInit(ReInitProposal),
ExternalInit(ExternalInitProposal),
AppAck(AppAckProposal),
GroupContextExtensions(GroupContextExtensionProposal),
}
impl Proposal {
pub(crate) fn proposal_type(&self) -> ProposalType {
match self {
Proposal::Add(ref _a) => ProposalType::Add,
Proposal::Update(ref _u) => ProposalType::Update,
Proposal::Remove(ref _r) => ProposalType::Remove,
Proposal::PreSharedKey(ref _p) => ProposalType::Presharedkey,
Proposal::ReInit(ref _r) => ProposalType::Reinit,
Proposal::ExternalInit(ref _r) => ProposalType::ExternalInit,
Proposal::AppAck(ref _r) => ProposalType::AppAck,
Proposal::GroupContextExtensions(ref _r) => ProposalType::GroupContextExtensions,
}
}
pub(crate) fn is_type(&self, proposal_type: ProposalType) -> bool {
self.proposal_type() == proposal_type
}
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize,
)]
pub struct AddProposal {
pub(crate) key_package: KeyPackage,
}
impl AddProposal {
pub fn key_package(&self) -> &KeyPackage {
&self.key_package
}
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub struct UpdateProposal {
pub(crate) key_package: KeyPackage,
}
impl UpdateProposal {
pub fn key_package(&self) -> &KeyPackage {
&self.key_package
}
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub struct RemoveProposal {
pub(crate) removed: KeyPackageRef,
}
impl RemoveProposal {
pub fn removed(&self) -> &KeyPackageRef {
&self.removed
}
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub struct PreSharedKeyProposal {
psk: PreSharedKeyId,
}
impl PreSharedKeyProposal {
#[cfg(any(feature = "test-utils", test))]
pub(crate) fn new(psk: PreSharedKeyId) -> Self {
Self { psk }
}
pub(crate) fn _psk(&self) -> &PreSharedKeyId {
&self.psk
}
pub(crate) fn into_psk_id(self) -> PreSharedKeyId {
self.psk
}
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub struct ReInitProposal {
pub(crate) group_id: GroupId,
pub(crate) version: ProtocolVersion,
pub(crate) ciphersuite: Ciphersuite,
pub(crate) extensions: TlsVecU32<Extension>,
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub struct ExternalInitProposal {
kem_output: TlsByteVecU16,
}
impl ExternalInitProposal {
pub(crate) fn kem_output(&self) -> &[u8] {
self.kem_output.as_slice()
}
}
impl From<Vec<u8>> for ExternalInitProposal {
fn from(kem_output: Vec<u8>) -> Self {
ExternalInitProposal {
kem_output: kem_output.into(),
}
}
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub struct AppAckProposal {
received_ranges: TlsVecU32<MessageRange>,
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub struct GroupContextExtensionProposal {
extensions: TlsVecU32<Extension>,
}
impl GroupContextExtensionProposal {
#[cfg(test)]
pub(crate) fn new(extensions: &[Extension]) -> Self {
Self {
extensions: extensions.into(),
}
}
}
#[derive(
PartialEq, Clone, Copy, Debug, TlsSerialize, TlsDeserialize, TlsSize, Serialize, Deserialize,
)]
#[repr(u8)]
pub(crate) enum ProposalOrRefType {
Proposal = 1,
Reference = 2,
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize,
)]
#[repr(u8)]
#[allow(missing_docs)]
pub(crate) enum ProposalOrRef {
#[tls_codec(discriminant = 1)]
Proposal(Proposal),
Reference(ProposalRef),
}
impl ProposalRef {
pub(crate) fn from_proposal(
ciphersuite: Ciphersuite,
backend: &impl OpenMlsCryptoProvider,
proposal: &Proposal,
) -> Result<Self, LibraryError> {
let encoded = proposal
.tls_serialize_detached()
.map_err(LibraryError::missing_bound_check)?;
Self::new(&encoded, ciphersuite, backend.crypto())
.map_err(LibraryError::unexpected_crypto_error)
}
}
#[derive(
Debug, PartialEq, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub(crate) struct MessageRange {
sender: KeyPackageRef,
first_generation: u32,
last_generation: u32,
}