libsignal_protocol/
ratchet.rs1mod keys;
7mod params;
8
9use rand::{CryptoRng, Rng};
10
11pub(crate) use self::keys::{ChainKey, MessageKeyGenerator, RootKey};
12pub use self::params::{AliceSignalProtocolParameters, BobSignalProtocolParameters, UsePQRatchet};
13use crate::protocol::CIPHERTEXT_MESSAGE_CURRENT_VERSION;
14use crate::state::SessionState;
15use crate::{consts, KeyPair, Result, SessionRecord, SignalProtocolError};
16
17type InitialPQRKey = [u8; 32];
18
19fn derive_keys(secret_input: &[u8]) -> (RootKey, ChainKey, InitialPQRKey) {
20 derive_keys_with_label(
21 b"WhisperText_X25519_SHA-256_CRYSTALS-KYBER-1024",
22 secret_input,
23 )
24}
25
26fn derive_keys_with_label(label: &[u8], secret_input: &[u8]) -> (RootKey, ChainKey, InitialPQRKey) {
27 let mut secrets = [0; 96];
28 hkdf::Hkdf::<sha2::Sha256>::new(None, secret_input)
29 .expand(label, &mut secrets)
30 .expect("valid length");
31 let (root_key_bytes, chain_key_bytes, pqr_bytes) =
32 (&secrets[0..32], &secrets[32..64], &secrets[64..96]);
33
34 let root_key = RootKey::new(root_key_bytes.try_into().expect("correct length"));
35 let chain_key = ChainKey::new(chain_key_bytes.try_into().expect("correct length"), 0);
36 let pqr_key: InitialPQRKey = pqr_bytes.try_into().expect("correct length");
37
38 (root_key, chain_key, pqr_key)
39}
40
41fn spqr_chain_params(self_connection: bool) -> spqr::ChainParams {
42 #[allow(clippy::needless_update)]
43 spqr::ChainParams {
44 max_jump: if self_connection {
45 u32::MAX
46 } else {
47 consts::MAX_FORWARD_JUMPS.try_into().expect("should be <4B")
48 },
49 max_ooo_keys: consts::MAX_MESSAGE_KEYS.try_into().expect("should be <4B"),
50 ..Default::default()
51 }
52}
53
54pub(crate) fn initialize_alice_session<R: Rng + CryptoRng>(
55 parameters: &AliceSignalProtocolParameters,
56 mut csprng: &mut R,
57) -> Result<SessionState> {
58 let local_identity = parameters.our_identity_key_pair().identity_key();
59
60 let mut secrets = Vec::with_capacity(32 * 6);
61
62 secrets.extend_from_slice(&[0xFFu8; 32]); let our_base_private_key = parameters.our_base_key_pair().private_key;
65
66 secrets.extend_from_slice(
67 ¶meters
68 .our_identity_key_pair()
69 .private_key()
70 .calculate_agreement(parameters.their_signed_pre_key())?,
71 );
72
73 secrets.extend_from_slice(
74 &our_base_private_key.calculate_agreement(parameters.their_identity_key().public_key())?,
75 );
76
77 secrets.extend_from_slice(
78 &our_base_private_key.calculate_agreement(parameters.their_signed_pre_key())?,
79 );
80
81 if let Some(their_one_time_prekey) = parameters.their_one_time_pre_key() {
82 secrets
83 .extend_from_slice(&our_base_private_key.calculate_agreement(their_one_time_prekey)?);
84 }
85
86 let kyber_ciphertext = {
87 let (ss, ct) = parameters.their_kyber_pre_key().encapsulate(&mut csprng)?;
88 secrets.extend_from_slice(ss.as_ref());
89 ct
90 };
91
92 let (root_key, chain_key, pqr_key) = derive_keys(&secrets);
93
94 let sending_ratchet_key = KeyPair::generate(&mut csprng);
95 let (sending_chain_root_key, sending_chain_chain_key) = root_key.create_chain(
96 parameters.their_ratchet_key(),
97 &sending_ratchet_key.private_key,
98 )?;
99
100 let self_session = local_identity == parameters.their_identity_key();
101 let pqr_state = match parameters.use_pq_ratchet() {
102 UsePQRatchet::Yes => spqr::initial_state(spqr::Params {
103 auth_key: &pqr_key,
104 version: spqr::Version::V1,
105 direction: spqr::Direction::A2B,
106 min_version: spqr::Version::V0,
111 chain_params: spqr_chain_params(self_session),
112 })
113 .map_err(|e| {
114 SignalProtocolError::InvalidArgument(format!(
117 "post-quantum ratchet: error creating initial A2B state: {e}"
118 ))
119 })?,
120 UsePQRatchet::No => spqr::SerializedState::new(), };
122
123 let mut session = SessionState::new(
124 CIPHERTEXT_MESSAGE_CURRENT_VERSION,
125 local_identity,
126 parameters.their_identity_key(),
127 &sending_chain_root_key,
128 ¶meters.our_base_key_pair().public_key,
129 pqr_state,
130 )
131 .with_receiver_chain(parameters.their_ratchet_key(), &chain_key)
132 .with_sender_chain(&sending_ratchet_key, &sending_chain_chain_key);
133
134 session.set_kyber_ciphertext(kyber_ciphertext);
135
136 Ok(session)
137}
138
139pub(crate) fn initialize_bob_session(
140 parameters: &BobSignalProtocolParameters,
141) -> Result<SessionState> {
142 let local_identity = parameters.our_identity_key_pair().identity_key();
143
144 let mut secrets = Vec::with_capacity(32 * 6);
145
146 secrets.extend_from_slice(&[0xFFu8; 32]); secrets.extend_from_slice(
149 ¶meters
150 .our_signed_pre_key_pair()
151 .private_key
152 .calculate_agreement(parameters.their_identity_key().public_key())?,
153 );
154
155 secrets.extend_from_slice(
156 ¶meters
157 .our_identity_key_pair()
158 .private_key()
159 .calculate_agreement(parameters.their_base_key())?,
160 );
161
162 secrets.extend_from_slice(
163 ¶meters
164 .our_signed_pre_key_pair()
165 .private_key
166 .calculate_agreement(parameters.their_base_key())?,
167 );
168
169 if let Some(our_one_time_pre_key_pair) = parameters.our_one_time_pre_key_pair() {
170 secrets.extend_from_slice(
171 &our_one_time_pre_key_pair
172 .private_key
173 .calculate_agreement(parameters.their_base_key())?,
174 );
175 }
176
177 secrets.extend_from_slice(
178 ¶meters
179 .our_kyber_pre_key_pair()
180 .secret_key
181 .decapsulate(parameters.their_kyber_ciphertext())?,
182 );
183
184 let (root_key, chain_key, pqr_key) = derive_keys(&secrets);
185
186 let self_session = local_identity == parameters.their_identity_key();
187 let pqr_state = match parameters.use_pq_ratchet() {
188 UsePQRatchet::Yes => spqr::initial_state(spqr::Params {
189 auth_key: &pqr_key,
190 version: spqr::Version::V1,
191 direction: spqr::Direction::B2A,
192 min_version: spqr::Version::V0,
197 chain_params: spqr_chain_params(self_session),
198 })
199 .map_err(|e| {
200 SignalProtocolError::InvalidArgument(format!(
203 "post-quantum ratchet: error creating initial B2A state: {e}"
204 ))
205 })?,
206 UsePQRatchet::No => spqr::SerializedState::new(), };
208 let session = SessionState::new(
209 CIPHERTEXT_MESSAGE_CURRENT_VERSION,
210 local_identity,
211 parameters.their_identity_key(),
212 &root_key,
213 parameters.their_base_key(),
214 pqr_state,
215 )
216 .with_sender_chain(parameters.our_ratchet_key_pair(), &chain_key);
217
218 Ok(session)
219}
220
221pub fn initialize_alice_session_record<R: Rng + CryptoRng>(
222 parameters: &AliceSignalProtocolParameters,
223 csprng: &mut R,
224) -> Result<SessionRecord> {
225 Ok(SessionRecord::new(initialize_alice_session(
226 parameters, csprng,
227 )?))
228}
229
230pub fn initialize_bob_session_record(
231 parameters: &BobSignalProtocolParameters,
232) -> Result<SessionRecord> {
233 Ok(SessionRecord::new(initialize_bob_session(parameters)?))
234}