1use libsignal_core::derive_arrays;
23use rand::{CryptoRng, Rng};
24
25use crate::handshake::Handshake;
26use crate::ratchet::{ChainKey, RootKey};
27use crate::{
28 CiphertextMessageType, IdentityKey, IdentityKeyPair, KeyPair, PublicKey, Result,
29 SignalProtocolError, kem,
30};
31
32pub(crate) struct Pqxdh;
37
38impl Handshake for Pqxdh {
39 type InitiatorParams = InitiatorParameters;
40 type RecipientParams<'a> = RecipientParameters<'a>;
41 type InitiatorMessage = kem::SerializedCiphertext;
42 type SessionSecret = HandshakeKeys;
43
44 fn initiate<R: Rng + CryptoRng>(
45 params: &Self::InitiatorParams,
46 rng: &mut R,
47 ) -> Result<(Self::InitiatorMessage, Self::SessionSecret)> {
48 let result = pqxdh_initiate(params, rng)?;
49 Ok((result.kyber_ciphertext, result.keys))
50 }
51
52 fn accept(params: &Self::RecipientParams<'_>) -> Result<Self::SessionSecret> {
53 pqxdh_accept(params)
54 }
55}
56
57pub(crate) type InitialPQRKey = [u8; 32];
59
60pub(crate) struct HandshakeKeys {
65 pub root_key: RootKey,
66 pub chain_key: ChainKey,
67 pub pqr_key: InitialPQRKey,
68}
69
70impl HandshakeKeys {
71 fn derive(secret_input: &[u8]) -> Self {
73 Self::derive_with_label(
74 b"WhisperText_X25519_SHA-256_CRYSTALS-KYBER-1024",
75 secret_input,
76 )
77 }
78
79 fn derive_with_label(label: &[u8], secret_input: &[u8]) -> Self {
80 let (root_key_bytes, chain_key_bytes, pqr_bytes) = derive_arrays(|bytes| {
81 hkdf::Hkdf::<sha2::Sha256>::new(None, secret_input)
82 .expand(label, bytes)
83 .expect("valid length")
84 });
85
86 Self {
87 root_key: RootKey::new(root_key_bytes),
88 chain_key: ChainKey::new(chain_key_bytes, 0),
89 pqr_key: pqr_bytes,
90 }
91 }
92}
93
94pub(crate) struct InitiatorAgreement {
101 keys: HandshakeKeys,
102 kyber_ciphertext: kem::SerializedCiphertext,
103}
104
105pub struct InitiatorParameters {
111 our_identity_key_pair: IdentityKeyPair,
112 our_ephemeral_key_pair: KeyPair,
113
114 their_identity_key: IdentityKey,
115 their_signed_pre_key: PublicKey,
116 their_one_time_pre_key: Option<PublicKey>,
117 their_ratchet_key: PublicKey,
118 their_kyber_pre_key: kem::PublicKey,
119
120 self_session: bool,
121}
122
123impl InitiatorParameters {
124 pub fn new(
125 our_identity_key_pair: IdentityKeyPair,
126 our_ephemeral_key_pair: KeyPair,
127 their_identity_key: IdentityKey,
128 their_signed_pre_key: PublicKey,
129 their_ratchet_key: PublicKey,
130 their_kyber_pre_key: kem::PublicKey,
131 self_session: bool,
132 ) -> Self {
133 Self {
134 our_identity_key_pair,
135 our_ephemeral_key_pair,
136 their_identity_key,
137 their_one_time_pre_key: None,
138 their_signed_pre_key,
139 their_ratchet_key,
140 their_kyber_pre_key,
141 self_session,
142 }
143 }
144
145 pub fn set_their_one_time_pre_key(&mut self, ec_public: PublicKey) {
146 self.their_one_time_pre_key = Some(ec_public);
147 }
148
149 #[inline]
150 pub fn our_identity_key_pair(&self) -> &IdentityKeyPair {
151 &self.our_identity_key_pair
152 }
153
154 #[inline]
155 pub fn our_ephemeral_key_pair(&self) -> &KeyPair {
156 &self.our_ephemeral_key_pair
157 }
158
159 #[inline]
160 pub fn their_identity_key(&self) -> &IdentityKey {
161 &self.their_identity_key
162 }
163
164 #[inline]
165 pub fn their_signed_pre_key(&self) -> &PublicKey {
166 &self.their_signed_pre_key
167 }
168
169 #[inline]
170 pub fn their_one_time_pre_key(&self) -> Option<&PublicKey> {
171 self.their_one_time_pre_key.as_ref()
172 }
173
174 #[inline]
175 pub fn their_kyber_pre_key(&self) -> &kem::PublicKey {
176 &self.their_kyber_pre_key
177 }
178
179 #[inline]
180 pub fn their_ratchet_key(&self) -> &PublicKey {
181 &self.their_ratchet_key
182 }
183
184 #[inline]
185 pub fn self_session(&self) -> bool {
186 self.self_session
187 }
188}
189
190pub(crate) fn pqxdh_initiate<R: Rng + CryptoRng>(
195 parameters: &InitiatorParameters,
196 mut csprng: &mut R,
197) -> Result<InitiatorAgreement> {
198 let mut secrets = Vec::with_capacity(32 * 6);
199
200 secrets.extend_from_slice(&[0xFFu8; 32]); secrets.extend_from_slice(
203 ¶meters
204 .our_identity_key_pair
205 .private_key()
206 .calculate_agreement(¶meters.their_signed_pre_key)?,
207 );
208
209 let our_ephemeral_private_key = parameters.our_ephemeral_key_pair.private_key;
210
211 secrets.extend_from_slice(
212 &our_ephemeral_private_key
213 .calculate_agreement(parameters.their_identity_key.public_key())?,
214 );
215
216 secrets.extend_from_slice(
217 &our_ephemeral_private_key.calculate_agreement(¶meters.their_signed_pre_key)?,
218 );
219
220 if let Some(their_one_time_prekey) = ¶meters.their_one_time_pre_key {
221 secrets.extend_from_slice(
222 &our_ephemeral_private_key.calculate_agreement(their_one_time_prekey)?,
223 );
224 }
225
226 let kyber_ciphertext = {
227 let (ss, ct) = parameters.their_kyber_pre_key.encapsulate(&mut csprng)?;
228 secrets.extend_from_slice(ss.as_ref());
229 ct
230 };
231
232 Ok(InitiatorAgreement {
233 keys: HandshakeKeys::derive(&secrets),
234 kyber_ciphertext,
235 })
236}
237
238pub struct RecipientParameters<'a> {
246 our_identity_key_pair: IdentityKeyPair,
247 our_signed_pre_key_pair: KeyPair,
248 our_one_time_pre_key_pair: Option<KeyPair>,
249 our_kyber_pre_key_pair: kem::KeyPair,
250
251 their_identity_key: IdentityKey,
252 their_ephemeral_key: PublicKey,
253 their_kyber_ciphertext: &'a kem::SerializedCiphertext,
254
255 self_session: bool,
256}
257
258impl<'a> RecipientParameters<'a> {
259 pub fn new(
260 our_identity_key_pair: IdentityKeyPair,
261 our_signed_pre_key_pair: KeyPair,
262 our_one_time_pre_key_pair: Option<KeyPair>,
263 our_kyber_pre_key_pair: kem::KeyPair,
264 their_identity_key: IdentityKey,
265 their_ephemeral_key: PublicKey,
266 their_kyber_ciphertext: &'a kem::SerializedCiphertext,
267 self_session: bool,
268 ) -> Self {
269 Self {
270 our_identity_key_pair,
271 our_signed_pre_key_pair,
272 our_one_time_pre_key_pair,
273 our_kyber_pre_key_pair,
274 their_identity_key,
275 their_ephemeral_key,
276 their_kyber_ciphertext,
277 self_session,
278 }
279 }
280
281 #[inline]
282 pub fn our_identity_key_pair(&self) -> &IdentityKeyPair {
283 &self.our_identity_key_pair
284 }
285
286 #[inline]
287 pub fn our_signed_pre_key_pair(&self) -> &KeyPair {
288 &self.our_signed_pre_key_pair
289 }
290
291 #[inline]
292 pub fn our_one_time_pre_key_pair(&self) -> Option<&KeyPair> {
293 self.our_one_time_pre_key_pair.as_ref()
294 }
295
296 #[inline]
297 pub fn our_kyber_pre_key_pair(&self) -> &kem::KeyPair {
298 &self.our_kyber_pre_key_pair
299 }
300
301 #[inline]
302 pub fn their_identity_key(&self) -> &IdentityKey {
303 &self.their_identity_key
304 }
305
306 #[inline]
307 pub fn their_ephemeral_key(&self) -> &PublicKey {
308 &self.their_ephemeral_key
309 }
310
311 #[inline]
312 pub fn their_kyber_ciphertext(&self) -> &kem::SerializedCiphertext {
313 self.their_kyber_ciphertext
314 }
315
316 #[inline]
317 pub fn self_session(&self) -> bool {
318 self.self_session
319 }
320}
321
322pub(crate) fn pqxdh_accept(parameters: &RecipientParameters) -> Result<HandshakeKeys> {
327 if !parameters.their_ephemeral_key.is_canonical() {
329 return Err(SignalProtocolError::InvalidMessage(
330 CiphertextMessageType::PreKey,
331 "incoming base key is invalid",
332 ));
333 }
334
335 let mut secrets = Vec::with_capacity(32 * 6);
336
337 secrets.extend_from_slice(&[0xFFu8; 32]); secrets.extend_from_slice(
340 ¶meters
341 .our_signed_pre_key_pair
342 .private_key
343 .calculate_agreement(parameters.their_identity_key.public_key())?,
344 );
345
346 secrets.extend_from_slice(
347 ¶meters
348 .our_identity_key_pair
349 .private_key()
350 .calculate_agreement(¶meters.their_ephemeral_key)?,
351 );
352
353 secrets.extend_from_slice(
354 ¶meters
355 .our_signed_pre_key_pair
356 .private_key
357 .calculate_agreement(¶meters.their_ephemeral_key)?,
358 );
359
360 if let Some(our_one_time_pre_key_pair) = ¶meters.our_one_time_pre_key_pair {
361 secrets.extend_from_slice(
362 &our_one_time_pre_key_pair
363 .private_key
364 .calculate_agreement(¶meters.their_ephemeral_key)?,
365 );
366 }
367
368 secrets.extend_from_slice(
369 ¶meters
370 .our_kyber_pre_key_pair
371 .secret_key
372 .decapsulate(parameters.their_kyber_ciphertext)?,
373 );
374
375 Ok(HandshakeKeys::derive(&secrets))
376}