1use openmls_traits::{crypto::OpenMlsCrypto, signatures::Signer, types::Ciphersuite};
2use tls_codec::*;
3
4use crate::{
5 binary_tree::LeafNodeIndex,
6 extensions::SenderExtensionIndex,
7 framing::{
8 mls_content::{AuthenticatedContentTbm, FramedContentBody, FramedContentTbs},
9 mls_content_in::FramedContentBodyIn,
10 MlsMessageIn, MlsMessageOut, PrivateMessage, PrivateMessageIn, PublicMessage,
11 PublicMessageIn, Sender, WireFormat,
12 },
13 group::GroupContext,
14 messages::{ConfirmationTag, GroupSecrets, PathSecret, Welcome},
15 prelude_test::signable::Signable,
16 schedule::{ConfirmationKey, JoinerSecret, MembershipKey},
17 test_utils::frankenstein::{FrankenHpkeCiphertext, FrankenPreSharedKeyId},
18};
19
20use super::{
21 commit::FrankenCommit,
22 compute_membership_tag,
23 group_info::{FrankenGroupContext, FrankenGroupInfo},
24 sign_with_label, FrankenKeyPackage, FrankenProposal,
25};
26
27#[derive(
28 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
29)]
30pub struct FrankenMlsMessage {
31 pub version: u16,
32 pub body: FrankenMlsMessageBody,
33}
34
35#[allow(clippy::large_enum_variant)]
36#[derive(
37 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
38)]
39#[repr(u16)]
40pub enum FrankenMlsMessageBody {
41 #[tls_codec(discriminant = 1)]
42 PublicMessage(FrankenPublicMessage),
43 #[tls_codec(discriminant = 2)]
44 PrivateMessage(FrankenPrivateMessage),
45 #[tls_codec(discriminant = 3)]
46 Welcome(FrankenWelcome),
47 #[tls_codec(discriminant = 4)]
48 GroupInfo(FrankenGroupInfo),
49 #[tls_codec(discriminant = 5)]
50 KeyPackage(FrankenKeyPackage),
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct FrankenPublicMessage {
55 pub content: FrankenFramedContent,
56 pub auth: FrankenFramedContentAuthData,
57 pub membership_tag: Option<VLBytes>,
58}
59
60impl tls_codec::Size for FrankenPublicMessage {
61 fn tls_serialized_len(&self) -> usize {
62 let tag_len = self
63 .membership_tag
64 .as_ref()
65 .map_or(0, |tag| tag.tls_serialized_len());
66
67 self.content.tls_serialized_len() + self.auth.tls_serialized_len() + tag_len
68 }
69}
70
71impl Deserialize for FrankenPublicMessage {
72 fn tls_deserialize<R: std::io::prelude::Read>(bytes: &mut R) -> Result<Self, Error>
73 where
74 Self: Sized,
75 {
76 let content = FrankenFramedContent::tls_deserialize(bytes)?;
77 let auth = if matches!(content.body, FrankenFramedContentBody::Commit(_)) {
78 FrankenFramedContentAuthData::tls_deserialize_with_tag(bytes)?
79 } else {
80 FrankenFramedContentAuthData::tls_deserialize_without_tag(bytes)?
81 };
82
83 let membership_tag = if matches!(content.sender, FrankenSender::Member(_)) {
84 Some(VLBytes::tls_deserialize(bytes)?)
85 } else {
86 None
87 };
88
89 Ok(Self {
90 content,
91 auth,
92 membership_tag,
93 })
94 }
95}
96
97impl DeserializeBytes for FrankenPublicMessage {
98 fn tls_deserialize_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error>
99 where
100 Self: Sized,
101 {
102 let (content, bytes) = FrankenFramedContent::tls_deserialize_bytes(bytes)?;
103 let (auth, bytes) = match content.body {
104 FrankenFramedContentBody::Commit(_) => {
105 FrankenFramedContentAuthData::tls_deserialize_bytes_with_tag(bytes)
106 }
107 _ => FrankenFramedContentAuthData::tls_deserialize_bytes_without_tag(bytes),
108 }?;
109 let (membership_tag, bytes) = match content.sender {
110 FrankenSender::Member(_) => {
111 let (tag, bytes) = VLBytes::tls_deserialize_bytes(bytes)?;
112 (Some(tag), bytes)
113 }
114 _ => (None, bytes),
115 };
116
117 Ok((
118 Self {
119 content,
120 auth,
121 membership_tag,
122 },
123 bytes,
124 ))
125 }
126}
127
128impl Serialize for FrankenPublicMessage {
129 fn tls_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
130 let mut written = 0;
131 written += self.content.tls_serialize(writer)?;
132 written += self.auth.tls_serialize(writer)?;
133 if let Some(tag) = &self.membership_tag {
134 written += tag.tls_serialize(writer)?;
135 }
136
137 Ok(written)
138 }
139}
140
141impl FrankenPublicMessage {
142 pub(crate) fn auth(
152 provider: &impl crate::storage::OpenMlsProvider,
153 ciphersuite: openmls_traits::types::Ciphersuite,
154 signer: &impl Signer,
155 content: FrankenFramedContent,
156 group_context: Option<&FrankenGroupContext>,
157 membership_key: Option<&[u8]>,
158 confirmation_tag: Option<VLBytes>,
159 ) -> Self {
160 let version = 1; let wire_format = 1; let franken_tbs = FrankenFramedContentTbs {
164 version,
165 wire_format,
166 content: &content,
167 group_context,
168 };
169
170 let auth = FrankenFramedContentAuthData::build(
171 signer,
172 version,
173 wire_format,
174 &content,
175 group_context,
176 confirmation_tag,
177 );
178
179 let tbm = FrankenAuthenticatedContentTbm {
180 content_tbs: franken_tbs,
181 auth: auth.clone(),
182 };
183
184 let membership_tag = membership_key.map(|membership_key| {
185 compute_membership_tag(provider.crypto(), ciphersuite, membership_key, &tbm)
186 });
187
188 FrankenPublicMessage {
189 content,
190 auth,
191 membership_tag,
192 }
193 }
194}
195
196#[derive(
197 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
198)]
199pub struct FrankenFramedContent {
200 pub group_id: VLBytes,
201 pub epoch: u64,
202 pub sender: FrankenSender,
203 pub authenticated_data: VLBytes,
204 pub body: FrankenFramedContentBody,
205}
206
207#[derive(
208 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
209)]
210#[repr(u8)]
211pub enum FrankenSender {
212 #[tls_codec(discriminant = 1)]
213 Member(u32),
214 External(u32),
215 NewMemberProposal,
216 NewMemberCommit,
217}
218
219#[derive(
220 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
221)]
222#[repr(u8)]
223pub enum FrankenFramedContentBody {
224 #[tls_codec(discriminant = 1)]
225 Application(VLBytes),
226 #[tls_codec(discriminant = 2)]
227 Proposal(FrankenProposal),
228 #[tls_codec(discriminant = 3)]
229 Commit(FrankenCommit),
230}
231
232#[derive(
233 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
234)]
235pub struct FrankenPrivateMessage {
236 pub group_id: VLBytes,
237 pub epoch: VLBytes,
238 pub content_type: FrankenContentType,
239 pub authenticated_data: VLBytes,
240 pub encrypted_sender_data: VLBytes,
241 pub ciphertext: VLBytes,
242}
243
244#[derive(
245 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
246)]
247pub struct FrankenWelcome {
248 pub cipher_suite: u16,
249 pub secrets: Vec<FrankenEncryptedGroupSecrets>,
250 pub encrypted_group_info: VLBytes,
251}
252
253#[derive(Debug, Clone, PartialEq, Eq)]
254pub struct FrankenFramedContentAuthData {
255 pub signature: VLBytes,
256 pub confirmation_tag: Option<VLBytes>,
257}
258
259impl FrankenFramedContentAuthData {
260 pub fn tls_deserialize_with_tag<R: std::io::Read>(
261 bytes: &mut R,
262 ) -> Result<Self, tls_codec::Error> {
263 let signature = VLBytes::tls_deserialize(bytes)?;
264 let confirmation_tag = VLBytes::tls_deserialize(bytes)?;
265
266 Ok(Self {
267 signature,
268 confirmation_tag: Some(confirmation_tag),
269 })
270 }
271
272 pub fn tls_deserialize_bytes_with_tag(bytes: &[u8]) -> Result<(Self, &[u8]), tls_codec::Error> {
273 let (signature, bytes) = VLBytes::tls_deserialize_bytes(bytes)?;
274 let (confirmation_tag, bytes) = VLBytes::tls_deserialize_bytes(bytes)?;
275
276 Ok((
277 Self {
278 signature,
279 confirmation_tag: Some(confirmation_tag),
280 },
281 bytes,
282 ))
283 }
284
285 pub fn tls_deserialize_without_tag<R: std::io::Read>(
286 bytes: &mut R,
287 ) -> Result<Self, tls_codec::Error> {
288 let signature = VLBytes::tls_deserialize(bytes)?;
289
290 Ok(Self {
291 signature,
292 confirmation_tag: None,
293 })
294 }
295
296 pub fn tls_deserialize_bytes_without_tag(
297 bytes: &[u8],
298 ) -> Result<(Self, &[u8]), tls_codec::Error> {
299 let (signature, bytes) = VLBytes::tls_deserialize_bytes(bytes)?;
300
301 Ok((
302 Self {
303 signature,
304 confirmation_tag: None,
305 },
306 bytes,
307 ))
308 }
309}
310
311impl tls_codec::Size for FrankenFramedContentAuthData {
312 fn tls_serialized_len(&self) -> usize {
313 if let Some(tag) = &self.confirmation_tag {
314 self.signature.tls_serialized_len() + tag.tls_serialized_len()
315 } else {
316 self.signature.tls_serialized_len()
317 }
318 }
319}
320
321impl Serialize for FrankenFramedContentAuthData {
322 fn tls_serialize<W: std::io::prelude::Write>(&self, writer: &mut W) -> Result<usize, Error> {
323 let mut written = 0;
324 written += self.signature.tls_serialize(writer)?;
325 if let Some(confirmation_tag) = &self.confirmation_tag {
326 written += confirmation_tag.tls_serialize(writer)?;
327 }
328 Ok(written)
329 }
330}
331
332impl FrankenFramedContentAuthData {
333 pub fn build(
334 signer: &impl Signer,
335 version: u16,
336 wire_format: u16,
337 content: &FrankenFramedContent,
338 group_context: Option<&FrankenGroupContext>,
339 confirmation_tag: Option<VLBytes>,
340 ) -> Self {
341 let content_tbs = FrankenFramedContentTbs {
342 version,
343 wire_format,
344 content,
345 group_context,
346 };
347
348 let content_tbs_serialized = content_tbs.tls_serialize_detached().unwrap();
349
350 let signature =
351 sign_with_label(signer, b"FramedContentTBS", &content_tbs_serialized).into();
352
353 Self {
354 signature,
355 confirmation_tag,
356 }
357 }
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
361pub struct FrankenFramedContentTbs<'a> {
362 version: u16,
363 wire_format: u16,
364 content: &'a FrankenFramedContent,
365 group_context: Option<&'a FrankenGroupContext>,
366}
367
368impl tls_codec::Size for FrankenFramedContentTbs<'_> {
369 fn tls_serialized_len(&self) -> usize {
370 if let Some(ctx) = self.group_context {
371 4 + self.content.tls_serialized_len() + ctx.tls_serialized_len()
372 } else {
373 4 + self.content.tls_serialized_len()
374 }
375 }
376}
377
378impl Serialize for FrankenFramedContentTbs<'_> {
379 fn tls_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
380 writer.write_all(&self.version.to_be_bytes())?;
381 writer.write_all(&self.wire_format.to_be_bytes())?;
382
383 let mut written = 4; written += self.content.tls_serialize(writer)?;
385 if let Some(group_context) = &self.group_context {
386 written += group_context.tls_serialize(writer)?;
387 }
388
389 Ok(written)
390 }
391}
392
393#[derive(Debug, Clone, PartialEq, Eq, TlsSerialize, TlsSize)]
394pub struct FrankenAuthenticatedContentTbm<'a> {
395 content_tbs: FrankenFramedContentTbs<'a>,
396 auth: FrankenFramedContentAuthData,
397}
398
399#[derive(
400 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
401)]
402#[repr(u8)]
403pub enum FrankenContentType {
404 Application = 1,
405 Proposal = 2,
406 Commit = 3,
407}
408
409#[derive(
410 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
411)]
412pub struct FrankenGroupSecrets {
413 pub joiner_secret: FrankenJoinerSecret,
414 pub path_secret: Option<FrankenPathSecret>,
415 pub psks: Vec<FrankenPreSharedKeyId>,
416}
417
418pub type FrankenPathSecret = VLBytes;
419pub type FrankenJoinerSecret = VLBytes;
420
421impl From<FrankenPathSecret> for PathSecret {
422 fn from(value: VLBytes) -> Self {
423 Self::tls_deserialize(&mut value.tls_serialize_detached().unwrap().as_slice()).unwrap()
424 }
425}
426
427#[derive(
428 Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize,
429)]
430pub struct FrankenEncryptedGroupSecrets {
431 pub new_member: VLBytes,
432 pub encrypted_group_secrets: FrankenHpkeCiphertext,
433}
434
435impl From<MlsMessageOut> for FrankenMlsMessage {
436 fn from(ln: MlsMessageOut) -> Self {
437 FrankenMlsMessage::tls_deserialize(&mut ln.tls_serialize_detached().unwrap().as_slice())
438 .unwrap()
439 }
440}
441impl From<FrankenMlsMessage> for MlsMessageIn {
442 fn from(fln: FrankenMlsMessage) -> Self {
443 MlsMessageIn::tls_deserialize(&mut fln.tls_serialize_detached().unwrap().as_slice())
444 .unwrap()
445 }
446}
447
448impl From<FrankenMlsMessage> for MlsMessageOut {
449 fn from(fln: FrankenMlsMessage) -> Self {
450 MlsMessageIn::tls_deserialize(&mut fln.tls_serialize_detached().unwrap().as_slice())
451 .unwrap()
452 .into()
453 }
454}
455
456impl From<PublicMessage> for FrankenPublicMessage {
457 fn from(ln: PublicMessage) -> Self {
458 FrankenPublicMessage::tls_deserialize(&mut ln.tls_serialize_detached().unwrap().as_slice())
459 .unwrap()
460 }
461}
462
463impl From<FrankenPublicMessage> for PublicMessage {
464 fn from(fln: FrankenPublicMessage) -> Self {
465 PublicMessageIn::tls_deserialize(&mut fln.tls_serialize_detached().unwrap().as_slice())
466 .unwrap()
467 .into()
468 }
469}
470
471impl From<PrivateMessage> for FrankenPrivateMessage {
472 fn from(ln: PrivateMessage) -> Self {
473 FrankenPrivateMessage::tls_deserialize(&mut ln.tls_serialize_detached().unwrap().as_slice())
474 .unwrap()
475 }
476}
477
478impl From<FrankenPrivateMessage> for PrivateMessage {
479 fn from(fln: FrankenPrivateMessage) -> Self {
480 PrivateMessageIn::tls_deserialize(&mut fln.tls_serialize_detached().unwrap().as_slice())
481 .unwrap()
482 .into()
483 }
484}
485
486impl From<Welcome> for FrankenWelcome {
487 fn from(ln: Welcome) -> Self {
488 FrankenWelcome::tls_deserialize(&mut ln.tls_serialize_detached().unwrap().as_slice())
489 .unwrap()
490 }
491}
492
493impl From<FrankenWelcome> for Welcome {
494 fn from(fln: FrankenWelcome) -> Self {
495 Welcome::tls_deserialize(&mut fln.tls_serialize_detached().unwrap().as_slice()).unwrap()
496 }
497}
498
499impl From<VLBytes> for JoinerSecret {
500 fn from(fln: VLBytes) -> Self {
501 JoinerSecret::tls_deserialize(&mut fln.tls_serialize_detached().unwrap().as_slice())
502 .unwrap()
503 }
504}
505
506impl From<FrankenGroupSecrets> for GroupSecrets {
507 fn from(ln: FrankenGroupSecrets) -> Self {
508 GroupSecrets::tls_deserialize(&mut ln.tls_serialize_detached().unwrap().as_slice()).unwrap()
509 }
510}
511
512impl From<FrankenFramedContentBody> for FramedContentBodyIn {
513 fn from(value: FrankenFramedContentBody) -> Self {
514 FramedContentBodyIn::tls_deserialize(
515 &mut value.tls_serialize_detached().unwrap().as_slice(),
516 )
517 .unwrap()
518 }
519}
520
521impl From<FrankenFramedContentBody> for FramedContentBody {
522 fn from(value: FrankenFramedContentBody) -> Self {
523 FramedContentBodyIn::from(value).into()
524 }
525}
526
527impl From<Sender> for FrankenSender {
528 fn from(value: Sender) -> Self {
529 match value {
530 Sender::Member(i) => FrankenSender::Member(i.u32()),
531 Sender::External(i) => FrankenSender::External(i.index() as u32),
534 Sender::NewMemberProposal => FrankenSender::NewMemberProposal,
535 Sender::NewMemberCommit => FrankenSender::NewMemberCommit,
536 }
537 }
538}
539
540impl From<FrankenSender> for Sender {
541 fn from(value: FrankenSender) -> Self {
542 match value {
543 FrankenSender::Member(i) => Sender::Member(LeafNodeIndex::new(i)),
544 FrankenSender::External(i) => Sender::External(SenderExtensionIndex::new(i)),
545 FrankenSender::NewMemberProposal => Sender::NewMemberProposal,
546 FrankenSender::NewMemberCommit => Sender::NewMemberCommit,
547 }
548 }
549}