securedrop_protocol_minimal/server/
challenge.rs1use crate::challenge::{derive_challenge_key, encrypt_message_id};
2use crate::keys::NR_ID;
3use crate::primitives::provider::constants::{LEN_KMID, LEN_MESSAGE_ID};
4use crate::primitives::ristretto255::{
5 dh_shared_secret, generate_random_scalar, random_dh_public_key,
6};
7use crate::{DHPublicKey, Envelope, FetchResponse};
8use alloc::vec::Vec;
9use rand_core::{CryptoRng, RngCore};
10
11#[cfg_attr(hax, hax_lib::fstar::verification_status(lax))]
19pub fn compute_fetch_challenges<R: RngCore + CryptoRng>(
20 rng: &mut R,
21 entries: &[([u8; LEN_MESSAGE_ID], Envelope)],
22 total_responses: usize,
23) -> Vec<FetchResponse> {
24 let mut responses = Vec::with_capacity(total_responses);
25
26 let decoy_mgdh = random_dh_public_key(rng);
28 let decoy_mgdh_pubkey = random_dh_public_key(rng);
29
30 for (message_id, envelope) in entries.iter() {
31 if responses.len() < total_responses {
32 responses.push(make_response(
33 rng,
34 message_id,
35 &envelope.mgdh,
36 &envelope.mgdh_pubkey,
37 NR_ID,
38 ));
39 }
40 }
41
42 while responses.len() < total_responses {
44 responses.push(make_response(
45 rng,
46 &[0u8; LEN_MESSAGE_ID],
47 &decoy_mgdh,
48 &decoy_mgdh_pubkey,
49 NR_ID,
50 ));
51 }
52 responses
53}
54
55#[cfg_attr(hax, hax_lib::fstar::verification_status(lax))]
57fn make_response<R: RngCore + CryptoRng>(
58 rng: &mut R,
59 message_id: &[u8; LEN_MESSAGE_ID],
60 mgdh: &DHPublicKey,
61 mgdh_pubkey: &DHPublicKey,
62 nr_id: &[u8],
63) -> FetchResponse {
64 let eph_sk = generate_random_scalar(&mut *rng);
66
67 let shared_secret = dh_shared_secret(mgdh, &eph_sk);
69 let key = derive_challenge_key(&shared_secret, nr_id).unwrap();
70 let enc_mid = encrypt_message_id(&key, message_id).unwrap();
71
72 let mut kmid = [0u8; LEN_KMID];
76 kmid.copy_from_slice(&enc_mid);
77
78 let pmgdh = dh_shared_secret(mgdh_pubkey, &eph_sk);
81
82 FetchResponse {
83 enc_id: kmid,
84 pmgdh,
85 }
86}