mls_spec/drafts/
semiprivate_message.rs

1use crate::{
2    SensitiveBytes,
3    credential::Credential,
4    crypto::{HpkeCiphertext, HpkePublicKey},
5    defs::{Epoch, LeafIndex, WireFormat},
6    group::HashReference,
7    messages::ReuseGuard,
8};
9
10use super::mls_extensions::safe_application::{Component, ComponentId};
11
12pub const EXTERNAL_RECEIVERS_COMPONENT_ID: ComponentId = 0xFEEE_0000; // TODO: Waiting for IANA registration
13static_assertions::const_assert!(
14    *super::mls_extensions::COMPONENT_RESERVED_PRIVATE_RANGE.start()
15        <= EXTERNAL_RECEIVERS_COMPONENT_ID
16        && EXTERNAL_RECEIVERS_COMPONENT_ID
17            <= *super::mls_extensions::COMPONENT_RESERVED_PRIVATE_RANGE.end()
18);
19
20pub const WIRE_FORMAT_MLS_SEMIPRIVATE_MESSAGE: u16 = 0xFAFE; // TODO: Waiting for IANA registration
21static_assertions::const_assert!(
22    *WireFormat::RESERVED_PRIVATE_USE_RANGE.start() <= WIRE_FORMAT_MLS_SEMIPRIVATE_MESSAGE
23        && WIRE_FORMAT_MLS_SEMIPRIVATE_MESSAGE <= *WireFormat::RESERVED_PRIVATE_USE_RANGE.end()
24);
25
26pub type ExternalReceiverRef = HashReference;
27
28#[derive(
29    Debug,
30    Clone,
31    PartialEq,
32    Eq,
33    tls_codec::TlsSerialize,
34    tls_codec::TlsDeserialize,
35    tls_codec::TlsSize,
36)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38pub struct ExternalReceiver {
39    pub external_receiver_public_key: HpkePublicKey,
40    pub credential: Credential,
41}
42
43#[derive(
44    Debug,
45    Clone,
46    PartialEq,
47    Eq,
48    tls_codec::TlsSerialize,
49    tls_codec::TlsDeserialize,
50    tls_codec::TlsSize,
51)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53pub struct ExternalReceivers {
54    pub external_receivers: Vec<ExternalReceiver>,
55}
56
57impl Component for ExternalReceivers {
58    fn component_id() -> ComponentId {
59        EXTERNAL_RECEIVERS_COMPONENT_ID
60    }
61}
62
63/// <https://rohanmahy.github.io/mls-semiprivatemessage/draft-mahy-mls-semiprivatemessage.html#section-3.1-2>
64///
65/// ```notrust,ignore
66/// struct {
67///   opaque key<V>;
68///   opaque nonce<V>;
69///   opaque reuse_guard[4];
70///   uint32 sender_leaf_index;
71/// } PerMessageKeyAndNonces;
72/// ```
73///
74#[derive(
75    Debug,
76    Clone,
77    PartialEq,
78    Eq,
79    tls_codec::TlsSerialize,
80    tls_codec::TlsDeserialize,
81    tls_codec::TlsSize,
82)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84pub struct PerMessageKeyAndNonces {
85    pub key: SensitiveBytes,
86    pub nonce: SensitiveBytes,
87    pub reuse_guard: ReuseGuard,
88    pub sender_leaf_index: LeafIndex,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, tls_codec::TlsSerialize, tls_codec::TlsSize)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize))]
93pub struct SemiPrivateMessageContext<'a> {
94    #[tls_codec(with = "crate::tlspl::bytes")]
95    pub group_id: &'a [u8],
96    pub epoch: &'a Epoch,
97    #[tls_codec(with = "crate::tlspl::bytes")]
98    pub partial_context_hash: &'a [u8],
99}
100
101#[derive(
102    Debug,
103    Clone,
104    PartialEq,
105    Eq,
106    tls_codec::TlsSerialize,
107    tls_codec::TlsDeserialize,
108    tls_codec::TlsSize,
109)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
111pub struct KeyForExternalReceiver {
112    pub external_receiver_ref: ExternalReceiverRef,
113    pub encrypted_keys_and_nonces: HpkeCiphertext,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, tls_codec::TlsSerialize, tls_codec::TlsSize)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize))]
118pub struct KeyForExternalReceiverRef<'a> {
119    pub external_receiver_ref: &'a ExternalReceiverRef,
120    pub encrypted_keys_and_nonces: &'a HpkeCiphertext,
121}
122
123pub mod messages {
124    use crate::{
125        SensitiveBytes,
126        defs::Epoch,
127        group::{GroupId, GroupIdRef, commits::Commit, proposals::Proposal},
128        messages::{ContentType, ContentTypeInner, FramedContentAuthData, PrivateMessageContent},
129    };
130
131    use super::{KeyForExternalReceiver, KeyForExternalReceiverRef};
132
133    #[derive(Debug, Clone, PartialEq, Eq, tls_codec::TlsSerialize, tls_codec::TlsSize)]
134    #[cfg_attr(feature = "serde", derive(serde::Serialize))]
135    pub struct SemiPrivateContentAad<'a> {
136        #[tls_codec(with = "crate::tlspl::bytes")]
137        pub group_id: GroupIdRef<'a>,
138        pub epoch: &'a Epoch,
139        pub content_type: &'a ContentType,
140        #[tls_codec(with = "crate::tlspl::bytes")]
141        pub authenticated_data: &'a [u8],
142        #[tls_codec(with = "crate::tlspl::bytes")]
143        pub partial_context_hash: &'a [u8],
144        pub keys_for_external_receivers: &'a [KeyForExternalReceiverRef<'a>],
145        #[tls_codec(with = "crate::tlspl::bytes")]
146        pub framed_content_tbs_hash: &'a [u8],
147    }
148
149    #[derive(Debug, Clone, PartialEq, Eq)]
150    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
151    pub struct SemiPrivateMessageContent {
152        pub inner: ContentTypeInner,
153        pub auth: FramedContentAuthData,
154        pub padding_len: usize,
155    }
156
157    impl tls_codec::Size for SemiPrivateMessageContent {
158        fn tls_serialized_len(&self) -> usize {
159            self.inner.tls_serialized_len() + self.auth.tls_serialized_len() + self.padding_len
160        }
161    }
162
163    impl tls_codec::Serialize for SemiPrivateMessageContent {
164        fn tls_serialize<W: std::io::Write>(
165            &self,
166            writer: &mut W,
167        ) -> Result<usize, tls_codec::Error> {
168            let mut written = 0;
169            written += self.inner.tls_serialize(writer)?;
170            written += self.auth.tls_serialize(writer)?;
171            writer.write_all(&vec![0u8; self.padding_len][..])?;
172            written += self.padding_len;
173            Ok(written)
174        }
175    }
176
177    impl SemiPrivateMessageContent {
178        pub fn tls_deserialize_with_content_type<R: std::io::Read>(
179            bytes: &mut R,
180            content_type: ContentType,
181        ) -> Result<Self, tls_codec::Error> {
182            use tls_codec::Deserialize as _;
183
184            let inner = match content_type {
185                ContentType::Proposal => ContentTypeInner::Proposal {
186                    proposal: Proposal::tls_deserialize(bytes)?,
187                },
188                ContentType::Commit => ContentTypeInner::Commit {
189                    commit: Commit::tls_deserialize(bytes)?,
190                },
191                _ => {
192                    return Err(tls_codec::Error::DecodingError(format!(
193                        "Tried to deserialize a {content_type}, which is invalid for a SemiPrivateMessage"
194                    )));
195                }
196            };
197            let auth =
198                FramedContentAuthData::tls_deserialize_with_content_type(bytes, content_type)?;
199
200            let padding_len = PrivateMessageContent::consume_padding(bytes)?;
201
202            Ok(Self {
203                inner,
204                auth,
205                padding_len,
206            })
207        }
208    }
209
210    #[derive(
211        Debug,
212        Clone,
213        PartialEq,
214        Eq,
215        tls_codec::TlsSerialize,
216        tls_codec::TlsDeserialize,
217        tls_codec::TlsSize,
218    )]
219    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
220    pub struct SemiPrivateMessage {
221        pub group_id: GroupId,
222        pub epoch: Epoch,
223        pub content_type: ContentType,
224        pub authenticated_data: SensitiveBytes,
225        pub partial_context_hash: SensitiveBytes,
226        pub keys_for_external_receivers: Vec<KeyForExternalReceiver>,
227        pub framed_content_tbs_hash: SensitiveBytes,
228        pub encrypted_sender_data: SensitiveBytes,
229        pub ciphertext: SensitiveBytes,
230    }
231}