mls_spec/drafts/
split_commit.rs1use crate::{
2 crypto::HpkeCiphertext,
3 defs::{ProtocolVersion, WireFormat},
4 group::commits::ProposalOrRef,
5 messages::{MlsMessage, MlsMessageContent},
6 tree::{UpdatePathNode, leaf_node::LeafNode},
7};
8
9pub const WIRE_FORMAT_MLS_SPLIT_COMMIT: u16 = 0xFF5C; static_assertions::const_assert!(
11 *WireFormat::RESERVED_PRIVATE_USE_RANGE.start() <= WIRE_FORMAT_MLS_SPLIT_COMMIT
12 && WIRE_FORMAT_MLS_SPLIT_COMMIT <= *WireFormat::RESERVED_PRIVATE_USE_RANGE.end()
13);
14
15pub const CONTENT_TYPE_SPLIT_COMMIT: u8 = 0xF5;
16
17#[derive(
18 Debug,
19 Clone,
20 PartialEq,
21 Eq,
22 Default,
23 tls_codec::TlsSize,
24 tls_codec::TlsDeserialize,
25 tls_codec::TlsSerialize,
26)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct SplitUpdatePath {
29 pub nodes: Vec<UpdatePathNode>,
30}
31
32#[derive(
33 Debug,
34 Clone,
35 PartialEq,
36 Eq,
37 Default,
38 tls_codec::TlsSize,
39 tls_codec::TlsDeserialize,
40 tls_codec::TlsSerialize,
41)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub struct SplitCommit {
44 #[tls_codec(with = "crate::tlspl::bytes")]
45 pub epoch_identifier: Vec<u8>,
46 pub proposals: Vec<ProposalOrRef>,
47 pub leaf_node: Option<LeafNode>,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52pub struct SplitCommitMessage {
53 pub split_commit_message: Box<MlsMessage>,
54 pub path: Option<SplitUpdatePath>,
55}
56
57impl tls_codec::Size for SplitCommitMessage {
58 fn tls_serialized_len(&self) -> usize {
59 let message_len = matches!(
60 self.split_commit_message.content,
61 MlsMessageContent::MlsPrivateMessage(_) | MlsMessageContent::MlsPublicMessage(_)
62 )
63 .then(|| self.split_commit_message.tls_serialized_len())
64 .unwrap_or_default();
65
66 message_len + self.path.tls_serialized_len()
67 }
68}
69
70impl tls_codec::Serialize for SplitCommitMessage {
71 fn tls_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, tls_codec::Error> {
72 if !matches!(
73 self.split_commit_message.content,
74 MlsMessageContent::MlsPrivateMessage(_) | MlsMessageContent::MlsPublicMessage(_),
75 ) {
76 return Err(tls_codec::Error::EncodingError("Cannot serialize a SplitCommitMessage containing other than PrivateMessage or PublicMessage to avoid infinite recursion".into()));
77 }
78
79 let mut written = self.split_commit_message.tls_serialize(writer)?;
80 written += self.path.tls_serialize(writer)?;
81 Ok(written)
82 }
83}
84
85impl tls_codec::Deserialize for SplitCommitMessage {
86 fn tls_deserialize<R: std::io::Read>(bytes: &mut R) -> Result<Self, tls_codec::Error>
87 where
88 Self: Sized,
89 {
90 let version = ProtocolVersion::tls_deserialize(bytes)?;
91 let wire_format = WireFormat::tls_deserialize(bytes)?;
92 let split_commit_message = match *wire_format {
93 WireFormat::MLS_PRIVATE_MESSAGE => MlsMessage { version, content: MlsMessageContent::MlsPrivateMessage(<_>::tls_deserialize(bytes)?) },
94 WireFormat::MLS_PUBLIC_MESSAGE => MlsMessage { version, content: MlsMessageContent::MlsPublicMessage(<_>::tls_deserialize(bytes)?) },
95 _ => return Err(tls_codec::Error::DecodingError("Cannot deserialize a SplitCommitMessage containing other than PrivateMessage or PublicMessage to avoid infinite recursion".into()))
96 };
97
98 Ok(Self {
99 split_commit_message: Box::new(split_commit_message),
100 path: <_>::tls_deserialize(bytes)?,
101 })
102 }
103}
104
105#[derive(
106 Debug,
107 Clone,
108 PartialEq,
109 Eq,
110 tls_codec::TlsSize,
111 tls_codec::TlsDeserialize,
112 tls_codec::TlsSerialize,
113)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
115pub struct PerMemberCommit {
116 pub split_commit_message: MlsMessage,
117 pub encrypted_path_secret: Option<HpkeCiphertext>,
118}