1use buffa::Message;
7use buffa::view::MessageView;
8use bytes::Bytes;
9use hmac::{Hmac, KeyInit, Mac};
10use rand::{CryptoRng, Rng};
11use sha2::Sha256;
12use std::ops::Range;
13use std::sync::OnceLock;
14use subtle::ConstantTimeEq;
15
16use crate::protocol::state::{PreKeyId, SignedPreKeyId};
17use crate::protocol::{IdentityKey, PrivateKey, PublicKey, Result, SignalProtocolError, Timestamp};
18
19fn get_or_try_init_bytes(
21 cache: &OnceLock<Box<[u8]>>,
22 init: impl FnOnce() -> Result<Box<[u8]>>,
23) -> Result<&[u8]> {
24 if let Some(val) = cache.get() {
25 return Ok(val);
26 }
27 let _ = cache.set(init()?);
28 Ok(cache.get().expect("just set"))
30}
31
32fn subslice_range(parent: &[u8], child: &[u8]) -> Option<Range<usize>> {
33 let start = (child.as_ptr() as usize).checked_sub(parent.as_ptr() as usize)?;
34 let end = start.checked_add(child.len())?;
35 (end <= parent.len()).then_some(start..end)
36}
37
38#[inline]
42fn bytes_from_slice(value: &[u8]) -> Bytes {
43 Bytes::from(Box::<[u8]>::from(value))
44}
45
46#[inline]
51fn bytes_into_boxed_slice(value: Bytes) -> Box<[u8]> {
52 Vec::<u8>::from(value).into_boxed_slice()
53}
54
55const PROTOBUF_TAG_WIRE_BITS: u32 = 3;
56const VERSION_NIBBLE_BITS: u32 = 4;
57const VERSION_NIBBLE_MASK: u8 = (1 << VERSION_NIBBLE_BITS) - 1;
58
59#[inline]
60fn wire_tag_value(field: u32, wire_type: buffa::encoding::WireType) -> u64 {
61 (u64::from(field) << PROTOBUF_TAG_WIRE_BITS) | wire_type as u64
62}
63
64#[inline]
65fn wire_tag_len(field: u32, wire_type: buffa::encoding::WireType) -> usize {
66 buffa::encoding::varint_len(wire_tag_value(field, wire_type))
67}
68
69#[inline]
70fn varint_field_len(field: u32, value: u64) -> usize {
71 wire_tag_len(field, buffa::encoding::WireType::Varint) + buffa::encoding::varint_len(value)
72}
73
74#[inline]
75fn bytes_field_len(field: u32, value_len: usize) -> usize {
76 wire_tag_len(field, buffa::encoding::WireType::LengthDelimited)
77 + buffa::encoding::varint_len(value_len as u64)
78 + value_len
79}
80
81#[inline]
82fn push_varint_field(field: u32, value: u64, output: &mut Vec<u8>) {
83 buffa::encoding::Tag::new(field, buffa::encoding::WireType::Varint).encode(output);
84 buffa::encoding::encode_varint(value, output);
85}
86
87#[inline]
89fn push_bytes_field(field: u32, value: &[u8], output: &mut Vec<u8>) -> Range<usize> {
90 buffa::encoding::Tag::new(field, buffa::encoding::WireType::LengthDelimited).encode(output);
91 buffa::encoding::encode_varint(value.len() as u64, output);
92 let start = output.len();
93 output.extend_from_slice(value);
94 start..output.len()
95}
96
97#[inline]
98fn encode_version_byte(message_version: u8, current_version: u8) -> u8 {
99 ((message_version & VERSION_NIBBLE_MASK) << VERSION_NIBBLE_BITS)
100 | (current_version & VERSION_NIBBLE_MASK)
101}
102
103#[inline]
104fn decode_message_version(version_byte: u8) -> u8 {
105 version_byte >> VERSION_NIBBLE_BITS
106}
107
108pub const CIPHERTEXT_MESSAGE_CURRENT_VERSION: u8 = 3;
111pub const SENDERKEY_MESSAGE_CURRENT_VERSION: u8 = 3;
112
113const MIN_SUPPORTED_VERSION: u8 = 3;
114const MAX_SUPPORTED_VERSION: u8 = 4;
115
116struct ParsedSignalMessage {
117 message_version: u8,
118 sender_ratchet_key: PublicKey,
119 counter: u32,
120 ciphertext_range: Range<usize>,
121}
122
123macro_rules! parse_signal_message {
128 ($value:expr) => {{
129 let value: &[u8] = $value;
130 if value.len() < SignalMessage::MAC_LENGTH + 1 {
131 return Err(SignalProtocolError::CiphertextMessageTooShort(value.len()));
132 }
133 let message_version = decode_message_version(value[0]);
134 if !(MIN_SUPPORTED_VERSION..=MAX_SUPPORTED_VERSION).contains(&message_version) {
135 return Err(SignalProtocolError::UnrecognizedCiphertextVersion(
136 message_version,
137 ));
138 }
139
140 let view = SignalMessage::decode_view_from(value)?;
141 let sender_ratchet_key = view
142 .ratchet_key
143 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
144 let sender_ratchet_key = PublicKey::deserialize(sender_ratchet_key)?;
145 let counter = view
146 .counter
147 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
148 let ciphertext_range = SignalMessage::ciphertext_range_from(value, &view)?;
149
150 ParsedSignalMessage {
151 message_version,
152 sender_ratchet_key,
153 counter,
154 ciphertext_range,
155 }
156 }};
157}
158
159struct ParsedPreKeySignalMessage {
160 message_version: u8,
161 registration_id: u32,
162 pre_key_id: Option<PreKeyId>,
163 signed_pre_key_id: SignedPreKeyId,
164 base_key: PublicKey,
165 identity_key: IdentityKey,
166 message_range: Range<usize>,
167 message: ParsedSignalMessage,
168}
169
170macro_rules! parse_pre_key_signal_message {
173 ($value:expr) => {{
174 let value: &[u8] = $value;
175 if value.is_empty() {
176 return Err(SignalProtocolError::CiphertextMessageTooShort(value.len()));
177 }
178
179 let message_version = decode_message_version(value[0]);
180 if !(MIN_SUPPORTED_VERSION..=MAX_SUPPORTED_VERSION).contains(&message_version) {
181 return Err(SignalProtocolError::UnrecognizedCiphertextVersion(
182 message_version,
183 ));
184 }
185
186 let view = waproto::whatsapp::PreKeySignalMessageView::decode_view(&value[1..])
187 .map_err(|_| SignalProtocolError::InvalidProtobufEncoding)?;
188 let base_key = view
189 .base_key
190 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
191 let identity_key = view
192 .identity_key
193 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
194 let message_bytes = view
195 .message
196 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
197 let signed_pre_key_id = view
198 .signed_pre_key_id
199 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
200 let message_range = subslice_range(value, message_bytes)
201 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
202
203 ParsedPreKeySignalMessage {
204 message_version,
205 registration_id: view.registration_id.unwrap_or_default(),
206 pre_key_id: view.pre_key_id.map(Into::into),
207 signed_pre_key_id: signed_pre_key_id.into(),
208 base_key: PublicKey::deserialize(base_key)?,
209 identity_key: IdentityKey::try_from(identity_key)?,
210 message_range,
211 message: parse_signal_message!(message_bytes),
212 }
213 }};
214}
215
216#[derive(Debug)]
217pub enum CiphertextMessage {
218 SignalMessage(SignalMessage),
219 PreKeySignalMessage(PreKeySignalMessage),
220 SenderKeyMessage(SenderKeyMessage),
221 PlaintextContent(PlaintextContent),
222}
223
224#[derive(Copy, Clone, Eq, PartialEq, Debug)]
225#[repr(u8)]
226pub enum CiphertextMessageType {
227 Whisper = 2,
228 PreKey = 3,
229 SenderKey = 7,
230 Plaintext = 8,
231}
232
233impl TryFrom<u8> for CiphertextMessageType {
234 type Error = crate::core::UnknownDiscriminant<u8>;
235
236 fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
237 match value {
238 2 => Ok(Self::Whisper),
239 3 => Ok(Self::PreKey),
240 7 => Ok(Self::SenderKey),
241 8 => Ok(Self::Plaintext),
242 _ => Err(crate::core::UnknownDiscriminant { value }),
243 }
244 }
245}
246
247impl CiphertextMessage {
248 pub fn message_type(&self) -> CiphertextMessageType {
249 match self {
250 CiphertextMessage::SignalMessage(_) => CiphertextMessageType::Whisper,
251 CiphertextMessage::PreKeySignalMessage(_) => CiphertextMessageType::PreKey,
252 CiphertextMessage::SenderKeyMessage(_) => CiphertextMessageType::SenderKey,
253 CiphertextMessage::PlaintextContent(_) => CiphertextMessageType::Plaintext,
254 }
255 }
256
257 pub fn serialize(&self) -> &[u8] {
258 match self {
259 CiphertextMessage::SignalMessage(x) => x.serialized(),
260 CiphertextMessage::PreKeySignalMessage(x) => x.serialized(),
261 CiphertextMessage::SenderKeyMessage(x) => x.serialized(),
262 CiphertextMessage::PlaintextContent(x) => x.serialized(),
263 }
264 }
265}
266
267#[derive(Debug, Clone)]
268struct SignalStorage {
269 serialized: Bytes,
272 ciphertext_range: Range<usize>,
273}
274
275impl SignalStorage {
276 #[inline]
277 fn serialized(&self) -> &[u8] {
278 self.serialized.as_ref()
279 }
280
281 #[inline]
282 fn ciphertext(&self) -> &[u8] {
283 &self.serialized[self.ciphertext_range.clone()]
284 }
285
286 #[inline]
287 fn into_boxed_slice(self) -> Box<[u8]> {
288 bytes_into_boxed_slice(self.serialized)
289 }
290}
291
292#[derive(Debug, Clone)]
293pub struct SignalMessage {
294 message_version: u8,
295 sender_ratchet_key: PublicKey,
296 counter: u32,
297 storage: SignalStorage,
298}
299
300impl SignalMessage {
301 const MAC_LENGTH: usize = 8;
302
303 #[allow(clippy::too_many_arguments, clippy::disallowed_methods)]
306 pub fn new(
307 message_version: u8,
308 mac_key: &[u8],
309 sender_ratchet_key: PublicKey,
310 counter: u32,
311 previous_counter: u32,
312 ciphertext: &[u8],
313 sender_identity_key: &IdentityKey,
314 receiver_identity_key: &IdentityKey,
315 ) -> Result<Self> {
316 use waproto::tags::signal_message as tags;
317
318 let ratchet_key = sender_ratchet_key.serialize();
323 let proto_len = bytes_field_len(tags::RATCHET_KEY, ratchet_key.len())
324 + varint_field_len(tags::COUNTER, u64::from(counter))
325 + varint_field_len(tags::PREVIOUS_COUNTER, u64::from(previous_counter))
326 + bytes_field_len(tags::CIPHERTEXT, ciphertext.len());
327 let mut serialized = Vec::with_capacity(1 + proto_len + Self::MAC_LENGTH);
328 serialized.push(encode_version_byte(
329 message_version,
330 CIPHERTEXT_MESSAGE_CURRENT_VERSION,
331 ));
332 push_bytes_field(tags::RATCHET_KEY, &ratchet_key, &mut serialized);
333 push_varint_field(tags::COUNTER, u64::from(counter), &mut serialized);
334 push_varint_field(
335 tags::PREVIOUS_COUNTER,
336 u64::from(previous_counter),
337 &mut serialized,
338 );
339 let ciphertext_range = push_bytes_field(tags::CIPHERTEXT, ciphertext, &mut serialized);
340 debug_assert_eq!(serialized.len(), 1 + proto_len);
341
342 let mac = Self::compute_mac(
343 sender_identity_key,
344 receiver_identity_key,
345 mac_key,
346 &serialized,
347 )?;
348 serialized.extend_from_slice(&mac);
349 Ok(Self {
350 message_version,
351 sender_ratchet_key,
352 counter,
353 storage: SignalStorage {
354 serialized: Bytes::from(serialized),
355 ciphertext_range,
356 },
357 })
358 }
359
360 #[inline]
361 pub fn message_version(&self) -> u8 {
362 self.message_version
363 }
364
365 #[inline]
366 pub fn sender_ratchet_key(&self) -> &PublicKey {
367 &self.sender_ratchet_key
368 }
369
370 #[inline]
371 pub fn counter(&self) -> u32 {
372 self.counter
373 }
374
375 #[inline]
376 pub fn serialized(&self) -> &[u8] {
377 self.storage.serialized()
378 }
379
380 #[inline]
381 pub fn into_serialized(self) -> Box<[u8]> {
382 self.storage.into_boxed_slice()
383 }
384
385 #[inline]
386 pub fn body(&self) -> Result<&[u8]> {
387 Ok(self.storage.ciphertext())
388 }
389
390 #[inline]
391 fn decode_view_from(serialized: &[u8]) -> Result<waproto::whatsapp::SignalMessageView<'_>> {
392 let proto_bytes = &serialized[1..serialized.len() - Self::MAC_LENGTH];
393 waproto::whatsapp::SignalMessageView::decode_view(proto_bytes)
394 .map_err(|_| SignalProtocolError::InvalidProtobufEncoding)
395 }
396
397 fn ciphertext_range_from(
398 serialized: &[u8],
399 view: &waproto::whatsapp::SignalMessageView<'_>,
400 ) -> Result<Range<usize>> {
401 let ciphertext = view
402 .ciphertext
403 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
404 subslice_range(serialized, ciphertext).ok_or(SignalProtocolError::InvalidProtobufEncoding)
405 }
406
407 fn rebase_serialized(&mut self, serialized: Bytes) {
408 let ciphertext_range = self.storage.ciphertext_range.clone();
409 debug_assert_eq!(self.storage.serialized(), serialized.as_ref());
410 self.storage = SignalStorage {
411 serialized,
412 ciphertext_range,
413 };
414 }
415
416 #[inline]
417 fn from_parsed(serialized: Bytes, parsed: ParsedSignalMessage) -> Self {
418 Self {
419 message_version: parsed.message_version,
420 sender_ratchet_key: parsed.sender_ratchet_key,
421 counter: parsed.counter,
422 storage: SignalStorage {
423 serialized,
424 ciphertext_range: parsed.ciphertext_range,
425 },
426 }
427 }
428
429 #[inline]
430 fn try_from_borrowed(value: &[u8]) -> Result<Self> {
431 let parsed = parse_signal_message!(value);
435 Ok(Self::from_parsed(bytes_from_slice(value), parsed))
436 }
437
438 #[inline]
439 fn try_from_shared(serialized: Bytes) -> Result<Self> {
440 let parsed = parse_signal_message!(&serialized);
441 Ok(Self::from_parsed(serialized, parsed))
442 }
443
444 #[inline]
447 pub(super) fn try_into_ciphertext_vec(self) -> std::result::Result<Vec<u8>, Self> {
448 if !self.storage.serialized.is_unique() {
449 return Err(self);
450 }
451
452 let SignalStorage {
453 serialized,
454 ciphertext_range,
455 } = self.storage;
456 let ciphertext = serialized.slice(ciphertext_range);
457 drop(serialized);
458 debug_assert!(ciphertext.is_unique());
459 Ok(Vec::from(ciphertext))
460 }
461
462 pub fn verify_mac(
463 &self,
464 sender_identity_key: &IdentityKey,
465 receiver_identity_key: &IdentityKey,
466 mac_key: &[u8],
467 ) -> Result<bool> {
468 let serialized = self.storage.serialized();
469 let our_mac = &Self::compute_mac(
470 sender_identity_key,
471 receiver_identity_key,
472 mac_key,
473 &serialized[..serialized.len() - Self::MAC_LENGTH],
474 )?;
475 let their_mac = &serialized[serialized.len() - Self::MAC_LENGTH..];
476 let result: bool = our_mac.ct_eq(their_mac).into();
477 if !result {
478 log::warn!(
480 "Bad Mac! Their Mac: {} Our Mac: {}",
481 hex::encode(their_mac),
482 hex::encode(our_mac)
483 );
484 }
485 Ok(result)
486 }
487
488 fn compute_mac(
489 sender_identity_key: &IdentityKey,
490 receiver_identity_key: &IdentityKey,
491 mac_key: &[u8],
492 message: &[u8],
493 ) -> Result<[u8; Self::MAC_LENGTH]> {
494 if mac_key.len() != 32 {
495 return Err(SignalProtocolError::InvalidMacKeyLength(mac_key.len()));
496 }
497 let mut mac = Hmac::<Sha256>::new_from_slice(mac_key)
498 .expect("HMAC-SHA256 should accept any size key");
499
500 mac.update(sender_identity_key.public_key().serialize().as_ref());
501 mac.update(receiver_identity_key.public_key().serialize().as_ref());
502 mac.update(message);
503 let mut result = [0u8; Self::MAC_LENGTH];
504 result.copy_from_slice(&mac.finalize().into_bytes()[..Self::MAC_LENGTH]);
505 Ok(result)
506 }
507}
508
509impl AsRef<[u8]> for SignalMessage {
510 fn as_ref(&self) -> &[u8] {
511 self.storage.serialized()
512 }
513}
514
515impl TryFrom<&[u8]> for SignalMessage {
516 type Error = SignalProtocolError;
517
518 fn try_from(value: &[u8]) -> Result<Self> {
519 Self::try_from_borrowed(value)
520 }
521}
522
523impl TryFrom<Bytes> for SignalMessage {
524 type Error = SignalProtocolError;
525
526 fn try_from(value: Bytes) -> Result<Self> {
527 Self::try_from_shared(value)
528 }
529}
530
531#[derive(Debug, Clone)]
532pub struct PreKeySignalMessage {
533 message_version: u8,
534 registration_id: u32,
535 pre_key_id: Option<PreKeyId>,
536 signed_pre_key_id: SignedPreKeyId,
537 base_key: PublicKey,
538 identity_key: IdentityKey,
539 message: SignalMessage,
540 serialized: Bytes,
541}
542
543impl PreKeySignalMessage {
544 #[allow(clippy::disallowed_methods)]
545 pub fn new(
546 message_version: u8,
547 registration_id: u32,
548 pre_key_id: Option<PreKeyId>,
549 signed_pre_key_id: SignedPreKeyId,
550 base_key: PublicKey,
551 identity_key: IdentityKey,
552 mut message: SignalMessage,
553 ) -> Result<Self> {
554 use waproto::tags::pre_key_signal_message as tags;
555
556 let pre_key_id_value = pre_key_id.map(Into::<u32>::into);
557 let signed_pre_key_id_value: u32 = signed_pre_key_id.into();
558 let base_key_bytes = base_key.serialize();
559 let identity_key_bytes = identity_key.serialize();
560 let nested_message = message.serialized();
561
562 let proto_len = pre_key_id_value
567 .map(|id| varint_field_len(tags::PRE_KEY_ID, u64::from(id)))
568 .unwrap_or_default()
569 + bytes_field_len(tags::BASE_KEY, base_key_bytes.len())
570 + bytes_field_len(tags::IDENTITY_KEY, identity_key_bytes.len())
571 + bytes_field_len(tags::MESSAGE, nested_message.len())
572 + varint_field_len(tags::REGISTRATION_ID, u64::from(registration_id))
573 + varint_field_len(tags::SIGNED_PRE_KEY_ID, u64::from(signed_pre_key_id_value));
574 let mut serialized = Vec::with_capacity(1 + proto_len);
575 serialized.push(encode_version_byte(
576 message_version,
577 CIPHERTEXT_MESSAGE_CURRENT_VERSION,
578 ));
579 if let Some(id) = pre_key_id_value {
580 push_varint_field(tags::PRE_KEY_ID, u64::from(id), &mut serialized);
581 }
582 push_bytes_field(tags::BASE_KEY, &base_key_bytes, &mut serialized);
583 push_bytes_field(tags::IDENTITY_KEY, &identity_key_bytes, &mut serialized);
584 let message_range = push_bytes_field(tags::MESSAGE, nested_message, &mut serialized);
585 push_varint_field(
586 tags::REGISTRATION_ID,
587 u64::from(registration_id),
588 &mut serialized,
589 );
590 push_varint_field(
591 tags::SIGNED_PRE_KEY_ID,
592 u64::from(signed_pre_key_id_value),
593 &mut serialized,
594 );
595 debug_assert_eq!(serialized.len(), 1 + proto_len);
596
597 let serialized = Bytes::from(serialized);
598 message.rebase_serialized(serialized.slice(message_range));
603 Ok(Self {
604 message_version,
605 registration_id,
606 pre_key_id,
607 signed_pre_key_id,
608 base_key,
609 identity_key,
610 message,
611 serialized,
612 })
613 }
614
615 #[inline]
616 fn from_parsed(serialized: Bytes, parsed: ParsedPreKeySignalMessage) -> Self {
617 let ParsedPreKeySignalMessage {
618 message_version,
619 registration_id,
620 pre_key_id,
621 signed_pre_key_id,
622 base_key,
623 identity_key,
624 message_range,
625 message,
626 } = parsed;
627 let message = SignalMessage::from_parsed(serialized.slice(message_range), message);
628
629 Self {
630 message_version,
631 registration_id,
632 pre_key_id,
633 signed_pre_key_id,
634 base_key,
635 identity_key,
636 message,
637 serialized,
638 }
639 }
640
641 #[inline]
642 pub fn message_version(&self) -> u8 {
643 self.message_version
644 }
645
646 #[inline]
647 pub fn registration_id(&self) -> u32 {
648 self.registration_id
649 }
650
651 #[inline]
652 pub fn pre_key_id(&self) -> Option<PreKeyId> {
653 self.pre_key_id
654 }
655
656 #[inline]
657 pub fn signed_pre_key_id(&self) -> SignedPreKeyId {
658 self.signed_pre_key_id
659 }
660
661 #[inline]
662 pub fn base_key(&self) -> &PublicKey {
663 &self.base_key
664 }
665
666 #[inline]
667 pub fn identity_key(&self) -> &IdentityKey {
668 &self.identity_key
669 }
670
671 #[inline]
672 pub fn message(&self) -> &SignalMessage {
673 &self.message
674 }
675
676 #[inline]
677 pub fn serialized(&self) -> &[u8] {
678 self.serialized.as_ref()
679 }
680
681 #[inline]
685 pub(super) fn into_message(self) -> SignalMessage {
686 let Self {
687 message,
688 serialized,
689 ..
690 } = self;
691 drop(serialized);
692 message
693 }
694
695 #[inline]
696 pub fn into_serialized(self) -> Box<[u8]> {
697 let Self {
698 message,
699 serialized,
700 ..
701 } = self;
702 drop(message);
705 bytes_into_boxed_slice(serialized)
706 }
707}
708
709impl AsRef<[u8]> for PreKeySignalMessage {
710 fn as_ref(&self) -> &[u8] {
711 self.serialized.as_ref()
712 }
713}
714
715impl TryFrom<&[u8]> for PreKeySignalMessage {
716 type Error = SignalProtocolError;
717
718 fn try_from(value: &[u8]) -> Result<Self> {
719 let parsed = parse_pre_key_signal_message!(value);
720 Ok(Self::from_parsed(bytes_from_slice(value), parsed))
721 }
722}
723
724impl TryFrom<Bytes> for PreKeySignalMessage {
725 type Error = SignalProtocolError;
726
727 fn try_from(value: Bytes) -> Result<Self> {
728 let parsed = parse_pre_key_signal_message!(&value);
729 Ok(Self::from_parsed(value, parsed))
730 }
731}
732
733#[derive(Debug)]
734pub struct SenderKeyMessage {
735 message_version: u8,
736 chain_id: u32,
737 iteration: u32,
738 serialized: Box<[u8]>,
739 ciphertext_cache: OnceLock<Box<[u8]>>,
741}
742
743impl Clone for SenderKeyMessage {
744 fn clone(&self) -> Self {
745 let ciphertext_cache = OnceLock::new();
746 if let Some(ciphertext) = self.ciphertext_cache.get() {
747 let _ = ciphertext_cache.set(ciphertext.clone());
748 }
749
750 Self {
751 message_version: self.message_version,
752 chain_id: self.chain_id,
753 iteration: self.iteration,
754 serialized: self.serialized.clone(),
755 ciphertext_cache,
756 }
757 }
758}
759
760impl SenderKeyMessage {
761 const SIGNATURE_LEN: usize = 64;
762
763 #[allow(clippy::disallowed_methods)]
764 pub fn new<R: CryptoRng + Rng>(
765 message_version: u8,
766 chain_id: u32,
767 iteration: u32,
768 ciphertext: Box<[u8]>,
769 csprng: &mut R,
770 signature_key: &PrivateKey,
771 ) -> Result<Self> {
772 let proto_message = waproto::whatsapp::SenderKeyMessage {
773 id: Some(chain_id),
774 iteration: Some(iteration),
775 ciphertext: Some(ciphertext.into_vec()),
776 };
777
778 let shifted_version = (message_version << 4) | 3u8;
781 let mut size_cache = buffa::SizeCache::new();
782 let proto_len = proto_message.compute_size(&mut size_cache) as usize;
783 let mut serialized = Vec::with_capacity(1 + proto_len + Self::SIGNATURE_LEN);
784 serialized.push(shifted_version);
785 proto_message.write_to(&mut size_cache, &mut serialized);
786
787 let signature = signature_key
789 .calculate_signature(&serialized, csprng)
790 .map_err(|_| SignalProtocolError::SignatureValidationFailed)?;
791 serialized.extend_from_slice(&signature);
792
793 Ok(Self {
794 message_version,
795 chain_id,
796 iteration,
797 serialized: serialized.into_boxed_slice(),
798 ciphertext_cache: OnceLock::new(),
799 })
800 }
801
802 pub fn verify_signature(&self, signature_key: &PublicKey) -> Result<bool> {
803 let valid = signature_key.verify_signature(
804 &self.serialized[..self.serialized.len() - Self::SIGNATURE_LEN],
805 &self.serialized[self.serialized.len() - Self::SIGNATURE_LEN..],
806 );
807
808 Ok(valid)
809 }
810
811 pub fn verify_signature_prepared(
815 &self,
816 signature_key: &crate::core::curve::PreparedVerifyingKey,
817 ) -> Result<bool> {
818 let valid = signature_key.verify_signature(
819 &self.serialized[..self.serialized.len() - Self::SIGNATURE_LEN],
820 &self.serialized[self.serialized.len() - Self::SIGNATURE_LEN..],
821 );
822
823 Ok(valid)
824 }
825
826 #[inline]
827 pub fn message_version(&self) -> u8 {
828 self.message_version
829 }
830
831 #[inline]
832 pub fn chain_id(&self) -> u32 {
833 self.chain_id
834 }
835
836 #[inline]
837 pub fn iteration(&self) -> u32 {
838 self.iteration
839 }
840
841 pub fn ciphertext(&self) -> Result<&[u8]> {
850 get_or_try_init_bytes(&self.ciphertext_cache, || self.decode_ciphertext())
851 }
852
853 fn decode_ciphertext(&self) -> Result<Box<[u8]>> {
854 let proto_bytes = &self.serialized[1..self.serialized.len() - Self::SIGNATURE_LEN];
856 let view = waproto::whatsapp::SenderKeyMessageView::decode_view(proto_bytes)
857 .map_err(|_| SignalProtocolError::InvalidProtobufEncoding)?;
858 match view.ciphertext {
859 Some(ciphertext) => Ok(Box::from(ciphertext)),
860 None => Err(SignalProtocolError::InvalidProtobufEncoding),
861 }
862 }
863
864 #[inline]
865 pub fn serialized(&self) -> &[u8] {
866 &self.serialized
867 }
868
869 #[inline]
870 pub fn into_serialized(self) -> Box<[u8]> {
871 self.serialized
872 }
873}
874
875impl AsRef<[u8]> for SenderKeyMessage {
876 fn as_ref(&self) -> &[u8] {
877 &self.serialized
878 }
879}
880
881impl TryFrom<&[u8]> for SenderKeyMessage {
882 type Error = SignalProtocolError;
883
884 fn try_from(value: &[u8]) -> Result<Self> {
885 if value.len() < 1 + Self::SIGNATURE_LEN {
886 return Err(SignalProtocolError::CiphertextMessageTooShort(value.len()));
887 }
888 let message_version = value[0] >> 4;
889 if message_version < SENDERKEY_MESSAGE_CURRENT_VERSION {
890 return Err(SignalProtocolError::LegacyCiphertextVersion(
891 message_version,
892 ));
893 }
894 if message_version > SENDERKEY_MESSAGE_CURRENT_VERSION {
895 return Err(SignalProtocolError::UnrecognizedCiphertextVersion(
896 message_version,
897 ));
898 }
899 let view = waproto::whatsapp::SenderKeyMessageView::decode_view(
900 &value[1..value.len() - Self::SIGNATURE_LEN],
901 )
902 .map_err(|_| SignalProtocolError::InvalidProtobufEncoding)?;
903
904 let Some(chain_id) = view.id else {
905 return Err(SignalProtocolError::InvalidProtobufEncoding);
906 };
907 let Some(iteration) = view.iteration else {
908 return Err(SignalProtocolError::InvalidProtobufEncoding);
909 };
910 let Some(ciphertext) = view.ciphertext else {
911 return Err(SignalProtocolError::InvalidProtobufEncoding);
912 };
913 let ciphertext: Box<[u8]> = Box::from(ciphertext);
914
915 let ciphertext_cache = OnceLock::new();
916 let _ = ciphertext_cache.set(ciphertext);
917
918 Ok(SenderKeyMessage {
919 message_version,
920 chain_id,
921 iteration,
922 serialized: Box::from(value),
923 ciphertext_cache,
924 })
925 }
926}
927
928#[derive(Debug, Clone)]
929pub struct SenderKeyDistributionMessage {
930 message_version: u8,
931 chain_id: u32,
932 iteration: u32,
933 chain_key: [u8; 32],
934 signing_key: PublicKey,
935 serialized: Box<[u8]>,
936}
937
938impl SenderKeyDistributionMessage {
939 #[allow(clippy::disallowed_methods)]
940 pub fn new(
941 message_version: u8,
942 chain_id: u32,
943 iteration: u32,
944 chain_key: [u8; 32],
945 signing_key: PublicKey,
946 ) -> Result<Self> {
947 let proto_message = waproto::whatsapp::SenderKeyDistributionMessage {
948 id: Some(chain_id),
949 iteration: Some(iteration),
950 chain_key: Some(chain_key.to_vec()),
951 signing_key: Some(signing_key.serialize().to_vec()),
952 };
953 let mut size_cache = buffa::SizeCache::new();
954 let message_len = proto_message.compute_size(&mut size_cache) as usize;
955 let mut serialized = Vec::with_capacity(1 + message_len);
956 serialized.push(((message_version & 0xF) << 4) | SENDERKEY_MESSAGE_CURRENT_VERSION);
957 proto_message.write_to(&mut size_cache, &mut serialized);
958
959 Ok(Self {
960 message_version,
961 chain_id,
962 iteration,
963 chain_key,
964 signing_key,
965 serialized: serialized.into_boxed_slice(),
966 })
967 }
968
969 #[inline]
970 pub fn message_version(&self) -> u8 {
971 self.message_version
972 }
973
974 #[inline]
975 pub fn chain_id(&self) -> u32 {
976 self.chain_id
977 }
978
979 #[inline]
980 pub fn iteration(&self) -> u32 {
981 self.iteration
982 }
983
984 #[inline]
985 pub fn chain_key(&self) -> &[u8; 32] {
986 &self.chain_key
987 }
988
989 #[inline]
990 pub fn signing_key(&self) -> &PublicKey {
991 &self.signing_key
992 }
993
994 #[inline]
995 pub fn serialized(&self) -> &[u8] {
996 &self.serialized
997 }
998
999 #[inline]
1000 pub fn into_serialized(self) -> Box<[u8]> {
1001 self.serialized
1002 }
1003}
1004
1005impl AsRef<[u8]> for SenderKeyDistributionMessage {
1006 fn as_ref(&self) -> &[u8] {
1007 &self.serialized
1008 }
1009}
1010
1011impl TryFrom<&[u8]> for SenderKeyDistributionMessage {
1012 type Error = SignalProtocolError;
1013
1014 fn try_from(value: &[u8]) -> Result<Self> {
1015 if value.len() < 1 + 32 + 32 {
1017 return Err(SignalProtocolError::CiphertextMessageTooShort(value.len()));
1018 }
1019
1020 let message_version = value[0] >> 4;
1021
1022 if message_version < SENDERKEY_MESSAGE_CURRENT_VERSION {
1023 return Err(SignalProtocolError::LegacyCiphertextVersion(
1024 message_version,
1025 ));
1026 }
1027 if message_version > SENDERKEY_MESSAGE_CURRENT_VERSION {
1028 return Err(SignalProtocolError::UnrecognizedCiphertextVersion(
1029 message_version,
1030 ));
1031 }
1032
1033 let view = waproto::whatsapp::SenderKeyDistributionMessageView::decode_view(&value[1..])
1034 .map_err(|_| SignalProtocolError::InvalidProtobufEncoding)?;
1035
1036 let chain_id = view
1037 .id
1038 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
1039 let iteration = view
1040 .iteration
1041 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
1042 let chain_key_bytes = view
1043 .chain_key
1044 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
1045 let signing_key = view
1046 .signing_key
1047 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
1048
1049 if chain_key_bytes.len() != 32 || signing_key.len() != 33 {
1050 return Err(SignalProtocolError::InvalidProtobufEncoding);
1051 }
1052
1053 let chain_key: [u8; 32] = chain_key_bytes
1054 .try_into()
1055 .map_err(|_| SignalProtocolError::InvalidProtobufEncoding)?;
1056 let signing_key = PublicKey::deserialize(signing_key)?;
1057
1058 Ok(SenderKeyDistributionMessage {
1059 message_version,
1060 chain_id,
1061 iteration,
1062 chain_key,
1063 signing_key,
1064 serialized: Box::from(value),
1065 })
1066 }
1067}
1068
1069#[derive(Debug, Clone)]
1070pub struct PlaintextContent {
1071 serialized: Box<[u8]>,
1072}
1073
1074impl PlaintextContent {
1075 const PLAINTEXT_CONTEXT_IDENTIFIER_BYTE: u8 = 0xC0;
1080
1081 #[inline]
1082 pub fn body(&self) -> &[u8] {
1083 &self.serialized[1..]
1084 }
1085
1086 #[inline]
1087 pub fn serialized(&self) -> &[u8] {
1088 &self.serialized
1089 }
1090}
1091
1092#[derive(Clone, PartialEq, Default)]
1093pub struct DecryptionErrorMessageProto {
1094 pub ratchet_key: Option<Vec<u8>>,
1095 pub timestamp: Option<u64>,
1096 pub device_id: Option<u32>,
1097}
1098
1099impl buffa::DefaultInstance for DecryptionErrorMessageProto {
1100 fn default_instance() -> &'static Self {
1101 static VALUE: buffa::__private::OnceBox<DecryptionErrorMessageProto> =
1102 buffa::__private::OnceBox::new();
1103 VALUE.get_or_init(|| Box::new(DecryptionErrorMessageProto::default()))
1104 }
1105}
1106
1107impl Message for DecryptionErrorMessageProto {
1108 fn compute_size(&self, _cache: &mut buffa::SizeCache) -> u32 {
1109 let mut size = 0u32;
1110 if let Some(ref v) = self.ratchet_key {
1111 size += 1 + buffa::types::bytes_encoded_len(v) as u32;
1112 }
1113 if let Some(v) = self.timestamp {
1114 size += 1 + buffa::types::uint64_encoded_len(v) as u32;
1115 }
1116 if let Some(v) = self.device_id {
1117 size += 1 + buffa::types::uint32_encoded_len(v) as u32;
1118 }
1119 size
1120 }
1121
1122 fn write_to(&self, _cache: &mut buffa::SizeCache, buf: &mut impl buffa::EncodeSink) {
1123 if let Some(ref v) = self.ratchet_key {
1124 buffa::encoding::Tag::new(1, buffa::encoding::WireType::LengthDelimited).encode(buf);
1125 buffa::types::encode_bytes(v, buf);
1126 }
1127 if let Some(v) = self.timestamp {
1128 buffa::encoding::Tag::new(2, buffa::encoding::WireType::Varint).encode(buf);
1129 buffa::types::encode_uint64(v, buf);
1130 }
1131 if let Some(v) = self.device_id {
1132 buffa::encoding::Tag::new(3, buffa::encoding::WireType::Varint).encode(buf);
1133 buffa::types::encode_uint32(v, buf);
1134 }
1135 }
1136
1137 fn merge_field(
1138 &mut self,
1139 tag: buffa::encoding::Tag,
1140 buf: &mut impl bytes::Buf,
1141 ctx: buffa::DecodeContext<'_>,
1142 ) -> core::result::Result<(), buffa::DecodeError> {
1143 use buffa::encoding::WireType;
1144 match tag.field_number() {
1147 1 if tag.wire_type() == WireType::LengthDelimited => {
1148 buffa::types::merge_bytes(self.ratchet_key.get_or_insert_with(Vec::new), buf)?;
1149 }
1150 2 if tag.wire_type() == WireType::Varint => {
1151 self.timestamp = Some(buffa::types::decode_uint64(buf)?);
1152 }
1153 3 if tag.wire_type() == WireType::Varint => {
1154 self.device_id = Some(buffa::types::decode_uint32(buf)?);
1155 }
1156 _ => {
1157 buffa::encoding::skip_field_depth(tag, buf, ctx.depth())?;
1161 }
1162 }
1163 Ok(())
1164 }
1165
1166 fn clear(&mut self) {
1167 self.ratchet_key = None;
1168 self.timestamp = None;
1169 self.device_id = None;
1170 }
1171}
1172
1173impl TryFrom<&[u8]> for PlaintextContent {
1174 type Error = SignalProtocolError;
1175
1176 fn try_from(value: &[u8]) -> Result<Self> {
1177 if value.is_empty() {
1178 return Err(SignalProtocolError::CiphertextMessageTooShort(0));
1179 }
1180 if value[0] != Self::PLAINTEXT_CONTEXT_IDENTIFIER_BYTE {
1181 return Err(SignalProtocolError::UnrecognizedMessageVersion(
1182 value[0] as u32,
1183 ));
1184 }
1185 Ok(Self {
1186 serialized: Box::from(value),
1187 })
1188 }
1189}
1190
1191#[derive(Debug, Clone)]
1192pub struct DecryptionErrorMessage {
1193 ratchet_key: Option<PublicKey>,
1194 timestamp: Timestamp,
1195 device_id: u32,
1196 serialized: Box<[u8]>,
1197}
1198
1199impl DecryptionErrorMessage {
1200 #[inline]
1201 pub fn timestamp(&self) -> Timestamp {
1202 self.timestamp
1203 }
1204
1205 #[inline]
1206 pub fn ratchet_key(&self) -> Option<&PublicKey> {
1207 self.ratchet_key.as_ref()
1208 }
1209
1210 #[inline]
1211 pub fn device_id(&self) -> u32 {
1212 self.device_id
1213 }
1214
1215 #[inline]
1216 pub fn serialized(&self) -> &[u8] {
1217 &self.serialized
1218 }
1219}
1220
1221impl TryFrom<&[u8]> for DecryptionErrorMessage {
1222 type Error = SignalProtocolError;
1223
1224 #[allow(clippy::disallowed_methods)]
1225 fn try_from(value: &[u8]) -> Result<Self> {
1226 let proto_structure = DecryptionErrorMessageProto::decode_from_slice(value)
1227 .map_err(|_| SignalProtocolError::InvalidProtobufEncoding)?;
1228 let timestamp = proto_structure
1229 .timestamp
1230 .map(Timestamp::from_epoch_millis)
1231 .ok_or(SignalProtocolError::InvalidProtobufEncoding)?;
1232 let ratchet_key = proto_structure
1233 .ratchet_key
1234 .map(|k| PublicKey::deserialize(&k))
1235 .transpose()?;
1236 let device_id = proto_structure.device_id.unwrap_or_default();
1237 Ok(Self {
1238 timestamp,
1239 ratchet_key,
1240 device_id,
1241 serialized: Box::from(value),
1242 })
1243 }
1244}
1245
1246#[cfg(test)]
1247#[allow(clippy::disallowed_methods)]
1248mod tests {
1249 use super::*;
1250
1251 const TEST_MESSAGE_VERSION: u8 = CIPHERTEXT_MESSAGE_CURRENT_VERSION;
1252 const TEST_MAC_KEY: [u8; 32] = [0xA5; 32];
1253 const TEST_RATCHET_SEED: u8 = 0x11;
1254 const TEST_SENDER_IDENTITY_SEED: u8 = 0x22;
1255 const TEST_RECEIVER_IDENTITY_SEED: u8 = 0x33;
1256 const TEST_BASE_KEY_SEED: u8 = 0x44;
1257
1258 fn public_key_from_seed(seed: u8) -> PublicKey {
1259 PrivateKey::deserialize(&[seed; 32])
1260 .expect("test private key")
1261 .public_key()
1262 .expect("test public key")
1263 }
1264
1265 fn test_signal_message(
1266 counter: u32,
1267 previous_counter: u32,
1268 ciphertext: &[u8],
1269 ) -> SignalMessage {
1270 SignalMessage::new(
1271 TEST_MESSAGE_VERSION,
1272 &TEST_MAC_KEY,
1273 public_key_from_seed(TEST_RATCHET_SEED),
1274 counter,
1275 previous_counter,
1276 ciphertext,
1277 &IdentityKey::new(public_key_from_seed(TEST_SENDER_IDENTITY_SEED)),
1278 &IdentityKey::new(public_key_from_seed(TEST_RECEIVER_IDENTITY_SEED)),
1279 )
1280 .expect("test SignalMessage")
1281 }
1282
1283 fn generated_signal_wire(counter: u32, previous_counter: u32, ciphertext: &[u8]) -> Vec<u8> {
1284 let sender_identity = IdentityKey::new(public_key_from_seed(TEST_SENDER_IDENTITY_SEED));
1285 let receiver_identity = IdentityKey::new(public_key_from_seed(TEST_RECEIVER_IDENTITY_SEED));
1286 let proto = waproto::whatsapp::SignalMessage {
1287 ratchet_key: Some(public_key_from_seed(TEST_RATCHET_SEED).serialize().to_vec()),
1288 counter: Some(counter),
1289 previous_counter: Some(previous_counter),
1290 ciphertext: Some(ciphertext.to_vec()),
1291 };
1292 let mut wire =
1293 Vec::with_capacity(1 + proto.encoded_len() as usize + SignalMessage::MAC_LENGTH);
1294 wire.push(encode_version_byte(
1295 TEST_MESSAGE_VERSION,
1296 CIPHERTEXT_MESSAGE_CURRENT_VERSION,
1297 ));
1298 wire.extend_from_slice(&proto.encode_to_vec());
1299 let mac =
1300 SignalMessage::compute_mac(&sender_identity, &receiver_identity, &TEST_MAC_KEY, &wire)
1301 .expect("test MAC");
1302 wire.extend_from_slice(&mac);
1303 wire
1304 }
1305
1306 fn generated_pre_key_wire(
1307 registration_id: u32,
1308 pre_key_id: Option<PreKeyId>,
1309 signed_pre_key_id: SignedPreKeyId,
1310 nested_message: &[u8],
1311 ) -> Vec<u8> {
1312 let proto = waproto::whatsapp::PreKeySignalMessage {
1313 registration_id: Some(registration_id),
1314 pre_key_id: pre_key_id.map(Into::into),
1315 signed_pre_key_id: Some(signed_pre_key_id.into()),
1316 base_key: Some(
1317 public_key_from_seed(TEST_BASE_KEY_SEED)
1318 .serialize()
1319 .to_vec(),
1320 ),
1321 identity_key: Some(
1322 public_key_from_seed(TEST_SENDER_IDENTITY_SEED)
1323 .serialize()
1324 .to_vec(),
1325 ),
1326 message: Some(nested_message.to_vec()),
1327 kyber_pre_key_id: None,
1328 kyber_ciphertext: None,
1329 };
1330 let mut wire = Vec::with_capacity(1 + proto.encoded_len() as usize);
1331 wire.push(encode_version_byte(
1332 TEST_MESSAGE_VERSION,
1333 CIPHERTEXT_MESSAGE_CURRENT_VERSION,
1334 ));
1335 wire.extend_from_slice(&proto.encode_to_vec());
1336 wire
1337 }
1338
1339 #[test]
1340 fn direct_signal_encoder_matches_generated_wire() {
1341 let counters = [0, 127, 128, u32::MAX];
1342 let ciphertexts = [Vec::new(), vec![0x5A], vec![0xC3; 128]];
1343
1344 for counter in counters {
1345 for previous_counter in counters {
1346 for ciphertext in &ciphertexts {
1347 let direct = test_signal_message(counter, previous_counter, ciphertext);
1348 let generated = generated_signal_wire(counter, previous_counter, ciphertext);
1349 assert_eq!(direct.serialized(), generated);
1350 }
1351 }
1352 }
1353 }
1354
1355 #[test]
1356 fn direct_pre_key_encoder_matches_generated_wire() {
1357 let signal_wire = generated_signal_wire(128, 127, &[0x7E; 64]);
1358 let cases = [
1359 (0, None, 0),
1360 (127, Some(127), 127),
1361 (128, Some(128), 128),
1362 (u32::MAX, Some(u32::MAX), u32::MAX),
1363 ];
1364
1365 for (registration_id, pre_key_id, signed_pre_key_id) in cases {
1366 let pre_key_id = pre_key_id.map(Into::into);
1367 let signed_pre_key_id = signed_pre_key_id.into();
1368 let direct = PreKeySignalMessage::new(
1369 TEST_MESSAGE_VERSION,
1370 registration_id,
1371 pre_key_id,
1372 signed_pre_key_id,
1373 public_key_from_seed(TEST_BASE_KEY_SEED),
1374 IdentityKey::new(public_key_from_seed(TEST_SENDER_IDENTITY_SEED)),
1375 SignalMessage::try_from(signal_wire.as_slice()).expect("nested SignalMessage"),
1376 )
1377 .expect("test PreKeySignalMessage");
1378 let generated = generated_pre_key_wire(
1379 registration_id,
1380 pre_key_id,
1381 signed_pre_key_id,
1382 &signal_wire,
1383 );
1384 assert_eq!(direct.serialized(), generated);
1385 }
1386 }
1387
1388 #[test]
1389 fn parsed_signal_body_shares_its_wire_allocation() {
1390 let wire = test_signal_message(7, 3, &[0x42; 256]).into_serialized();
1391
1392 let borrowed = SignalMessage::try_from(wire.as_ref()).expect("borrowed SignalMessage");
1393 assert!(
1394 subslice_range(
1395 borrowed.serialized(),
1396 borrowed.body().expect("borrowed Signal body"),
1397 )
1398 .is_some()
1399 );
1400
1401 let transport = Bytes::from(wire);
1402 let transport_ptr = transport.as_ptr();
1403 let shared = SignalMessage::try_from(transport).expect("shared SignalMessage");
1404 assert_eq!(shared.serialized().as_ptr(), transport_ptr);
1405 assert!(
1406 subslice_range(
1407 shared.serialized(),
1408 shared.body().expect("shared Signal body"),
1409 )
1410 .is_some()
1411 );
1412 }
1413
1414 #[test]
1415 fn parsed_pre_key_and_nested_signal_share_one_wire_allocation() {
1416 let signal_wire = generated_signal_wire(9, 4, &[0x24; 256]);
1417 let wire = PreKeySignalMessage::new(
1418 TEST_MESSAGE_VERSION,
1419 128,
1420 Some(127.into()),
1421 129.into(),
1422 public_key_from_seed(TEST_BASE_KEY_SEED),
1423 IdentityKey::new(public_key_from_seed(TEST_SENDER_IDENTITY_SEED)),
1424 SignalMessage::try_from(signal_wire.as_slice()).expect("nested SignalMessage"),
1425 )
1426 .expect("test PreKeySignalMessage")
1427 .into_serialized();
1428
1429 let borrowed =
1430 PreKeySignalMessage::try_from(wire.as_ref()).expect("borrowed PreKeySignalMessage");
1431 assert!(subslice_range(borrowed.serialized(), borrowed.message().serialized()).is_some());
1432 assert!(
1433 subslice_range(
1434 borrowed.serialized(),
1435 borrowed
1436 .message()
1437 .body()
1438 .expect("borrowed nested Signal body"),
1439 )
1440 .is_some()
1441 );
1442
1443 let transport = Bytes::from(wire);
1444 let transport_ptr = transport.as_ptr();
1445 let shared = PreKeySignalMessage::try_from(transport).expect("shared PreKeySignalMessage");
1446 assert_eq!(shared.serialized().as_ptr(), transport_ptr);
1447 assert!(subslice_range(shared.serialized(), shared.message().serialized()).is_some());
1448 assert!(
1449 subslice_range(
1450 shared.serialized(),
1451 shared.message().body().expect("shared nested Signal body"),
1452 )
1453 .is_some()
1454 );
1455 }
1456
1457 #[test]
1458 fn consuming_unique_encoded_messages_reuses_their_wire_allocations() {
1459 let signal = test_signal_message(1, 0, b"signal");
1460 let signal_ptr = signal.serialized().as_ptr();
1461 let signal_wire = signal.into_serialized();
1462 assert_eq!(signal_wire.as_ptr(), signal_ptr);
1463
1464 let pre_key = PreKeySignalMessage::new(
1465 TEST_MESSAGE_VERSION,
1466 1,
1467 Some(1.into()),
1468 1.into(),
1469 public_key_from_seed(TEST_BASE_KEY_SEED),
1470 IdentityKey::new(public_key_from_seed(TEST_SENDER_IDENTITY_SEED)),
1471 SignalMessage::try_from(signal_wire.as_ref()).expect("nested SignalMessage"),
1472 )
1473 .expect("test PreKeySignalMessage");
1474 let pre_key_ptr = pre_key.serialized().as_ptr();
1475 let pre_key_wire = pre_key.into_serialized();
1476 assert_eq!(pre_key_wire.as_ptr(), pre_key_ptr);
1477 }
1478
1479 #[test]
1480 fn consuming_unique_signal_body_reuses_the_wire_allocation() {
1481 let expected = [0x5a; 256];
1482 let signal = test_signal_message(1, 0, &expected);
1483 let wire_ptr = signal.serialized().as_ptr();
1484
1485 let ciphertext = signal
1486 .try_into_ciphertext_vec()
1487 .expect("unique Signal wire must be consumable");
1488
1489 assert_eq!(ciphertext, expected);
1490 assert_eq!(ciphertext.as_ptr(), wire_ptr);
1491 }
1492
1493 #[test]
1494 fn nested_pre_key_signal_becomes_consumable_after_outer_envelope_is_dropped() {
1495 let expected = [0x6b; 256];
1496 let signal_wire = generated_signal_wire(1, 0, &expected);
1497 let pre_key = PreKeySignalMessage::new(
1498 TEST_MESSAGE_VERSION,
1499 1,
1500 Some(1.into()),
1501 1.into(),
1502 public_key_from_seed(TEST_BASE_KEY_SEED),
1503 IdentityKey::new(public_key_from_seed(TEST_SENDER_IDENTITY_SEED)),
1504 SignalMessage::try_from(signal_wire.as_slice()).expect("nested SignalMessage"),
1505 )
1506 .expect("test PreKeySignalMessage");
1507
1508 let ciphertext = pre_key
1509 .into_message()
1510 .try_into_ciphertext_vec()
1511 .expect("dropping the outer PreKey view must make its nested Signal wire unique");
1512
1513 assert_eq!(ciphertext, expected);
1514 }
1515
1516 #[test]
1517 fn shared_signal_wire_is_preserved_for_borrowed_fallback() {
1518 let wire = Bytes::from(test_signal_message(1, 0, b"shared").into_serialized());
1519 let retained_owner = wire.clone();
1520 let signal = SignalMessage::try_from(wire).expect("shared SignalMessage");
1521
1522 assert!(signal.try_into_ciphertext_vec().is_err());
1523 assert!(!retained_owner.is_empty());
1524 }
1525
1526 #[test]
1527 fn decryption_error_proto_uses_buffa_size_cache_encoding() {
1528 let proto = DecryptionErrorMessageProto {
1529 ratchet_key: Some(vec![1, 2, 3]),
1530 timestamp: Some(150),
1531 device_id: Some(7),
1532 };
1533
1534 let bytes = proto.encode_to_vec();
1535 assert_eq!(bytes, [0x0a, 3, 1, 2, 3, 0x10, 0x96, 0x01, 0x18, 7]);
1536 assert_eq!(proto.encoded_len() as usize, bytes.len());
1537
1538 let decoded =
1539 DecryptionErrorMessageProto::decode_from_slice(&bytes).expect("decode test proto");
1540 assert_eq!(decoded.ratchet_key.as_deref(), Some(&[1, 2, 3][..]));
1541 assert_eq!(decoded.timestamp, Some(150));
1542 assert_eq!(decoded.device_id, Some(7));
1543 }
1544}