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; static_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; static_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#[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}