libsignal_protocol/
ratchet.rs

1//
2// Copyright 2020 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6mod keys;
7mod params;
8
9use rand::{CryptoRng, Rng};
10
11pub(crate) use self::keys::{ChainKey, MessageKeys, RootKey};
12pub use self::params::{AliceSignalProtocolParameters, BobSignalProtocolParameters};
13use crate::protocol::{CIPHERTEXT_MESSAGE_CURRENT_VERSION, CIPHERTEXT_MESSAGE_PRE_KYBER_VERSION};
14use crate::state::SessionState;
15use crate::{KeyPair, Result, SessionRecord};
16
17fn derive_keys(has_kyber: bool, secret_input: &[u8]) -> (RootKey, ChainKey) {
18    let label = if has_kyber {
19        b"WhisperText_X25519_SHA-256_CRYSTALS-KYBER-1024".as_slice()
20    } else {
21        b"WhisperText".as_slice()
22    };
23    derive_keys_with_label(label, secret_input)
24}
25
26fn message_version(has_kyber: bool) -> u8 {
27    if has_kyber {
28        CIPHERTEXT_MESSAGE_CURRENT_VERSION
29    } else {
30        CIPHERTEXT_MESSAGE_PRE_KYBER_VERSION
31    }
32}
33
34fn derive_keys_with_label(label: &[u8], secret_input: &[u8]) -> (RootKey, ChainKey) {
35    let mut secrets = [0; 64];
36    hkdf::Hkdf::<sha2::Sha256>::new(None, secret_input)
37        .expand(label, &mut secrets)
38        .expect("valid length");
39    let (root_key_bytes, chain_key_bytes) = secrets.split_at(32);
40
41    let root_key = RootKey::new(root_key_bytes.try_into().expect("correct length"));
42    let chain_key = ChainKey::new(chain_key_bytes.try_into().expect("correct length"), 0);
43
44    (root_key, chain_key)
45}
46
47pub(crate) fn initialize_alice_session<R: Rng + CryptoRng>(
48    parameters: &AliceSignalProtocolParameters,
49    mut csprng: &mut R,
50) -> Result<SessionState> {
51    let local_identity = parameters.our_identity_key_pair().identity_key();
52
53    let sending_ratchet_key = KeyPair::generate(&mut csprng);
54
55    let mut secrets = Vec::with_capacity(32 * 5);
56
57    secrets.extend_from_slice(&[0xFFu8; 32]); // "discontinuity bytes"
58
59    let our_base_private_key = parameters.our_base_key_pair().private_key;
60
61    secrets.extend_from_slice(
62        &parameters
63            .our_identity_key_pair()
64            .private_key()
65            .calculate_agreement(parameters.their_signed_pre_key())?,
66    );
67
68    secrets.extend_from_slice(
69        &our_base_private_key.calculate_agreement(parameters.their_identity_key().public_key())?,
70    );
71
72    secrets.extend_from_slice(
73        &our_base_private_key.calculate_agreement(parameters.their_signed_pre_key())?,
74    );
75
76    if let Some(their_one_time_prekey) = parameters.their_one_time_pre_key() {
77        secrets
78            .extend_from_slice(&our_base_private_key.calculate_agreement(their_one_time_prekey)?);
79    }
80
81    let kyber_ciphertext = parameters.their_kyber_pre_key().map(|kyber_public| {
82        let (ss, ct) = kyber_public.encapsulate();
83        secrets.extend_from_slice(ss.as_ref());
84        ct
85    });
86    let has_kyber = parameters.their_kyber_pre_key().is_some();
87
88    let (root_key, chain_key) = derive_keys(has_kyber, &secrets);
89
90    let (sending_chain_root_key, sending_chain_chain_key) = root_key.create_chain(
91        parameters.their_ratchet_key(),
92        &sending_ratchet_key.private_key,
93    )?;
94
95    let mut session = SessionState::new(
96        message_version(has_kyber),
97        local_identity,
98        parameters.their_identity_key(),
99        &sending_chain_root_key,
100        &parameters.our_base_key_pair().public_key,
101    )
102    .with_receiver_chain(parameters.their_ratchet_key(), &chain_key)
103    .with_sender_chain(&sending_ratchet_key, &sending_chain_chain_key);
104
105    if let Some(kyber_ciphertext) = kyber_ciphertext {
106        session.set_kyber_ciphertext(kyber_ciphertext);
107    }
108
109    Ok(session)
110}
111
112pub(crate) fn initialize_bob_session(
113    parameters: &BobSignalProtocolParameters,
114) -> Result<SessionState> {
115    let local_identity = parameters.our_identity_key_pair().identity_key();
116
117    let mut secrets = Vec::with_capacity(32 * 5);
118
119    secrets.extend_from_slice(&[0xFFu8; 32]); // "discontinuity bytes"
120
121    secrets.extend_from_slice(
122        &parameters
123            .our_signed_pre_key_pair()
124            .private_key
125            .calculate_agreement(parameters.their_identity_key().public_key())?,
126    );
127
128    secrets.extend_from_slice(
129        &parameters
130            .our_identity_key_pair()
131            .private_key()
132            .calculate_agreement(parameters.their_base_key())?,
133    );
134
135    secrets.extend_from_slice(
136        &parameters
137            .our_signed_pre_key_pair()
138            .private_key
139            .calculate_agreement(parameters.their_base_key())?,
140    );
141
142    if let Some(our_one_time_pre_key_pair) = parameters.our_one_time_pre_key_pair() {
143        secrets.extend_from_slice(
144            &our_one_time_pre_key_pair
145                .private_key
146                .calculate_agreement(parameters.their_base_key())?,
147        );
148    }
149
150    match (
151        parameters.our_kyber_pre_key_pair(),
152        parameters.their_kyber_ciphertext(),
153    ) {
154        (Some(key_pair), Some(ciphertext)) => {
155            let ss = key_pair.secret_key.decapsulate(ciphertext)?;
156            secrets.extend_from_slice(ss.as_ref());
157        }
158        (None, None) => (), // Alice does not support kyber prekeys
159        _ => {
160            panic!("Either both or none of the kyber key pair and ciphertext can be provided")
161        }
162    }
163    let has_kyber = parameters.our_kyber_pre_key_pair().is_some();
164
165    let (root_key, chain_key) = derive_keys(has_kyber, &secrets);
166
167    let session = SessionState::new(
168        message_version(has_kyber),
169        local_identity,
170        parameters.their_identity_key(),
171        &root_key,
172        parameters.their_base_key(),
173    )
174    .with_sender_chain(parameters.our_ratchet_key_pair(), &chain_key);
175
176    Ok(session)
177}
178
179pub fn initialize_alice_session_record<R: Rng + CryptoRng>(
180    parameters: &AliceSignalProtocolParameters,
181    csprng: &mut R,
182) -> Result<SessionRecord> {
183    Ok(SessionRecord::new(initialize_alice_session(
184        parameters, csprng,
185    )?))
186}
187
188pub fn initialize_bob_session_record(
189    parameters: &BobSignalProtocolParameters,
190) -> Result<SessionRecord> {
191    Ok(SessionRecord::new(initialize_bob_session(parameters)?))
192}