Skip to main content

libsignal_protocol/state/
session.rs

1//
2// Copyright 2020-2022 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6use std::result::Result;
7use std::time::{Duration, SystemTime};
8
9use bitflags::bitflags;
10use prost::Message;
11#[cfg(test)]
12use rand::{CryptoRng, Rng};
13use subtle::ConstantTimeEq;
14
15use crate::proto::storage::{RecordStructure, SessionStructure, session_structure};
16use crate::protocol::CIPHERTEXT_MESSAGE_PRE_KYBER_VERSION;
17#[cfg(test)]
18use crate::ratchet::MessageKeyGenerator;
19use crate::ratchet::{ChainKey, RootKey};
20use crate::state::{KyberPreKeyId, PreKeyId, SignedPreKeyId};
21use crate::{
22    IdentityKey, KeyPair, PrivateKey, PublicKey, SessionNotFound, SignalProtocolError, consts, kem,
23};
24
25/// A distinct error type to keep from accidentally propagating deserialization errors.
26#[derive(Debug)]
27pub(crate) struct InvalidSessionError(pub(crate) &'static str);
28
29impl std::fmt::Display for InvalidSessionError {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        self.0.fmt(f)
32    }
33}
34
35impl From<InvalidSessionError> for SignalProtocolError {
36    fn from(e: InvalidSessionError) -> Self {
37        Self::InvalidSessionStructure(e.0)
38    }
39}
40
41#[derive(Debug, Clone)]
42pub(crate) struct UnacknowledgedPreKeyMessageItems<'a> {
43    pre_key_id: Option<PreKeyId>,
44    signed_pre_key_id: SignedPreKeyId,
45    base_key: PublicKey,
46    // Although we require PQXDH for all new sessions now,
47    // we may in theory have an existing X3DH unacknowledged session,
48    // so we leave these optional for now.
49    kyber_pre_key_id: Option<KyberPreKeyId>,
50    kyber_ciphertext: Option<&'a [u8]>,
51    timestamp: SystemTime,
52}
53
54impl<'a> UnacknowledgedPreKeyMessageItems<'a> {
55    fn new(
56        pre_key_id: Option<PreKeyId>,
57        signed_pre_key_id: SignedPreKeyId,
58        base_key: PublicKey,
59        pending_kyber_pre_key: Option<&'a session_structure::PendingKyberPreKey>,
60        timestamp: SystemTime,
61    ) -> Self {
62        let (kyber_pre_key_id, kyber_ciphertext) = pending_kyber_pre_key
63            .map(|pending| (pending.pre_key_id.into(), pending.ciphertext.as_slice()))
64            .unzip();
65        Self {
66            pre_key_id,
67            signed_pre_key_id,
68            base_key,
69            kyber_pre_key_id,
70            kyber_ciphertext,
71            timestamp,
72        }
73    }
74
75    pub(crate) fn pre_key_id(&self) -> Option<PreKeyId> {
76        self.pre_key_id
77    }
78
79    pub(crate) fn signed_pre_key_id(&self) -> SignedPreKeyId {
80        self.signed_pre_key_id
81    }
82
83    pub(crate) fn base_key(&self) -> &PublicKey {
84        &self.base_key
85    }
86
87    pub(crate) fn kyber_pre_key_id(&self) -> Option<KyberPreKeyId> {
88        self.kyber_pre_key_id
89    }
90
91    pub(crate) fn kyber_ciphertext(&self) -> Option<&'a [u8]> {
92        self.kyber_ciphertext
93    }
94
95    pub(crate) fn timestamp(&self) -> SystemTime {
96        self.timestamp
97    }
98}
99
100bitflags! {
101    /// Specifies which criteria make a session "usable" beyond simply having a present sender
102    /// chain.
103    ///
104    /// These requirements are conjunctive, i.e. specifying `NotStale | EstablishedWithPqxdh` means
105    /// the session must be neither stale nor established with X3DH.
106    ///
107    /// This struct is generated using the `bitflags` crate; the [`Flags`](::bitflags::Flags) trait
108    /// provides most of its API surface. It can also use "classic" C bitflag syntax, with `|` for
109    /// union and `&` for intersection (as shown above).
110    #[repr(transparent)]
111    #[derive(Clone, Copy, PartialEq, Eq)]
112    pub struct SessionUsabilityRequirements : u32 {
113        /// Requires that a session not be stale.
114        ///
115        /// A non-stale session is one of the following:
116        /// - "incoming", i.e. started by the peer
117        /// - "acknowledged", i.e. started locally but received a response
118        /// - no more than a few weeks old (the exact time is chosen by libsignal)
119        const NotStale = 1 << 0;
120        /// Requires that a session was established using PQXDH (or newer) rather than X3DH/X4DH.
121        ///
122        /// This includes unacknowledged sessions that are using PQXDH, since if they get a
123        /// response, the peer is confirmed to be using PQXDH as well.
124        const EstablishedWithPqxdh = 1 << 1;
125        /// Requires that a session is using SPQR.
126        ///
127        /// **Warning:** This allows unacknowledged sessions that include SPQR in their PreKey
128        /// messages. If the peer downgrades the session (by discarding the SPQR information) and
129        /// the local client allows it, a session that is previously considered "usable" can become
130        /// "not usable" upon receiving a response. Therefore, this should not be used to determine
131        /// whether a session is usable unless future downgrades will also be rejected.
132        const Spqr = 1 << 2;
133    }
134}
135
136#[derive(Clone, Debug)]
137pub(crate) struct SessionState {
138    session: SessionStructure,
139}
140
141impl SessionState {
142    pub(crate) fn from_session_structure(session: SessionStructure) -> Self {
143        Self { session }
144    }
145
146    pub(crate) fn new(
147        version: u8,
148        our_identity: &IdentityKey,
149        their_identity: &IdentityKey,
150        root_key: &RootKey,
151        alice_base_key: &PublicKey,
152        pq_ratchet_state: spqr::SerializedState,
153    ) -> Self {
154        Self {
155            session: SessionStructure {
156                session_version: version as u32,
157                local_identity_public: our_identity.public_key().serialize().into_vec(),
158                remote_identity_public: their_identity.serialize().into_vec(),
159                root_key: root_key.key().to_vec(),
160                previous_counter: 0,
161                sender_chain: None,
162                receiver_chains: vec![],
163                pending_pre_key: None,
164                pending_kyber_pre_key: None,
165                remote_registration_id: 0,
166                local_registration_id: 0,
167                alice_base_key: alice_base_key.serialize().into_vec(),
168                pq_ratchet_state,
169            },
170        }
171    }
172
173    pub(crate) fn alice_base_key(&self) -> &[u8] {
174        // Check the length before returning?
175        &self.session.alice_base_key
176    }
177
178    pub(crate) fn session_version(&self) -> Result<u32, InvalidSessionError> {
179        match self.session.session_version {
180            0 => Ok(2),
181            v => Ok(v),
182        }
183    }
184
185    pub(crate) fn remote_identity_key(&self) -> Result<Option<IdentityKey>, InvalidSessionError> {
186        match self.session.remote_identity_public.len() {
187            0 => Ok(None),
188            _ => Ok(Some(
189                IdentityKey::decode(&self.session.remote_identity_public)
190                    .map_err(|_| InvalidSessionError("invalid remote identity key"))?,
191            )),
192        }
193    }
194
195    pub(crate) fn remote_identity_key_bytes(&self) -> Result<Option<Vec<u8>>, InvalidSessionError> {
196        Ok(self.remote_identity_key()?.map(|k| k.serialize().to_vec()))
197    }
198
199    pub(crate) fn local_identity_key(&self) -> Result<IdentityKey, InvalidSessionError> {
200        IdentityKey::decode(&self.session.local_identity_public)
201            .map_err(|_| InvalidSessionError("invalid local identity key"))
202    }
203
204    pub(crate) fn local_identity_key_bytes(&self) -> Result<Vec<u8>, InvalidSessionError> {
205        Ok(self.local_identity_key()?.serialize().to_vec())
206    }
207
208    #[deprecated]
209    #[allow(unused)]
210    pub(crate) fn session_with_self(&self) -> Result<bool, InvalidSessionError> {
211        if let Some(remote_id) = self.remote_identity_key_bytes()? {
212            let local_id = self.local_identity_key_bytes()?;
213            return Ok(remote_id == local_id);
214        }
215
216        // If remote ID is not set then we can't be sure but treat as non-self
217        Ok(false)
218    }
219
220    pub(crate) fn previous_counter(&self) -> u32 {
221        self.session.previous_counter
222    }
223
224    #[cfg(test)]
225    pub(crate) fn set_previous_counter(&mut self, ctr: u32) {
226        self.session.previous_counter = ctr;
227    }
228
229    #[cfg(test)]
230    pub(crate) fn root_key(&self) -> Result<RootKey, InvalidSessionError> {
231        let root_key_bytes = self.session.root_key[..]
232            .try_into()
233            .map_err(|_| InvalidSessionError("invalid root key"))?;
234        Ok(RootKey::new(root_key_bytes))
235    }
236
237    #[cfg(test)]
238    pub(crate) fn set_root_key(&mut self, root_key: &RootKey) {
239        self.session.root_key = root_key.key().to_vec();
240    }
241
242    pub(crate) fn sender_ratchet_key(&self) -> Result<PublicKey, InvalidSessionError> {
243        match self.session.sender_chain {
244            None => Err(InvalidSessionError("missing sender chain")),
245            Some(ref c) => PublicKey::deserialize(&c.sender_ratchet_key)
246                .map_err(|_| InvalidSessionError("invalid sender chain ratchet key")),
247        }
248    }
249
250    pub(crate) fn sender_ratchet_key_for_logging(&self) -> Result<String, InvalidSessionError> {
251        Ok(hex::encode(self.sender_ratchet_key()?.public_key_bytes()))
252    }
253
254    pub(crate) fn sender_ratchet_private_key(&self) -> Result<PrivateKey, InvalidSessionError> {
255        match self.session.sender_chain {
256            None => Err(InvalidSessionError("missing sender chain")),
257            Some(ref c) => PrivateKey::deserialize(&c.sender_ratchet_key_private)
258                .map_err(|_| InvalidSessionError("invalid sender chain private ratchet key")),
259        }
260    }
261
262    pub fn has_usable_sender_chain(
263        &self,
264        now: SystemTime,
265        requirements: SessionUsabilityRequirements,
266    ) -> Result<bool, InvalidSessionError> {
267        if self.session.sender_chain.is_none() {
268            return Ok(false);
269        }
270        if requirements.contains(SessionUsabilityRequirements::NotStale) {
271            if let Some(pending_pre_key) = &self.session.pending_pre_key {
272                let creation_timestamp =
273                    SystemTime::UNIX_EPOCH + Duration::from_secs(pending_pre_key.timestamp);
274                if creation_timestamp + consts::MAX_UNACKNOWLEDGED_SESSION_AGE < now {
275                    return Ok(false);
276                }
277            }
278        }
279        #[allow(clippy::collapsible_if)]
280        if requirements.contains(SessionUsabilityRequirements::EstablishedWithPqxdh) {
281            if self.session_version()? <= CIPHERTEXT_MESSAGE_PRE_KYBER_VERSION.into() {
282                return Ok(false);
283            }
284        }
285        #[allow(clippy::collapsible_if)]
286        if requirements.contains(SessionUsabilityRequirements::Spqr) {
287            if self.pq_ratchet_state().is_empty() {
288                return Ok(false);
289            }
290        }
291        Ok(true)
292    }
293
294    pub(crate) fn all_receiver_chain_logging_info(&self) -> Vec<(Vec<u8>, Option<u32>)> {
295        let mut results = vec![];
296        for chain in self.session.receiver_chains.iter() {
297            let sender_ratchet_public = chain.sender_ratchet_key.clone();
298
299            let chain_key_idx = chain.chain_key.as_ref().map(|chain_key| chain_key.index);
300
301            results.push((sender_ratchet_public, chain_key_idx))
302        }
303        results
304    }
305
306    pub(crate) fn get_receiver_chain(
307        &self,
308        sender: &PublicKey,
309    ) -> Result<Option<(session_structure::Chain, usize)>, InvalidSessionError> {
310        for (idx, chain) in self.session.receiver_chains.iter().enumerate() {
311            // If we compared bytes directly it would be faster, but may miss non-canonical points.
312            // It's unclear if supporting such points is desirable.
313            let chain_ratchet_key = PublicKey::deserialize(&chain.sender_ratchet_key)
314                .map_err(|_| InvalidSessionError("invalid receiver chain ratchet key"))?;
315
316            if &chain_ratchet_key == sender {
317                return Ok(Some((chain.clone(), idx)));
318            }
319        }
320
321        Ok(None)
322    }
323
324    pub(crate) fn get_receiver_chain_key(
325        &self,
326        sender: &PublicKey,
327    ) -> Result<Option<ChainKey>, InvalidSessionError> {
328        match self.get_receiver_chain(sender)? {
329            None => Ok(None),
330            Some((chain, _)) => match chain.chain_key {
331                None => Err(InvalidSessionError("missing receiver chain key")),
332                Some(c) => {
333                    let chain_key_bytes = c.key[..]
334                        .try_into()
335                        .map_err(|_| InvalidSessionError("invalid receiver chain key"))?;
336                    Ok(Some(ChainKey::new(chain_key_bytes, c.index)))
337                }
338            },
339        }
340    }
341
342    pub(crate) fn add_receiver_chain(&mut self, sender: &PublicKey, chain_key: &ChainKey) {
343        let chain_key = session_structure::chain::ChainKey {
344            index: chain_key.index(),
345            key: chain_key.key().to_vec(),
346        };
347
348        let chain = session_structure::Chain {
349            sender_ratchet_key: sender.serialize().to_vec(),
350            sender_ratchet_key_private: vec![],
351            chain_key: Some(chain_key),
352            message_keys: vec![],
353        };
354
355        self.session.receiver_chains.push(chain);
356
357        if self.session.receiver_chains.len() > consts::MAX_RECEIVER_CHAINS {
358            log::info!(
359                "Trimming excessive receiver_chain for session with base key {}, chain count: {}",
360                self.sender_ratchet_key_for_logging()
361                    .unwrap_or_else(|e| format!("<error: {}>", e.0)),
362                self.session.receiver_chains.len()
363            );
364            self.session.receiver_chains.remove(0);
365        }
366    }
367
368    pub(crate) fn with_receiver_chain(mut self, sender: &PublicKey, chain_key: &ChainKey) -> Self {
369        self.add_receiver_chain(sender, chain_key);
370        self
371    }
372
373    pub(crate) fn set_sender_chain(&mut self, sender: &KeyPair, next_chain_key: &ChainKey) {
374        let chain_key = session_structure::chain::ChainKey {
375            index: next_chain_key.index(),
376            key: next_chain_key.key().to_vec(),
377        };
378
379        let new_chain = session_structure::Chain {
380            sender_ratchet_key: sender.public_key.serialize().to_vec(),
381            sender_ratchet_key_private: sender.private_key.serialize().to_vec(),
382            chain_key: Some(chain_key),
383            message_keys: vec![],
384        };
385
386        self.session.sender_chain = Some(new_chain);
387    }
388
389    pub(crate) fn with_sender_chain(mut self, sender: &KeyPair, next_chain_key: &ChainKey) -> Self {
390        self.set_sender_chain(sender, next_chain_key);
391        self
392    }
393
394    pub(crate) fn get_sender_chain_key(&self) -> Result<ChainKey, InvalidSessionError> {
395        let sender_chain = self
396            .session
397            .sender_chain
398            .as_ref()
399            .ok_or(InvalidSessionError("missing sender chain"))?;
400
401        let chain_key = sender_chain
402            .chain_key
403            .as_ref()
404            .ok_or(InvalidSessionError("missing sender chain key"))?;
405
406        let chain_key_bytes = chain_key.key[..]
407            .try_into()
408            .map_err(|_| InvalidSessionError("invalid sender chain key"))?;
409
410        Ok(ChainKey::new(chain_key_bytes, chain_key.index))
411    }
412
413    pub(crate) fn get_sender_chain_key_bytes(&self) -> Result<Vec<u8>, InvalidSessionError> {
414        Ok(self.get_sender_chain_key()?.key().to_vec())
415    }
416
417    pub(crate) fn set_sender_chain_key(&mut self, next_chain_key: &ChainKey) {
418        let chain_key = session_structure::chain::ChainKey {
419            index: next_chain_key.index(),
420            key: next_chain_key.key().to_vec(),
421        };
422
423        // Is it actually valid to call this function with sender_chain == None?
424
425        let new_chain = match self.session.sender_chain.take() {
426            None => session_structure::Chain {
427                sender_ratchet_key: vec![],
428                sender_ratchet_key_private: vec![],
429                chain_key: Some(chain_key),
430                message_keys: vec![],
431            },
432            Some(mut c) => {
433                c.chain_key = Some(chain_key);
434                c
435            }
436        };
437
438        self.session.sender_chain = Some(new_chain);
439    }
440
441    #[cfg(test)]
442    pub(crate) fn get_message_keys(
443        &mut self,
444        sender: &PublicKey,
445        counter: u32,
446    ) -> Result<Option<MessageKeyGenerator>, InvalidSessionError> {
447        if let Some(mut chain_and_index) = self.get_receiver_chain(sender)? {
448            let message_key_idx = chain_and_index
449                .0
450                .message_keys
451                .iter()
452                .position(|m| m.index == counter);
453
454            if let Some(position) = message_key_idx {
455                let message_key = chain_and_index.0.message_keys.remove(position);
456                let keys =
457                    MessageKeyGenerator::from_pb(message_key).map_err(InvalidSessionError)?;
458
459                // Update with message key removed
460                self.session.receiver_chains[chain_and_index.1] = chain_and_index.0;
461                return Ok(Some(keys));
462            }
463        }
464
465        Ok(None)
466    }
467
468    #[cfg(test)]
469    pub(crate) fn set_message_keys(
470        &mut self,
471        sender: &PublicKey,
472        message_keys: MessageKeyGenerator,
473    ) -> Result<(), InvalidSessionError> {
474        let chain_and_index = self
475            .get_receiver_chain(sender)?
476            .expect("called set_message_keys for a non-existent chain");
477        let mut updated_chain = chain_and_index.0;
478        updated_chain.message_keys.insert(0, message_keys.into_pb());
479
480        if updated_chain.message_keys.len() > consts::MAX_MESSAGE_KEYS {
481            updated_chain.message_keys.pop();
482        }
483
484        self.session.receiver_chains[chain_and_index.1] = updated_chain;
485
486        Ok(())
487    }
488
489    #[cfg(test)]
490    pub(crate) fn set_receiver_chain_key(
491        &mut self,
492        sender: &PublicKey,
493        chain_key: &ChainKey,
494    ) -> Result<(), InvalidSessionError> {
495        let chain_and_index = self
496            .get_receiver_chain(sender)?
497            .expect("called set_receiver_chain_key for a non-existent chain");
498        let mut updated_chain = chain_and_index.0;
499        updated_chain.chain_key = Some(session_structure::chain::ChainKey {
500            index: chain_key.index(),
501            key: chain_key.key().to_vec(),
502        });
503
504        self.session.receiver_chains[chain_and_index.1] = updated_chain;
505
506        Ok(())
507    }
508
509    pub(crate) fn set_unacknowledged_pre_key_message(
510        &mut self,
511        pre_key_id: Option<PreKeyId>,
512        signed_ec_pre_key_id: SignedPreKeyId,
513        base_key: &PublicKey,
514        now: SystemTime,
515    ) {
516        let signed_ec_pre_key_id: u32 = signed_ec_pre_key_id.into();
517        let pending = session_structure::PendingPreKey {
518            pre_key_id: pre_key_id.map(PreKeyId::into),
519            signed_pre_key_id: signed_ec_pre_key_id as i32,
520            base_key: base_key.serialize().to_vec(),
521            timestamp: now
522                .duration_since(SystemTime::UNIX_EPOCH)
523                .unwrap_or_default()
524                .as_secs(),
525        };
526        self.session.pending_pre_key = Some(pending);
527    }
528
529    pub(crate) fn set_kyber_ciphertext(&mut self, ciphertext: kem::SerializedCiphertext) {
530        let pending = session_structure::PendingKyberPreKey {
531            pre_key_id: u32::MAX, // has to be set to the actual value separately
532            ciphertext: ciphertext.into_vec(),
533        };
534        self.session.pending_kyber_pre_key = Some(pending);
535    }
536
537    pub(crate) fn set_unacknowledged_kyber_pre_key_id(
538        &mut self,
539        signed_kyber_pre_key_id: KyberPreKeyId,
540    ) {
541        let pending = self
542            .session
543            .pending_kyber_pre_key
544            .as_mut()
545            .expect("must have been set if kyber pre key is present");
546        pending.pre_key_id = signed_kyber_pre_key_id.into();
547    }
548
549    pub(crate) fn unacknowledged_pre_key_message_items(
550        &self,
551    ) -> Result<Option<UnacknowledgedPreKeyMessageItems<'_>>, InvalidSessionError> {
552        if let Some(ref pending_pre_key) = self.session.pending_pre_key {
553            Ok(Some(UnacknowledgedPreKeyMessageItems::new(
554                pending_pre_key.pre_key_id.map(Into::into),
555                (pending_pre_key.signed_pre_key_id as u32).into(),
556                PublicKey::deserialize(&pending_pre_key.base_key)
557                    .map_err(|_| InvalidSessionError("invalid pending PreKey message base key"))?,
558                self.session.pending_kyber_pre_key.as_ref(),
559                SystemTime::UNIX_EPOCH + Duration::from_secs(pending_pre_key.timestamp),
560            )))
561        } else {
562            Ok(None)
563        }
564    }
565
566    pub(crate) fn clear_unacknowledged_pre_key_message(&mut self) {
567        // Explicitly destructuring the SessionStructure in case there are new
568        // pending fields that need to be cleared.
569        let SessionStructure {
570            session_version: _session_version,
571            local_identity_public: _local_identity_public,
572            remote_identity_public: _remote_identity_public,
573            root_key: _root_key,
574            previous_counter: _previous_counter,
575            sender_chain: _sender_chain,
576            receiver_chains: _receiver_chains,
577            pending_pre_key: _pending_pre_key,
578            pending_kyber_pre_key: _pending_kyber_pre_key,
579            remote_registration_id: _remote_registration_id,
580            local_registration_id: _local_registration_id,
581            alice_base_key: _alice_base_key,
582            pq_ratchet_state: _pq_ratchet_state,
583        } = &self.session;
584        // ####### IMPORTANT #######
585        // Don't forget to clean up new pending fields.
586        // ####### IMPORTANT #######
587        self.session.pending_pre_key = None;
588        self.session.pending_kyber_pre_key = None;
589    }
590
591    pub(crate) fn set_remote_registration_id(&mut self, registration_id: u32) {
592        self.session.remote_registration_id = registration_id;
593    }
594
595    pub(crate) fn remote_registration_id(&self) -> u32 {
596        self.session.remote_registration_id
597    }
598
599    pub(crate) fn set_local_registration_id(&mut self, registration_id: u32) {
600        self.session.local_registration_id = registration_id;
601    }
602
603    pub(crate) fn local_registration_id(&self) -> u32 {
604        self.session.local_registration_id
605    }
606
607    pub(crate) fn get_kyber_ciphertext(&self) -> Option<&Vec<u8>> {
608        self.session
609            .pending_kyber_pre_key
610            .as_ref()
611            .map(|pending| &pending.ciphertext)
612    }
613
614    /// Extract the Double Ratchet state from this session.
615    ///
616    /// This **moves** the receiver chains out of the session protobuf,
617    /// leaving them empty. The caller must either apply the ratchet state
618    /// back via [`apply_ratchet_state`](Self::apply_ratchet_state) or
619    /// discard this `SessionState` entirely. Reading receiver chains from
620    /// a taken session is invalid and will produce incorrect results.
621    ///
622    /// The caller must supply `self_session` (whether this is a note-to-self
623    /// session) since it requires identity key comparison, which is above the
624    /// ratchet layer.
625    pub(crate) fn take_ratchet_state(
626        &mut self,
627        self_session: bool,
628    ) -> crate::error::Result<crate::double_ratchet::RatchetState> {
629        let receiver_chains = std::mem::take(&mut self.session.receiver_chains);
630        Ok(crate::double_ratchet::RatchetState::from_pb(
631            &self.session,
632            self_session,
633            receiver_chains,
634        )?)
635    }
636
637    /// Write modified Double Ratchet state back into this session.
638    ///
639    /// Only the ratchet-relevant fields are updated; all other session
640    /// fields (identity keys, pending pre-key, SPQR state, etc.) are
641    /// left unchanged.
642    pub(crate) fn apply_ratchet_state(&mut self, ratchet: crate::double_ratchet::RatchetState) {
643        ratchet.apply_to_pb(&mut self.session);
644    }
645
646    #[cfg(test)]
647    pub(crate) fn pq_ratchet_recv(
648        &mut self,
649        msg: &spqr::SerializedMessage,
650    ) -> Result<spqr::MessageKey, spqr::Error> {
651        let _trace = libsignal_debug::trace_block!("SessionState::pq_ratchet_recv");
652        let spqr::Recv { state, key } = spqr::recv(&self.session.pq_ratchet_state, msg)?;
653        self.session.pq_ratchet_state = state;
654        Ok(key)
655    }
656
657    #[cfg(test)]
658    pub(crate) fn pq_ratchet_send<R: Rng + CryptoRng>(
659        &mut self,
660        csprng: &mut R,
661    ) -> Result<(spqr::SerializedMessage, spqr::MessageKey), spqr::Error> {
662        let _trace = libsignal_debug::trace_block!("SessionState::pq_ratchet_send");
663        let spqr::Send { state, key, msg } = spqr::send(&self.session.pq_ratchet_state, csprng)?;
664        self.session.pq_ratchet_state = state;
665        Ok((msg, key))
666    }
667
668    pub(crate) fn pq_ratchet_state(&self) -> &spqr::SerializedState {
669        &self.session.pq_ratchet_state
670    }
671
672    /// Move the PQ ratchet state out, leaving an empty vec in its place.
673    ///
674    /// The caller must either write a new PQ ratchet state back via
675    /// [`set_pq_ratchet_state`](Self::set_pq_ratchet_state) or discard
676    /// this `SessionState` entirely.
677    pub(crate) fn take_pq_ratchet_state(&mut self) -> spqr::SerializedState {
678        std::mem::take(&mut self.session.pq_ratchet_state)
679    }
680
681    pub(crate) fn set_pq_ratchet_state(&mut self, state: spqr::SerializedState) {
682        self.session.pq_ratchet_state = state;
683    }
684}
685
686impl From<SessionStructure> for SessionState {
687    fn from(value: SessionStructure) -> SessionState {
688        SessionState::from_session_structure(value)
689    }
690}
691
692impl From<SessionState> for SessionStructure {
693    fn from(value: SessionState) -> SessionStructure {
694        value.session
695    }
696}
697
698impl From<&SessionState> for SessionStructure {
699    fn from(value: &SessionState) -> SessionStructure {
700        value.session.clone()
701    }
702}
703
704#[derive(Clone)]
705pub struct SessionRecord {
706    current_session: Option<SessionState>,
707    previous_sessions: Vec<Vec<u8>>,
708}
709
710impl SessionRecord {
711    pub fn new_fresh() -> Self {
712        Self {
713            current_session: None,
714            previous_sessions: Vec::new(),
715        }
716    }
717
718    pub(crate) fn new(state: SessionState) -> Self {
719        Self {
720            current_session: Some(state),
721            previous_sessions: Vec::new(),
722        }
723    }
724
725    pub fn deserialize(bytes: &[u8]) -> Result<Self, SignalProtocolError> {
726        let record = RecordStructure::decode(bytes)
727            .map_err(|_| InvalidSessionError("failed to decode session record protobuf"))?;
728
729        Ok(Self {
730            current_session: record.current_session.map(|s| s.into()),
731            previous_sessions: record.previous_sessions,
732        })
733    }
734
735    /// If there's a session with a matching version and `alice_base_key`, ensures that it is the
736    /// current session, promoting if necessary.
737    ///
738    /// Returns `Ok(true)` if such a session was found, `Ok(false)` if not, and
739    /// `Err(InvalidSessionError)` if an invalid session was found during the search (whether
740    /// current or not).
741    pub(crate) fn promote_matching_session(
742        &mut self,
743        version: u32,
744        alice_base_key: &[u8],
745    ) -> Result<bool, InvalidSessionError> {
746        if let Some(current_session) = &self.current_session {
747            if current_session.session_version()? == version
748                && alice_base_key
749                    .ct_eq(current_session.alice_base_key())
750                    .into()
751            {
752                return Ok(true);
753            }
754        }
755
756        let mut session_to_promote = None;
757        for (i, previous) in self.previous_session_states().enumerate() {
758            let previous = previous?;
759            if previous.session_version()? == version
760                && alice_base_key.ct_eq(previous.alice_base_key()).into()
761            {
762                session_to_promote = Some((i, previous));
763                break;
764            }
765        }
766
767        if let Some((i, state)) = session_to_promote {
768            self.promote_old_session(i, state);
769            return Ok(true);
770        }
771
772        Ok(false)
773    }
774
775    pub(crate) fn session_state(&self) -> Option<&SessionState> {
776        self.current_session.as_ref()
777    }
778
779    pub(crate) fn session_state_mut(&mut self) -> Option<&mut SessionState> {
780        self.current_session.as_mut()
781    }
782
783    pub(crate) fn set_session_state(&mut self, session: SessionState) {
784        self.current_session = Some(session);
785    }
786
787    pub(crate) fn previous_session_states(
788        &self,
789    ) -> impl ExactSizeIterator<Item = Result<SessionState, InvalidSessionError>> + '_ {
790        self.previous_sessions.iter().map(|bytes| {
791            Ok(SessionStructure::decode(&bytes[..])
792                .map_err(|_| InvalidSessionError("failed to decode previous session protobuf"))?
793                .into())
794        })
795    }
796
797    pub(crate) fn promote_old_session(
798        &mut self,
799        old_session: usize,
800        updated_session: SessionState,
801    ) {
802        self.previous_sessions.remove(old_session);
803        self.promote_state(updated_session)
804    }
805
806    pub(crate) fn promote_state(&mut self, new_state: SessionState) {
807        self.archive_current_state_inner();
808        self.current_session = Some(new_state);
809    }
810
811    // A non-fallible version of archive_current_state.
812    //
813    // Returns `true` if there was a session to archive, `false` if not.
814    fn archive_current_state_inner(&mut self) -> bool {
815        if let Some(mut current_session) = self.current_session.take() {
816            if self.previous_sessions.len() >= consts::ARCHIVED_STATES_MAX_LENGTH {
817                self.previous_sessions.pop();
818            }
819            current_session.clear_unacknowledged_pre_key_message();
820            self.previous_sessions
821                .insert(0, current_session.session.encode_to_vec());
822            true
823        } else {
824            false
825        }
826    }
827
828    pub fn archive_current_state(&mut self) -> Result<(), SignalProtocolError> {
829        if !self.archive_current_state_inner() {
830            log::info!("Skipping archive, current session state is fresh");
831        }
832        Ok(())
833    }
834
835    pub fn serialize(&self) -> Result<Vec<u8>, SignalProtocolError> {
836        let record = RecordStructure {
837            current_session: self.current_session.as_ref().map(|s| s.into()),
838            previous_sessions: self.previous_sessions.clone(),
839        };
840        Ok(record.encode_to_vec())
841    }
842
843    pub fn current_pq_state(&self) -> Option<&spqr::SerializedState> {
844        self.current_session.as_ref().map(|s| s.pq_ratchet_state())
845    }
846
847    pub fn remote_registration_id(&self) -> Result<u32, SignalProtocolError> {
848        Ok(self
849            .session_state()
850            .ok_or_else(|| {
851                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
852                    "remote_registration_id",
853                ))
854            })?
855            .remote_registration_id())
856    }
857
858    pub fn local_registration_id(&self) -> Result<u32, SignalProtocolError> {
859        Ok(self
860            .session_state()
861            .ok_or_else(|| {
862                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
863                    "local_registration_id",
864                ))
865            })?
866            .local_registration_id())
867    }
868
869    pub fn session_version(&self) -> Result<u32, SignalProtocolError> {
870        Ok(self
871            .session_state()
872            .ok_or_else(|| {
873                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
874                    "session_version",
875                ))
876            })?
877            .session_version()?)
878    }
879
880    pub fn local_identity_key_bytes(&self) -> Result<Vec<u8>, SignalProtocolError> {
881        Ok(self
882            .session_state()
883            .ok_or_else(|| {
884                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
885                    "local_identity_key_bytes",
886                ))
887            })?
888            .local_identity_key_bytes()?)
889    }
890
891    pub fn remote_identity_key_bytes(&self) -> Result<Option<Vec<u8>>, SignalProtocolError> {
892        Ok(self
893            .session_state()
894            .ok_or_else(|| {
895                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
896                    "remote_identity_key_bytes",
897                ))
898            })?
899            .remote_identity_key_bytes()?)
900    }
901
902    pub fn has_usable_sender_chain(
903        &self,
904        now: SystemTime,
905        requirements: SessionUsabilityRequirements,
906    ) -> Result<bool, SignalProtocolError> {
907        match &self.current_session {
908            Some(session) => Ok(session.has_usable_sender_chain(now, requirements)?),
909            None => Ok(false),
910        }
911    }
912
913    pub fn alice_base_key(&self) -> Result<&[u8], SignalProtocolError> {
914        Ok(self
915            .session_state()
916            .ok_or_else(|| {
917                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
918                    "alice_base_key",
919                ))
920            })?
921            .alice_base_key())
922    }
923
924    pub fn get_receiver_chain_key_bytes(
925        &self,
926        sender: &PublicKey,
927    ) -> Result<Option<Box<[u8]>>, SignalProtocolError> {
928        Ok(self
929            .session_state()
930            .ok_or_else(|| {
931                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
932                    "get_receiver_chain_key",
933                ))
934            })?
935            .get_receiver_chain_key(sender)?
936            .map(|chain| chain.key()[..].into()))
937    }
938
939    pub fn get_sender_chain_key_bytes(&self) -> Result<Vec<u8>, SignalProtocolError> {
940        Ok(self
941            .session_state()
942            .ok_or_else(|| {
943                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
944                    "get_sender_chain_key_bytes",
945                ))
946            })?
947            .get_sender_chain_key_bytes()?)
948    }
949
950    pub fn current_ratchet_key_matches(
951        &self,
952        key: &PublicKey,
953    ) -> Result<bool, SignalProtocolError> {
954        match &self.current_session {
955            Some(session) => Ok(&session.sender_ratchet_key()? == key),
956            None => Ok(false),
957        }
958    }
959
960    pub fn get_kyber_ciphertext(&self) -> Result<Option<&Vec<u8>>, SignalProtocolError> {
961        Ok(self
962            .session_state()
963            .ok_or_else(|| {
964                SignalProtocolError::SessionNotFound(SessionNotFound::without_address(
965                    "get_kyber_ciphertext",
966                ))
967            })?
968            .get_kyber_ciphertext())
969    }
970}