Skip to main content

wacore_libsignal/protocol/
protocol.rs

1//
2// Copyright 2020-2021 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6use 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
19/// Get-or-init for `OnceLock<Box<[u8]>>` with a fallible initializer.
20fn 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    // get() can't be None: even if a racing set() lost, the winner's value is stored
29    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/// Own a borrowed wire buffer in one allocation. Constructing `Bytes` from a
39/// boxed slice keeps its allocation directly recoverable by
40/// [`bytes_into_boxed_slice`] until sharing is actually requested.
41#[inline]
42fn bytes_from_slice(value: &[u8]) -> Bytes {
43    Bytes::from(Box::<[u8]>::from(value))
44}
45
46/// Preserve the historical owned-return API. `Bytes -> Vec` reuses a unique,
47/// full backing allocation and copies only when the input is a shared slice,
48/// which is the only case where returning one standalone `Box<[u8]>` requires
49/// new ownership.
50#[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/// Append a protobuf bytes field and return the value's range in `output`.
88#[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
108// Signal's original implementation uses version 4, but WhatsApp Web,
109// Interoperable Signal implementations use version 3.
110pub 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
123/// Expand validation into each ownership adapter so parse errors are lowered
124/// directly into its final `Result`. A function returning an intermediate
125/// parsed value prevents that optimization on current LLVM and adds work to
126/// the common format-probing failure path.
127macro_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
170/// Parse both layers of a PreKey envelope before either ownership adapter
171/// allocates or increments a reference count.
172macro_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    /// Immutable, reference-counted ownership lets a decoded Signal envelope
270    /// remain a slice of its transport or enclosing PreKey allocation.
271    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    // Signal wire protos are (de)serialized only in this crate, so direct
304    // buffa calls duplicate nothing; no codec pin needed (here and below).
305    #[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        // Encode the tiny envelope straight into its final allocation. The
319        // generated field tags keep this schema-driven, while direct framing
320        // avoids allocating a temporary Vec for both the ratchet key and the
321        // potentially large ciphertext before copying them into the envelope.
322        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        // Validate before taking ownership. Besides avoiding wasted work for
432        // malformed input, this preserves the allocation-free rejection path
433        // used when callers probe a ciphertext's Signal envelope type.
434        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    /// Recover a uniquely-owned wire allocation as a ciphertext-only `Vec`.
445    /// Shared envelopes keep using the reusable decrypt scratch path instead.
446    #[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            // A warning instead of an error because we try multiple sessions.
479            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        // Follow the generated encoder's ascending-tag order while writing all
563        // byte fields directly into the final envelope. This removes three
564        // temporary Vec allocations/copies without changing canonical wire
565        // output.
566        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        // The outer envelope must be contiguous, so it necessarily copied the
599        // inner bytes once. Rebase the retained parsed message onto that copy
600        // and release its old allocation: steady-state PreKey storage is now a
601        // single allocation shared by both handles.
602        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    /// Consume the outer PreKey envelope and release its shared handle, leaving
682    /// the nested Signal message as the sole owner when no other wire slices
683    /// exist. Used by the owned decrypt path after all PreKey metadata is read.
684    #[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        // Release the nested slice first so a uniquely-owned outer allocation
703        // can be recovered by `Bytes -> Vec` without a copy.
704        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 is cached after the first parse to avoid re-decoding.
740    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        // Build serialized buffer directly: [version_byte || proto || signature]
779        // Sign over [version_byte || proto], then append signature
780        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        // Sign the data we've built so far (version + proto)
788        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    /// Like [`Self::verify_signature`], against a cached verifier: the
812    /// per-key Edwards derivations are reused across messages instead of
813    /// recomputed per signature.
814    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    /// Returns the ciphertext, parsing and caching it on first access.
842    ///
843    /// The ciphertext is extracted from the protobuf-encoded `serialized` bytes
844    /// and cached to avoid repeated parsing.
845    ///
846    /// # Performance Note
847    ///
848    /// Callers should avoid calling this in hot loops when possible.
849    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        // serialized layout: [version_byte || protobuf || signature]
855        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        // The message contains at least a X25519 key and a chain key
1016        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    /// Identifies a serialized PlaintextContent.
1076    ///
1077    /// This ensures someone doesn't try to serialize an arbitrary Content message as
1078    /// PlaintextContent; only messages that are okay to send as plaintext should be allowed.
1079    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        // Validate wire type per field; a mismatch falls through to skip_field
1145        // instead of mis-decoding peer input.
1146        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                // Thread the live recursion budget through: a bare skip_field
1158                // restarts it at RECURSION_LIMIT, which unknown group fields
1159                // could exploit for depth-doubling.
1160                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}