securedrop_protocol_minimal/
primitives.rs1use alloc::vec::Vec;
2use anyhow::Error;
3use rand_core::{CryptoRng, RngCore};
4
5pub(crate) mod dh_akem;
6pub(crate) mod mlkem;
7pub mod pad;
8pub(crate) mod provider;
9pub mod ristretto255;
10pub(crate) mod xwing;
11
12use provider::chacha20poly1305::KEY_LEN;
13
14use crate::primitives::ristretto255::DHPublicKey;
15
16pub const MESSAGE_ID_FETCH_SIZE: usize = 10;
21
22const CHALLENGE_SALT: &[u8] = b"securedrop-challenge-v1";
24
25pub fn derive_challenge_key(
30 shared_secret: &DHPublicKey,
31 newsroom_id: &[u8],
32) -> Result<[u8; KEY_LEN], Error> {
33 use crate::primitives::provider::hkdf;
34
35 let mut challenge_key: [u8; KEY_LEN] = [0u8; KEY_LEN];
36 hkdf::sha256(
38 &mut challenge_key,
39 CHALLENGE_SALT,
40 &shared_secret.into_bytes(),
41 newsroom_id,
42 )
43 .map_err(|_| anyhow::anyhow!("HKDF challenge key derivation failed"))?;
44
45 Ok(challenge_key)
46}
47
48#[cfg_attr(hax, hax_lib::requires(
53 message_id.len()
54 <= usize::MAX
55 - provider::chacha20poly1305::NONCE_LEN
56 - provider::chacha20poly1305::TAG_LEN
57))]
58pub fn encrypt_message_id(key: &[u8; KEY_LEN], message_id: &[u8]) -> Result<Vec<u8>, Error> {
59 use provider::chacha20poly1305::{NONCE_LEN, TAG_LEN};
60
61 let nonce = [0u8; NONCE_LEN];
63
64 let mut output = alloc::vec::Vec::new();
66 let mut ciphertext = alloc::vec![0u8; message_id.len() + TAG_LEN];
67
68 match provider::chacha20poly1305::encrypt(key, message_id, &mut ciphertext, &[], &nonce) {
70 Ok(_) => {}
71 Err(e) => {
72 return Err(anyhow::anyhow!(
73 "ChaCha20-Poly1305 encryption failed: {:?}",
74 e
75 ));
76 }
77 }
78 output.extend_from_slice(&ciphertext);
79 Ok(output)
80}
81
82pub fn decrypt_message_id(key: &[u8; KEY_LEN], encrypted_data: &[u8]) -> Result<Vec<u8>, Error> {
86 use provider::chacha20poly1305::{NONCE_LEN, TAG_LEN};
87
88 if encrypted_data.len() < TAG_LEN {
89 return Err(anyhow::anyhow!("Encrypted data too short"));
90 }
91
92 let nonce = [0u8; NONCE_LEN];
94
95 let mut plaintext = alloc::vec![0u8; encrypted_data.len() - TAG_LEN];
97
98 provider::chacha20poly1305::decrypt(
100 key,
101 &mut plaintext,
102 encrypted_data,
103 &[], &nonce,
105 )
106 .map_err(|e| anyhow::anyhow!("ChaCha20-Poly1305 decryption failed: {:?}", e))?;
107
108 Ok(plaintext)
109}