1use std::fmt;
10
11use buffa::MessageField;
12use bytes::Bytes;
13
14use crate::core::curve::PublicKey;
15use crate::protocol::error::{Result, SignalProtocolError};
16use crate::protocol::ratchet::MessageKeyGenerator;
17use crate::protocol::ratchet::keys::MessageKeys;
18use crate::protocol::stores::{
19 SenderKeyStateStructure, SessionStructure, sender_key_state_structure, session_structure,
20};
21
22const PRIVATE_KEY_BYTES: usize = 32;
23const SYMMETRIC_KEY_BYTES: usize = 32;
24const MESSAGE_IV_BYTES: usize = 16;
25
26#[derive(Debug, Clone, PartialEq, Eq, Default)]
28pub struct SessionRecordComponents {
29 pub current_session: Option<SessionComponents>,
30 pub previous_sessions: Vec<SessionComponents>,
31}
32
33#[derive(Clone, PartialEq, Eq, Default)]
35pub struct SessionComponents {
36 pub session_version: Option<u32>,
37 pub local_identity_public: Option<Vec<u8>>,
38 pub remote_identity_public: Option<Vec<u8>>,
39 pub root_key: Option<Vec<u8>>,
40 pub previous_counter: Option<u32>,
41 pub sender_chain: Option<SessionChainComponents>,
42 pub receiver_chains: Vec<SessionChainComponents>,
43 pub pending_key_exchange: Option<PendingKeyExchangeComponents>,
44 pub pending_pre_key: Option<PendingPreKeyComponents>,
45 pub remote_registration_id: Option<u32>,
46 pub local_registration_id: Option<u32>,
47 pub needs_refresh: Option<bool>,
48 pub alice_base_key: Option<Vec<u8>>,
49}
50
51#[derive(Clone, PartialEq, Eq, Default)]
57pub struct SessionChainComponents {
58 pub sender_ratchet_key: Option<Vec<u8>>,
59 pub sender_ratchet_key_private: Option<Vec<u8>>,
60 pub chain_key: Option<SessionChainKeyComponents>,
61 pub message_keys: Vec<SessionMessageKeyComponents>,
62}
63
64#[derive(Clone, PartialEq, Eq, Default)]
66pub struct SessionChainKeyComponents {
67 pub index: Option<u32>,
68 pub key: Option<Vec<u8>>,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct SessionMessageKeyComponents {
74 pub index: u32,
75 pub material: SessionMessageKeyMaterial,
76}
77
78#[derive(Clone, Copy, PartialEq, Eq)]
85pub enum SessionMessageKeyMaterial {
86 Seed([u8; SYMMETRIC_KEY_BYTES]),
87 Derived {
88 cipher_key: [u8; SYMMETRIC_KEY_BYTES],
89 mac_key: [u8; SYMMETRIC_KEY_BYTES],
90 iv: [u8; MESSAGE_IV_BYTES],
91 },
92}
93
94#[derive(Clone, PartialEq, Eq, Default)]
96pub struct PendingKeyExchangeComponents {
97 pub sequence: Option<u32>,
98 pub local_base_key: Option<Vec<u8>>,
99 pub local_base_key_private: Option<Vec<u8>>,
100 pub local_ratchet_key: Option<Vec<u8>>,
101 pub local_ratchet_key_private: Option<Vec<u8>>,
102 pub local_identity_key: Option<Vec<u8>>,
103 pub local_identity_key_private: Option<Vec<u8>>,
104}
105
106#[derive(Clone, PartialEq, Eq, Default)]
108pub struct PendingPreKeyComponents {
109 pub pre_key_id: Option<u32>,
110 pub signed_pre_key_id: Option<i32>,
111 pub base_key: Option<Vec<u8>>,
112 pub kyber_pre_key_id: Option<u32>,
113 pub kyber_ciphertext: Option<Vec<u8>>,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Default)]
118pub struct SenderKeyRecordComponents {
119 pub states: Vec<SenderKeyStateComponents>,
120}
121
122#[derive(Debug, Clone, PartialEq, Eq)]
124pub struct SenderKeyStateComponents {
125 pub key_id: u32,
126 pub chain_key: SenderChainKeyComponents,
127 pub signing_key: SenderSigningKeyComponents,
128 pub message_keys: Vec<SenderMessageKeyComponents>,
129}
130
131#[derive(Clone, PartialEq, Eq)]
133pub struct SenderChainKeyComponents {
134 pub iteration: u32,
135 pub seed: Vec<u8>,
136}
137
138#[derive(Clone, PartialEq, Eq)]
140pub struct SenderSigningKeyComponents {
141 pub public: Vec<u8>,
142 pub private: Option<Vec<u8>>,
143}
144
145#[derive(Clone, PartialEq, Eq)]
147pub struct SenderMessageKeyComponents {
148 pub iteration: u32,
149 pub seed: Vec<u8>,
150}
151
152struct Redacted;
153
154impl fmt::Debug for Redacted {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 f.write_str("<redacted>")
157 }
158}
159
160macro_rules! impl_redacted_debug {
161 ($type:ident { visible: [$($visible:ident),* $(,)?], secret: [$($secret:ident),* $(,)?] }) => {
162 impl fmt::Debug for $type {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 let mut debug = f.debug_struct(stringify!($type));
165 $(debug.field(stringify!($visible), &self.$visible);)*
166 $(debug.field(stringify!($secret), &Redacted);)*
167 debug.finish()
168 }
169 }
170 };
171}
172
173impl_redacted_debug!(SessionComponents {
174 visible: [
175 session_version,
176 previous_counter,
177 sender_chain,
178 receiver_chains,
179 pending_key_exchange,
180 pending_pre_key,
181 remote_registration_id,
182 local_registration_id,
183 needs_refresh,
184 ],
185 secret: [
186 local_identity_public,
187 remote_identity_public,
188 root_key,
189 alice_base_key,
190 ]
191});
192impl_redacted_debug!(SessionChainComponents {
193 visible: [chain_key, message_keys],
194 secret: [sender_ratchet_key, sender_ratchet_key_private]
195});
196impl_redacted_debug!(SessionChainKeyComponents {
197 visible: [index],
198 secret: [key]
199});
200impl_redacted_debug!(PendingKeyExchangeComponents {
201 visible: [sequence],
202 secret: [
203 local_base_key,
204 local_base_key_private,
205 local_ratchet_key,
206 local_ratchet_key_private,
207 local_identity_key,
208 local_identity_key_private,
209 ]
210});
211impl_redacted_debug!(PendingPreKeyComponents {
212 visible: [pre_key_id, signed_pre_key_id, kyber_pre_key_id],
213 secret: [base_key, kyber_ciphertext]
214});
215impl_redacted_debug!(SenderChainKeyComponents {
216 visible: [iteration],
217 secret: [seed]
218});
219impl_redacted_debug!(SenderSigningKeyComponents {
220 visible: [],
221 secret: [public, private]
222});
223impl_redacted_debug!(SenderMessageKeyComponents {
224 visible: [iteration],
225 secret: [seed]
226});
227
228impl fmt::Debug for SessionMessageKeyMaterial {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 match self {
231 Self::Seed(_) => f.debug_tuple("Seed").field(&Redacted).finish(),
232 Self::Derived { .. } => f
233 .debug_struct("Derived")
234 .field("cipher_key", &Redacted)
235 .field("mac_key", &Redacted)
236 .field("iv", &Redacted)
237 .finish(),
238 }
239 }
240}
241
242fn invalid(field: &'static str, expectation: &'static str) -> SignalProtocolError {
243 SignalProtocolError::InvalidArgument(format!("{field} must be {expectation}"))
244}
245
246fn normalize_public_key(mut value: Vec<u8>, field: &'static str) -> Result<Vec<u8>> {
247 let key = match value.len() {
248 PublicKey::RAW_KEY_LEN => PublicKey::from_djb_public_key_bytes(&value),
249 PublicKey::SERIALIZED_KEY_LEN => PublicKey::deserialize(&value),
250 _ => {
251 return Err(invalid(field, "a raw or canonically serialized public key"));
252 }
253 }
254 .map_err(|_| invalid(field, "a valid public key"))?;
255
256 value.clear();
257 value.extend_from_slice(&key.serialize());
258 Ok(value)
259}
260
261fn optional_public_key(value: Option<Vec<u8>>, field: &'static str) -> Result<Option<Vec<u8>>> {
262 value
263 .map(|value| normalize_public_key(value, field))
264 .transpose()
265}
266
267fn required_public_key(value: Option<Vec<u8>>, field: &'static str) -> Result<Vec<u8>> {
268 normalize_public_key(value.ok_or_else(|| invalid(field, "present"))?, field)
269}
270
271fn exact_bytes(value: Vec<u8>, length: usize, field: &'static str) -> Result<Vec<u8>> {
272 if value.len() == length {
273 Ok(value)
274 } else {
275 Err(SignalProtocolError::InvalidArgument(format!(
276 "{field} must be {length} bytes"
277 )))
278 }
279}
280
281fn optional_exact_bytes(
282 value: Option<Vec<u8>>,
283 length: usize,
284 field: &'static str,
285) -> Result<Option<Vec<u8>>> {
286 value
287 .map(|value| exact_bytes(value, length, field))
288 .transpose()
289}
290
291fn required_exact_bytes(
292 value: Option<Vec<u8>>,
293 length: usize,
294 field: &'static str,
295) -> Result<Vec<u8>> {
296 exact_bytes(
297 value.ok_or_else(|| invalid(field, "present"))?,
298 length,
299 field,
300 )
301}
302
303fn key_material<const N: usize>(value: Bytes, field: &'static str) -> Result<[u8; N]> {
306 <[u8; N]>::try_from(value.as_ref())
307 .map_err(|_| SignalProtocolError::InvalidArgument(format!("{field} must be {N} bytes")))
308}
309
310fn required_key_material<const N: usize>(
311 value: Option<Bytes>,
312 field: &'static str,
313) -> Result<[u8; N]> {
314 key_material(value.ok_or_else(|| invalid(field, "present"))?, field)
315}
316
317impl SessionMessageKeyComponents {
318 fn into_structure(self) -> session_structure::chain::MessageKey {
319 match self.material {
320 SessionMessageKeyMaterial::Seed(seed) => {
321 MessageKeyGenerator::new_from_seed(&seed, self.index).into_pb()
322 }
323 SessionMessageKeyMaterial::Derived {
324 cipher_key,
325 mac_key,
326 iv,
327 } => session_structure::chain::MessageKey {
328 index: Some(self.index),
329 cipher_key: Some(Bytes::copy_from_slice(&cipher_key)),
330 mac_key: Some(Bytes::copy_from_slice(&mac_key)),
331 iv: Some(Bytes::copy_from_slice(&iv)),
332 seed: None,
333 },
334 }
335 }
336
337 fn from_structure(mut value: session_structure::chain::MessageKey) -> Result<Self> {
338 let index = value
339 .index
340 .ok_or_else(|| invalid("session message-key index", "present"))?;
341 if let Some(seed) = value.seed.take() {
347 let seed = key_material(seed, "session message-key seed")?;
348 let derived = MessageKeys::derive_keys(&seed, None, index);
349 if value.cipher_key.as_deref() != Some(derived.cipher_key())
350 || value.mac_key.as_deref() != Some(derived.mac_key())
351 || value.iv.as_deref() != Some(derived.iv())
352 {
353 return Err(invalid(
354 "session message-key seed",
355 "consistent with the derived keys stored beside it",
356 ));
357 }
358 return Ok(Self {
359 index,
360 material: SessionMessageKeyMaterial::Seed(seed),
361 });
362 }
363 Ok(Self {
364 index,
365 material: SessionMessageKeyMaterial::Derived {
366 cipher_key: required_key_material(value.cipher_key, "session message cipher key")?,
367 mac_key: required_key_material(value.mac_key, "session message MAC key")?,
368 iv: required_key_material(value.iv, "session message IV")?,
369 },
370 })
371 }
372}
373
374impl SessionChainKeyComponents {
375 fn into_structure(self) -> Result<session_structure::chain::ChainKey> {
376 Ok(session_structure::chain::ChainKey {
377 index: self.index,
378 key: optional_exact_bytes(self.key, SYMMETRIC_KEY_BYTES, "session chain key")?
379 .map(Bytes::from),
380 })
381 }
382
383 fn from_structure(value: session_structure::chain::ChainKey) -> Result<Self> {
384 Ok(Self {
385 index: value.index,
386 key: optional_exact_bytes(
387 value.key.map(|value| value.to_vec()),
388 SYMMETRIC_KEY_BYTES,
389 "session chain key",
390 )?,
391 })
392 }
393
394 fn into_required_structure(self) -> Result<session_structure::chain::ChainKey> {
395 Ok(session_structure::chain::ChainKey {
396 index: Some(
397 self.index
398 .ok_or_else(|| invalid("sender chain-key index", "present"))?,
399 ),
400 key: Some(Bytes::from(required_exact_bytes(
401 self.key,
402 SYMMETRIC_KEY_BYTES,
403 "sender chain key",
404 )?)),
405 })
406 }
407
408 fn from_required_structure(value: session_structure::chain::ChainKey) -> Result<Self> {
409 Ok(Self {
410 index: Some(
411 value
412 .index
413 .ok_or_else(|| invalid("sender chain-key index", "present"))?,
414 ),
415 key: Some(required_exact_bytes(
416 value.key.map(|value| value.to_vec()),
417 SYMMETRIC_KEY_BYTES,
418 "sender chain key",
419 )?),
420 })
421 }
422}
423
424impl SessionChainComponents {
425 fn into_sender_structure(self) -> Result<session_structure::Chain> {
426 Ok(session_structure::Chain {
427 sender_ratchet_key: Some(required_public_key(
428 self.sender_ratchet_key,
429 "sender ratchet public key",
430 )?),
431 sender_ratchet_key_private: Some(required_exact_bytes(
432 self.sender_ratchet_key_private,
433 PRIVATE_KEY_BYTES,
434 "sender ratchet private key",
435 )?),
436 chain_key: MessageField::some(
437 self.chain_key
438 .ok_or_else(|| invalid("sender chain key", "present"))?
439 .into_required_structure()?,
440 ),
441 message_keys: self
442 .message_keys
443 .into_iter()
444 .map(SessionMessageKeyComponents::into_structure)
445 .collect(),
446 })
447 }
448
449 fn into_receiver_structure(self) -> Result<session_structure::Chain> {
450 if self.sender_ratchet_key_private.is_some() {
451 return Err(invalid(
452 "receiver ratchet private key",
453 "absent from receiver chains",
454 ));
455 }
456
457 Ok(session_structure::Chain {
458 sender_ratchet_key: optional_public_key(
459 self.sender_ratchet_key,
460 "receiver ratchet public key",
461 )?,
462 sender_ratchet_key_private: None,
463 chain_key: self
464 .chain_key
465 .map(SessionChainKeyComponents::into_structure)
466 .transpose()?
467 .into(),
468 message_keys: self
469 .message_keys
470 .into_iter()
471 .map(SessionMessageKeyComponents::into_structure)
472 .collect(),
473 })
474 }
475
476 fn from_sender_structure(mut value: session_structure::Chain) -> Result<Self> {
477 Ok(Self {
478 sender_ratchet_key: Some(required_public_key(
479 value.sender_ratchet_key,
480 "sender ratchet public key",
481 )?),
482 sender_ratchet_key_private: Some(required_exact_bytes(
483 value.sender_ratchet_key_private,
484 PRIVATE_KEY_BYTES,
485 "sender ratchet private key",
486 )?),
487 chain_key: Some(SessionChainKeyComponents::from_required_structure(
488 value
489 .chain_key
490 .take()
491 .ok_or_else(|| invalid("sender chain key", "present"))?,
492 )?),
493 message_keys: value
494 .message_keys
495 .into_iter()
496 .map(SessionMessageKeyComponents::from_structure)
497 .collect::<Result<_>>()?,
498 })
499 }
500
501 fn from_receiver_structure(mut value: session_structure::Chain) -> Result<Self> {
502 value.sender_ratchet_key_private = None;
505 Ok(Self {
506 sender_ratchet_key: optional_public_key(
507 value.sender_ratchet_key,
508 "receiver ratchet public key",
509 )?,
510 sender_ratchet_key_private: None,
511 chain_key: value
512 .chain_key
513 .take()
514 .map(SessionChainKeyComponents::from_structure)
515 .transpose()?,
516 message_keys: value
517 .message_keys
518 .into_iter()
519 .map(SessionMessageKeyComponents::from_structure)
520 .collect::<Result<_>>()?,
521 })
522 }
523}
524
525impl PendingKeyExchangeComponents {
526 fn into_structure(self) -> Result<session_structure::PendingKeyExchange> {
527 Ok(session_structure::PendingKeyExchange {
528 sequence: self.sequence,
529 local_base_key: optional_public_key(self.local_base_key, "local base public key")?,
530 local_base_key_private: optional_exact_bytes(
531 self.local_base_key_private,
532 PRIVATE_KEY_BYTES,
533 "local base private key",
534 )?,
535 local_ratchet_key: optional_public_key(
536 self.local_ratchet_key,
537 "local ratchet public key",
538 )?,
539 local_ratchet_key_private: optional_exact_bytes(
540 self.local_ratchet_key_private,
541 PRIVATE_KEY_BYTES,
542 "local ratchet private key",
543 )?,
544 local_identity_key: optional_public_key(
545 self.local_identity_key,
546 "local identity public key",
547 )?,
548 local_identity_key_private: optional_exact_bytes(
549 self.local_identity_key_private,
550 PRIVATE_KEY_BYTES,
551 "local identity private key",
552 )?,
553 })
554 }
555
556 fn from_structure(value: session_structure::PendingKeyExchange) -> Result<Self> {
557 Ok(Self {
558 sequence: value.sequence,
559 local_base_key: optional_public_key(value.local_base_key, "local base public key")?,
560 local_base_key_private: optional_exact_bytes(
561 value.local_base_key_private,
562 PRIVATE_KEY_BYTES,
563 "local base private key",
564 )?,
565 local_ratchet_key: optional_public_key(
566 value.local_ratchet_key,
567 "local ratchet public key",
568 )?,
569 local_ratchet_key_private: optional_exact_bytes(
570 value.local_ratchet_key_private,
571 PRIVATE_KEY_BYTES,
572 "local ratchet private key",
573 )?,
574 local_identity_key: optional_public_key(
575 value.local_identity_key,
576 "local identity public key",
577 )?,
578 local_identity_key_private: optional_exact_bytes(
579 value.local_identity_key_private,
580 PRIVATE_KEY_BYTES,
581 "local identity private key",
582 )?,
583 })
584 }
585}
586
587impl PendingPreKeyComponents {
588 fn into_structure(self) -> Result<session_structure::PendingPreKey> {
589 Ok(session_structure::PendingPreKey {
590 pre_key_id: self.pre_key_id,
591 signed_pre_key_id: self.signed_pre_key_id,
592 base_key: optional_public_key(self.base_key, "pending pre-key base key")?,
593 kyber_pre_key_id: self.kyber_pre_key_id,
594 kyber_ciphertext: self.kyber_ciphertext,
595 })
596 }
597
598 fn from_structure(value: session_structure::PendingPreKey) -> Result<Self> {
599 Ok(Self {
600 pre_key_id: value.pre_key_id,
601 signed_pre_key_id: value.signed_pre_key_id,
602 base_key: optional_public_key(value.base_key, "pending pre-key base key")?,
603 kyber_pre_key_id: value.kyber_pre_key_id,
604 kyber_ciphertext: value.kyber_ciphertext,
605 })
606 }
607}
608
609pub(crate) fn session_structure_from_components(
610 value: SessionComponents,
611) -> Result<SessionStructure> {
612 Ok(SessionStructure {
613 session_version: value.session_version,
614 local_identity_public: optional_public_key(
615 value.local_identity_public,
616 "local identity public key",
617 )?,
618 remote_identity_public: optional_public_key(
619 value.remote_identity_public,
620 "remote identity public key",
621 )?,
622 root_key: optional_exact_bytes(value.root_key, SYMMETRIC_KEY_BYTES, "session root key")?,
623 previous_counter: value.previous_counter,
624 sender_chain: value
625 .sender_chain
626 .map(SessionChainComponents::into_sender_structure)
627 .transpose()?
628 .into(),
629 receiver_chains: value
630 .receiver_chains
631 .into_iter()
632 .map(SessionChainComponents::into_receiver_structure)
633 .collect::<Result<_>>()?,
634 pending_key_exchange: value
635 .pending_key_exchange
636 .map(PendingKeyExchangeComponents::into_structure)
637 .transpose()?
638 .into(),
639 pending_pre_key: value
640 .pending_pre_key
641 .map(PendingPreKeyComponents::into_structure)
642 .transpose()?
643 .into(),
644 remote_registration_id: value.remote_registration_id,
645 local_registration_id: value.local_registration_id,
646 needs_refresh: value.needs_refresh,
647 alice_base_key: optional_public_key(value.alice_base_key, "session base public key")?,
648 })
649}
650
651pub(crate) fn session_components_from_structure(
652 mut value: SessionStructure,
653) -> Result<SessionComponents> {
654 Ok(SessionComponents {
655 session_version: value.session_version,
656 local_identity_public: optional_public_key(
657 value.local_identity_public,
658 "local identity public key",
659 )?,
660 remote_identity_public: optional_public_key(
661 value.remote_identity_public,
662 "remote identity public key",
663 )?,
664 root_key: optional_exact_bytes(value.root_key, SYMMETRIC_KEY_BYTES, "session root key")?,
665 previous_counter: value.previous_counter,
666 sender_chain: value
667 .sender_chain
668 .take()
669 .map(SessionChainComponents::from_sender_structure)
670 .transpose()?,
671 receiver_chains: value
672 .receiver_chains
673 .into_iter()
674 .map(SessionChainComponents::from_receiver_structure)
675 .collect::<Result<_>>()?,
676 pending_key_exchange: value
677 .pending_key_exchange
678 .take()
679 .map(PendingKeyExchangeComponents::from_structure)
680 .transpose()?,
681 pending_pre_key: value
682 .pending_pre_key
683 .take()
684 .map(PendingPreKeyComponents::from_structure)
685 .transpose()?,
686 remote_registration_id: value.remote_registration_id,
687 local_registration_id: value.local_registration_id,
688 needs_refresh: value.needs_refresh,
689 alice_base_key: optional_public_key(value.alice_base_key, "session base public key")?,
690 })
691}
692
693pub(crate) fn sender_state_structure_from_components(
694 value: SenderKeyStateComponents,
695) -> Result<SenderKeyStateStructure> {
696 let chain_seed = exact_bytes(
697 value.chain_key.seed,
698 SYMMETRIC_KEY_BYTES,
699 "sender chain seed",
700 )?;
701 let signing_public =
702 normalize_public_key(value.signing_key.public, "sender signing public key")?;
703 let signing_private = optional_exact_bytes(
704 value.signing_key.private,
705 PRIVATE_KEY_BYTES,
706 "sender signing private key",
707 )?;
708 Ok(SenderKeyStateStructure {
709 sender_key_id: Some(value.key_id),
710 sender_chain_key: MessageField::some(sender_key_state_structure::SenderChainKey {
711 iteration: Some(value.chain_key.iteration),
712 seed: Some(Bytes::from(chain_seed)),
713 }),
714 sender_signing_key: MessageField::some(sender_key_state_structure::SenderSigningKey {
715 public: Some(Bytes::from(signing_public)),
716 private: signing_private.map(Bytes::from),
717 }),
718 sender_message_keys: value
719 .message_keys
720 .into_iter()
721 .map(|key| {
722 Ok(sender_key_state_structure::SenderMessageKey {
723 iteration: Some(key.iteration),
724 seed: Some(Bytes::from(exact_bytes(
725 key.seed,
726 SYMMETRIC_KEY_BYTES,
727 "sender message-key seed",
728 )?)),
729 })
730 })
731 .collect::<Result<_>>()?,
732 })
733}
734
735pub(crate) fn sender_state_components_from_structure(
736 mut value: SenderKeyStateStructure,
737) -> Result<SenderKeyStateComponents> {
738 let chain = value
739 .sender_chain_key
740 .take()
741 .ok_or_else(|| invalid("sender chain key", "present"))?;
742 let signing = value
743 .sender_signing_key
744 .take()
745 .ok_or_else(|| invalid("sender signing key", "present"))?;
746 Ok(SenderKeyStateComponents {
747 key_id: value
748 .sender_key_id
749 .ok_or_else(|| invalid("sender key id", "present"))?,
750 chain_key: SenderChainKeyComponents {
751 iteration: chain
752 .iteration
753 .ok_or_else(|| invalid("sender chain iteration", "present"))?,
754 seed: exact_bytes(
755 chain
756 .seed
757 .ok_or_else(|| invalid("sender chain seed", "present"))?
758 .to_vec(),
759 SYMMETRIC_KEY_BYTES,
760 "sender chain seed",
761 )?,
762 },
763 signing_key: SenderSigningKeyComponents {
764 public: normalize_public_key(
765 signing
766 .public
767 .ok_or_else(|| invalid("sender signing public key", "present"))?
768 .to_vec(),
769 "sender signing public key",
770 )?,
771 private: optional_exact_bytes(
772 signing.private.map(|value| value.to_vec()),
773 PRIVATE_KEY_BYTES,
774 "sender signing private key",
775 )?,
776 },
777 message_keys: value
778 .sender_message_keys
779 .into_iter()
780 .map(|key| {
781 Ok(SenderMessageKeyComponents {
782 iteration: key
783 .iteration
784 .ok_or_else(|| invalid("sender message-key iteration", "present"))?,
785 seed: exact_bytes(
786 key.seed
787 .ok_or_else(|| invalid("sender message-key seed", "present"))?
788 .to_vec(),
789 SYMMETRIC_KEY_BYTES,
790 "sender message-key seed",
791 )?,
792 })
793 })
794 .collect::<Result<_>>()?,
795 })
796}
797
798#[cfg(test)]
799mod tests {
800 use super::*;
801 use crate::protocol::{
802 ChainKey, IdentityKey, KeyPair, RootKey, SenderKeyRecord, SessionRecord, SessionState,
803 };
804
805 #[derive(Debug, Clone, Copy)]
806 enum SenderChainFault {
807 MissingPrivateKey,
808 PrivateKeyLength(usize),
809 MissingPublicKey,
810 MissingChainKey,
811 MissingChainKeyIndex,
812 MissingChainKeySecret,
813 }
814
815 fn public_key(seed: u8) -> Vec<u8> {
816 PublicKey::from_djb_public_key_bytes(&[seed; PublicKey::RAW_KEY_LEN])
817 .expect("valid test key")
818 .serialize()
819 .to_vec()
820 }
821
822 fn canonical_session_state() -> SessionState {
823 let mut rng = rand::make_rng::<rand::rngs::StdRng>();
824 let local_identity = IdentityKey::new(KeyPair::generate(&mut rng).public_key);
825 let remote_identity = IdentityKey::new(KeyPair::generate(&mut rng).public_key);
826 let base_key = KeyPair::generate(&mut rng).public_key;
827 let mut state = SessionState::new(
828 3,
829 &local_identity,
830 &remote_identity,
831 &RootKey::new([3; SYMMETRIC_KEY_BYTES]),
832 &base_key,
833 );
834 state.set_sender_chain(
835 &KeyPair::generate(&mut rng),
836 &ChainKey::new([4; SYMMETRIC_KEY_BYTES], 5),
837 );
838 state.add_receiver_chain(
839 &KeyPair::generate(&mut rng).public_key,
840 &ChainKey::new([6; SYMMETRIC_KEY_BYTES], 7),
841 );
842 state
843 }
844
845 fn canonical_session_components() -> SessionRecordComponents {
846 SessionRecord::new(canonical_session_state())
847 .into_components()
848 .expect("canonical session projects")
849 }
850
851 fn sender_chain_faults() -> [SenderChainFault; 8] {
852 [
853 SenderChainFault::MissingPrivateKey,
854 SenderChainFault::PrivateKeyLength(0),
855 SenderChainFault::PrivateKeyLength(PRIVATE_KEY_BYTES - 1),
856 SenderChainFault::PrivateKeyLength(PRIVATE_KEY_BYTES + 1),
857 SenderChainFault::MissingPublicKey,
858 SenderChainFault::MissingChainKey,
859 SenderChainFault::MissingChainKeyIndex,
860 SenderChainFault::MissingChainKeySecret,
861 ]
862 }
863
864 fn corrupt_sender_components(
865 components: &mut SessionRecordComponents,
866 fault: SenderChainFault,
867 ) {
868 let sender = components
869 .current_session
870 .as_mut()
871 .and_then(|session| session.sender_chain.as_mut())
872 .expect("canonical sender chain");
873 match fault {
874 SenderChainFault::MissingPrivateKey => sender.sender_ratchet_key_private = None,
875 SenderChainFault::PrivateKeyLength(length) => {
876 sender.sender_ratchet_key_private = Some(vec![0; length]);
877 }
878 SenderChainFault::MissingPublicKey => sender.sender_ratchet_key = None,
879 SenderChainFault::MissingChainKey => sender.chain_key = None,
880 SenderChainFault::MissingChainKeyIndex => {
881 sender
882 .chain_key
883 .as_mut()
884 .expect("canonical chain key")
885 .index = None;
886 }
887 SenderChainFault::MissingChainKeySecret => {
888 sender.chain_key.as_mut().expect("canonical chain key").key = None;
889 }
890 }
891 }
892
893 fn corrupt_persisted_sender(session: &mut SessionStructure, fault: SenderChainFault) {
894 let sender = session
895 .sender_chain
896 .as_option_mut()
897 .expect("canonical sender chain");
898 match fault {
899 SenderChainFault::MissingPrivateKey => sender.sender_ratchet_key_private = None,
900 SenderChainFault::PrivateKeyLength(length) => {
901 sender.sender_ratchet_key_private = Some(vec![0; length]);
902 }
903 SenderChainFault::MissingPublicKey => sender.sender_ratchet_key = None,
904 SenderChainFault::MissingChainKey => sender.chain_key = MessageField::none(),
905 SenderChainFault::MissingChainKeyIndex => {
906 sender
907 .chain_key
908 .as_option_mut()
909 .expect("canonical chain key")
910 .index = None;
911 }
912 SenderChainFault::MissingChainKeySecret => {
913 sender
914 .chain_key
915 .as_option_mut()
916 .expect("canonical chain key")
917 .key = None;
918 }
919 }
920 }
921
922 fn session_record() -> SessionRecordComponents {
923 let current_session = SessionComponents {
924 session_version: Some(3),
925 local_identity_public: Some(public_key(1)),
926 remote_identity_public: Some(public_key(2)),
927 root_key: Some(vec![3; 32]),
928 previous_counter: Some(0),
929 sender_chain: Some(SessionChainComponents {
930 sender_ratchet_key: Some(public_key(4)),
931 sender_ratchet_key_private: Some(vec![5; 32]),
932 chain_key: Some(SessionChainKeyComponents {
933 index: Some(0),
934 key: Some(vec![6; 32]),
935 }),
936 message_keys: vec![SessionMessageKeyComponents {
937 index: 1,
938 material: SessionMessageKeyMaterial::Derived {
939 cipher_key: [16; 32],
940 mac_key: [17; 32],
941 iv: [18; 16],
942 },
943 }],
944 }),
945 receiver_chains: vec![SessionChainComponents {
946 sender_ratchet_key: Some(public_key(7)),
947 sender_ratchet_key_private: None,
948 chain_key: Some(SessionChainKeyComponents {
949 index: Some(2),
950 key: Some(vec![8; 32]),
951 }),
952 message_keys: vec![SessionMessageKeyComponents {
953 index: 3,
954 material: SessionMessageKeyMaterial::Derived {
955 cipher_key: [19; 32],
956 mac_key: [20; 32],
957 iv: [21; 16],
958 },
959 }],
960 }],
961 pending_key_exchange: Some(PendingKeyExchangeComponents {
962 sequence: Some(22),
963 local_base_key: Some(public_key(23)),
964 local_base_key_private: Some(vec![24; 32]),
965 local_ratchet_key: Some(public_key(25)),
966 local_ratchet_key_private: Some(vec![26; 32]),
967 local_identity_key: Some(public_key(27)),
968 local_identity_key_private: Some(vec![28; 32]),
969 }),
970 pending_pre_key: Some(PendingPreKeyComponents {
971 pre_key_id: Some(29),
972 signed_pre_key_id: Some(30),
973 base_key: Some(public_key(31)),
974 kyber_pre_key_id: Some(32),
975 kyber_ciphertext: Some(vec![33; 48]),
976 }),
977 remote_registration_id: Some(9),
978 local_registration_id: Some(10),
979 needs_refresh: Some(false),
980 alice_base_key: Some(public_key(11)),
981 };
982 SessionRecordComponents {
983 current_session: Some(current_session.clone()),
984 previous_sessions: vec![current_session],
985 }
986 }
987
988 fn sender_key_record() -> SenderKeyRecordComponents {
989 SenderKeyRecordComponents {
990 states: vec![
991 SenderKeyStateComponents {
992 key_id: 17,
993 chain_key: SenderChainKeyComponents {
994 iteration: 0,
995 seed: vec![12; 32],
996 },
997 signing_key: SenderSigningKeyComponents {
998 public: public_key(13),
999 private: Some(vec![14; 32]),
1000 },
1001 message_keys: vec![SenderMessageKeyComponents {
1002 iteration: 3,
1003 seed: vec![15; 32],
1004 }],
1005 },
1006 SenderKeyStateComponents {
1007 key_id: 18,
1008 chain_key: SenderChainKeyComponents {
1009 iteration: 4,
1010 seed: vec![16; 32],
1011 },
1012 signing_key: SenderSigningKeyComponents {
1013 public: public_key(17),
1014 private: None,
1015 },
1016 message_keys: Vec::new(),
1017 },
1018 ],
1019 }
1020 }
1021
1022 #[test]
1023 fn raw_public_keys_are_normalized_once() {
1024 let normalized = normalize_public_key(vec![7; 32], "test key").expect("valid key");
1025 assert_eq!(normalized.len(), PublicKey::SERIALIZED_KEY_LEN);
1026 assert_eq!(normalized[0], crate::core::curve::KeyType::Djb.value());
1027 assert_eq!(&normalized[1..], &[7; 32]);
1028 }
1029
1030 #[test]
1031 fn invalid_serialized_public_key_type_is_rejected() {
1032 let mut key = public_key(7);
1033 key[0] = u8::MAX;
1034
1035 assert!(normalize_public_key(key, "test key").is_err());
1036 }
1037
1038 #[test]
1039 fn debug_output_redacts_key_material() {
1040 let chain = SessionChainKeyComponents {
1041 index: Some(7),
1042 key: Some(vec![42; 32]),
1043 };
1044 let material = SessionMessageKeyMaterial::Derived {
1045 cipher_key: [1; 32],
1046 mac_key: [2; 32],
1047 iv: [3; 16],
1048 };
1049
1050 assert_eq!(
1051 format!("{chain:?}"),
1052 "SessionChainKeyComponents { index: Some(7), key: <redacted> }"
1053 );
1054 assert_eq!(
1055 format!("{material:?}"),
1056 "Derived { cipher_key: <redacted>, mac_key: <redacted>, iv: <redacted> }"
1057 );
1058 }
1059
1060 #[test]
1061 fn message_key_seed_uses_the_existing_derivation() {
1062 let seed = [9; 32];
1063 let expected = MessageKeyGenerator::new_from_seed(&seed, 17).into_pb();
1064 let actual = SessionMessageKeyComponents {
1065 index: 17,
1066 material: SessionMessageKeyMaterial::Seed(seed),
1067 }
1068 .into_structure();
1069
1070 assert_eq!(actual, expected);
1071 }
1072
1073 #[test]
1076 fn seed_material_survives_the_record_round_trip() {
1077 let expected = SessionMessageKeyComponents {
1078 index: 17,
1079 material: SessionMessageKeyMaterial::Seed([9; SYMMETRIC_KEY_BYTES]),
1080 };
1081 let actual = SessionMessageKeyComponents::from_structure(expected.clone().into_structure())
1082 .expect("valid persisted key");
1083
1084 assert_eq!(actual, expected);
1085 }
1086
1087 #[test]
1090 fn a_persisted_key_without_a_seed_projects_as_derived() {
1091 let mut persisted = SessionMessageKeyComponents {
1092 index: 3,
1093 material: SessionMessageKeyMaterial::Seed([9; SYMMETRIC_KEY_BYTES]),
1094 }
1095 .into_structure();
1096 persisted.seed = None;
1097
1098 let actual =
1099 SessionMessageKeyComponents::from_structure(persisted).expect("seedless key projects");
1100
1101 assert!(matches!(
1102 actual.material,
1103 SessionMessageKeyMaterial::Derived { .. }
1104 ));
1105 }
1106
1107 #[test]
1110 fn a_malformed_persisted_seed_is_rejected() {
1111 for length in [0, SYMMETRIC_KEY_BYTES - 1, SYMMETRIC_KEY_BYTES + 1] {
1112 let mut persisted = SessionMessageKeyComponents {
1113 index: 3,
1114 material: SessionMessageKeyMaterial::Seed([9; SYMMETRIC_KEY_BYTES]),
1115 }
1116 .into_structure();
1117 persisted.seed = Some(Bytes::from(vec![0; length]));
1118
1119 let error = SessionMessageKeyComponents::from_structure(persisted)
1120 .expect_err("malformed seed must fail");
1121 assert!(
1122 matches!(error, SignalProtocolError::InvalidArgument(_)),
1123 "{length}-byte seed: {error}"
1124 );
1125 }
1126 }
1127
1128 #[test]
1131 fn a_persisted_seed_that_derives_other_keys_is_rejected() {
1132 let mut persisted = SessionMessageKeyComponents {
1133 index: 3,
1134 material: SessionMessageKeyMaterial::Seed([9; SYMMETRIC_KEY_BYTES]),
1135 }
1136 .into_structure();
1137 persisted.seed = Some(Bytes::copy_from_slice(&[10; SYMMETRIC_KEY_BYTES]));
1138
1139 let error = SessionMessageKeyComponents::from_structure(persisted)
1140 .expect_err("inconsistent seed must fail");
1141 assert!(matches!(error, SignalProtocolError::InvalidArgument(_)));
1142 }
1143
1144 #[test]
1145 fn canonical_session_api_round_trips_through_components() {
1146 let state = canonical_session_state();
1147 let structure = SessionStructure::from(&state);
1148 assert_eq!(
1149 structure.receiver_chains[0].sender_ratchet_key_private,
1150 Some(Vec::new())
1151 );
1152 assert_eq!(
1153 structure
1154 .sender_chain
1155 .as_option()
1156 .and_then(|chain| chain.sender_ratchet_key_private.as_ref())
1157 .map(Vec::len),
1158 Some(PRIVATE_KEY_BYTES)
1159 );
1160
1161 let persisted = SessionRecord::new(state)
1162 .serialize()
1163 .expect("serialize canonical record");
1164 let projected = SessionRecord::deserialize(&persisted)
1165 .expect("deserialize canonical record")
1166 .into_components()
1167 .expect("project canonical record");
1168 let session = projected
1169 .current_session
1170 .as_ref()
1171 .expect("test session is present");
1172 assert_eq!(session.receiver_chains[0].sender_ratchet_key_private, None);
1173 assert_eq!(
1174 session
1175 .sender_chain
1176 .as_ref()
1177 .and_then(|chain| chain.sender_ratchet_key_private.as_ref())
1178 .map(Vec::len),
1179 Some(PRIVATE_KEY_BYTES)
1180 );
1181
1182 let rebuilt = SessionRecord::from_components(projected.clone())
1183 .expect("rebuild canonical record")
1184 .serialize()
1185 .expect("serialize rebuilt record");
1186 let reprojected = SessionRecord::deserialize(&rebuilt)
1187 .expect("deserialize rebuilt record")
1188 .into_components()
1189 .expect("project rebuilt record");
1190 assert_eq!(reprojected, projected);
1191 }
1192
1193 #[test]
1194 fn persisted_receiver_private_material_is_ignored() {
1195 for (case, private_key) in [
1196 ("absent", None),
1197 ("empty", Some(Vec::new())),
1198 ("one byte", Some(vec![0; 1])),
1199 ("short key", Some(vec![0; PRIVATE_KEY_BYTES - 1])),
1200 ("key-sized", Some(vec![0; PRIVATE_KEY_BYTES])),
1201 ("long key", Some(vec![0; PRIVATE_KEY_BYTES + 1])),
1202 ] {
1203 let mut persisted = SessionStructure::from(canonical_session_state());
1204 persisted.receiver_chains[0].sender_ratchet_key_private = private_key;
1205
1206 let projected = session_components_from_structure(persisted).unwrap_or_else(|error| {
1207 panic!("{case} receiver material must be ignored: {error}")
1208 });
1209 assert_eq!(
1210 projected.receiver_chains[0].sender_ratchet_key_private, None,
1211 "{case}"
1212 );
1213 }
1214 }
1215
1216 #[test]
1217 fn receiver_component_import_rejects_private_material() {
1218 for private_key in [Vec::new(), vec![0; PRIVATE_KEY_BYTES]] {
1219 let mut components = canonical_session_components();
1220 components
1221 .current_session
1222 .as_mut()
1223 .expect("canonical session")
1224 .receiver_chains[0]
1225 .sender_ratchet_key_private = Some(private_key);
1226
1227 let error = SessionRecord::from_components(components)
1228 .err()
1229 .expect("receiver components must not carry private material");
1230 assert!(matches!(error, SignalProtocolError::InvalidArgument(_)));
1231 }
1232 }
1233
1234 #[test]
1235 fn sender_component_import_requires_a_complete_chain() {
1236 for fault in sender_chain_faults() {
1237 let mut components = canonical_session_components();
1238 corrupt_sender_components(&mut components, fault);
1239
1240 let error = SessionRecord::from_components(components)
1241 .err()
1242 .unwrap_or_else(|| panic!("sender fault {fault:?} must fail"));
1243 assert!(
1244 matches!(error, SignalProtocolError::InvalidArgument(_)),
1245 "{fault:?}: {error}"
1246 );
1247 }
1248 }
1249
1250 #[test]
1251 fn persisted_sender_projection_requires_a_complete_chain() {
1252 for fault in sender_chain_faults() {
1253 let mut persisted = SessionStructure::from(canonical_session_state());
1254 corrupt_persisted_sender(&mut persisted, fault);
1255
1256 let error = session_components_from_structure(persisted)
1257 .err()
1258 .unwrap_or_else(|| panic!("persisted sender fault {fault:?} must fail"));
1259 assert!(
1260 matches!(error, SignalProtocolError::InvalidArgument(_)),
1261 "{fault:?}: {error}"
1262 );
1263 }
1264 }
1265
1266 #[test]
1267 fn component_import_allows_an_absent_sender_chain() {
1268 let mut components = canonical_session_components();
1269 components
1270 .current_session
1271 .as_mut()
1272 .expect("canonical session")
1273 .sender_chain = None;
1274
1275 SessionRecord::from_components(components)
1276 .expect("intermediate state without sender chain");
1277 }
1278
1279 #[test]
1280 fn session_components_use_the_canonical_record_codec() {
1281 let expected = session_record();
1282 let bytes = SessionRecord::from_components(expected.clone())
1283 .expect("valid components")
1284 .serialize()
1285 .expect("serialize record");
1286 let actual = SessionRecord::deserialize(&bytes)
1287 .expect("deserialize record")
1288 .into_components()
1289 .expect("project record");
1290
1291 assert_eq!(actual, expected);
1292 }
1293
1294 #[test]
1295 fn session_components_bound_archived_state_count() {
1296 let mut components = session_record();
1297 let archived = components
1298 .current_session
1299 .clone()
1300 .expect("test session is present");
1301 components.previous_sessions =
1302 vec![archived; crate::protocol::consts::ARCHIVED_STATES_MAX_LENGTH + 1];
1303
1304 let actual = SessionRecord::from_components(components)
1305 .expect("valid components")
1306 .into_components()
1307 .expect("project record");
1308
1309 assert_eq!(
1310 actual.previous_sessions.len(),
1311 crate::protocol::consts::ARCHIVED_STATES_MAX_LENGTH
1312 );
1313 }
1314
1315 #[test]
1316 fn session_handoff_burns_the_reserved_sender_range_in_all_states() {
1317 let mut record = SessionRecord::from_components(session_record()).expect("valid record");
1318 record.reserve_sender_chain_counters(0);
1319 let ceiling = record.reserved_sender_chain_index();
1320 let components = record.into_components().expect("safe handoff");
1321 let index = components
1322 .current_session
1323 .as_ref()
1324 .and_then(|session| session.sender_chain.as_ref())
1325 .and_then(|chain| chain.chain_key.as_ref())
1326 .and_then(|chain| chain.index);
1327 let archived_indexes: Vec<_> = components
1328 .previous_sessions
1329 .iter()
1330 .map(|session| {
1331 session
1332 .sender_chain
1333 .as_ref()
1334 .and_then(|chain| chain.chain_key.as_ref())
1335 .and_then(|chain| chain.index)
1336 })
1337 .collect();
1338
1339 assert_eq!(index, Some(ceiling));
1340 assert_eq!(archived_indexes, vec![Some(ceiling)]);
1341 }
1342
1343 #[test]
1344 fn session_handoff_drops_unadvanceable_chains_without_losing_the_record() {
1345 let mut record = SessionRecord::from_components(session_record()).expect("valid record");
1346 record.reserve_sender_chain_counters(
1347 crate::protocol::consts::MAX_RESERVATION_FAST_FORWARD + 1,
1348 );
1349
1350 let components = record
1351 .into_components()
1352 .expect("unadvanceable chains must not consume the record on error");
1353
1354 assert!(
1355 components
1356 .current_session
1357 .as_ref()
1358 .is_some_and(|session| session.sender_chain.is_none())
1359 );
1360 assert_eq!(components.previous_sessions.len(), 1);
1361 assert!(components.previous_sessions[0].sender_chain.is_none());
1362 }
1363
1364 #[test]
1365 fn sender_key_components_use_the_canonical_record_codec() {
1366 let expected = sender_key_record();
1367 let bytes = SenderKeyRecord::from_components(expected.clone())
1368 .expect("valid components")
1369 .serialize()
1370 .expect("serialize record");
1371 let actual = SenderKeyRecord::deserialize(&bytes)
1372 .expect("deserialize record")
1373 .into_components()
1374 .expect("project record");
1375
1376 assert_eq!(actual, expected);
1377 }
1378
1379 #[test]
1380 fn sender_key_components_bound_state_history() {
1381 let mut components = sender_key_record();
1382 let state = components.states[0].clone();
1383 components.states = vec![state; crate::protocol::consts::MAX_SENDER_KEY_STATES + 1];
1384
1385 let actual = SenderKeyRecord::from_components(components)
1386 .expect("valid components")
1387 .into_components()
1388 .expect("project record");
1389
1390 assert_eq!(
1391 actual.states.len(),
1392 crate::protocol::consts::MAX_SENDER_KEY_STATES
1393 );
1394 }
1395
1396 #[test]
1397 fn sender_key_handoff_burns_the_reserved_iteration_range() {
1398 let mut record =
1399 SenderKeyRecord::from_components(sender_key_record()).expect("valid record");
1400 record.reserve_iterations(0);
1401 let ceiling = record.reserved_iteration();
1402 let components = record.into_components().expect("safe handoff");
1403
1404 assert_eq!(components.states[0].chain_key.iteration, ceiling);
1405 }
1406}