securedrop_protocol_minimal/primitives/
x25519.rs1use crate::primitives::provider::{self, curve25519::ecdh};
2use anyhow::Error;
3use rand_core::{CryptoRng, RngCore};
4
5pub const DH_PUBLIC_KEY_LEN: usize = crate::primitives::provider::curve25519::PK_LEN;
6pub(crate) const DH_PRIVATE_KEY_LEN: usize = crate::primitives::provider::curve25519::SK_LEN;
7pub(crate) const DH_SHARED_SECRET_LEN: usize =
8 crate::primitives::provider::curve25519::LEN_DH_SHARE;
9
10#[derive(Debug, Clone, Copy)]
12pub struct DHPublicKey([u8; DH_PUBLIC_KEY_LEN]);
13
14impl DHPublicKey {
15 pub fn into_bytes(self) -> [u8; DH_PUBLIC_KEY_LEN] {
16 self.0
17 }
18
19 pub fn from_bytes(bytes: [u8; DH_PUBLIC_KEY_LEN]) -> Self {
20 Self(bytes)
21 }
22}
23
24#[derive(Debug, Clone)]
26pub struct DHPrivateKey([u8; DH_PRIVATE_KEY_LEN]);
27
28impl DHPrivateKey {
29 pub fn into_bytes(self) -> [u8; DH_PRIVATE_KEY_LEN] {
30 self.0
31 }
32
33 pub fn from_bytes(bytes: [u8; DH_PRIVATE_KEY_LEN]) -> Self {
34 Self(bytes)
35 }
36}
37
38#[derive(Debug, Clone)]
40pub struct DHSharedSecret([u8; 32]);
41
42impl DHSharedSecret {
43 pub fn into_bytes(self) -> [u8; 32] {
44 self.0
45 }
46
47 pub fn from_bytes(bytes: [u8; 32]) -> Self {
48 Self(bytes)
49 }
50}
51
52pub fn deterministic_dh_keygen(randomness: [u8; 32]) -> Result<(DHPrivateKey, DHPublicKey), Error> {
55 let mut public_key = [0u8; DH_PUBLIC_KEY_LEN];
56 let mut secret_key = [0u8; DH_PRIVATE_KEY_LEN];
57
58 provider::curve25519::x25519_keygen(&mut public_key, &mut secret_key, &randomness)
59 .map_err(|_| anyhow::anyhow!("X25519 key generation failed"))?;
60
61 Ok((DHPrivateKey(secret_key), DHPublicKey(public_key)))
62}
63
64pub fn generate_dh_keypair<R: RngCore + CryptoRng>(
66 mut rng: R,
67) -> Result<(DHPrivateKey, DHPublicKey), Error> {
68 let mut randomness = [0u8; 32];
69 rng.fill_bytes(&mut randomness);
70
71 let mut public_key = [0u8; DH_PUBLIC_KEY_LEN];
72 let mut secret_key = [0u8; DH_PRIVATE_KEY_LEN];
73
74 provider::curve25519::x25519_keygen(&mut public_key, &mut secret_key, &randomness)
77 .map_err(|_| anyhow::anyhow!("X25519 key generation failed"))?;
78
79 typed(secret_key, public_key)
80}
81
82#[cfg_attr(hax, hax_lib::ensures(|result| result.is_ok()))]
85fn typed(
86 sk: [u8; DH_PRIVATE_KEY_LEN],
87 pk: [u8; DH_PUBLIC_KEY_LEN],
88) -> Result<(DHPrivateKey, DHPublicKey), Error> {
89 Ok((DHPrivateKey(sk), DHPublicKey(pk)))
90}
91
92pub fn generate_random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Result<[u8; 32], Error> {
94 let mut randomness = [0u8; 32];
95 rng.fill_bytes(&mut randomness);
96
97 let mut secret_key = [0u8; 32];
98 let mut _public_key = [0u8; 32]; provider::curve25519::x25519_keygen(&mut _public_key, &mut secret_key, &randomness)
103 .map_err(|_| anyhow::anyhow!("X25519 key generation failed"))?;
104
105 Ok(secret_key)
106}
107
108pub fn dh_public_key_from_scalar(scalar: [u8; 32]) -> DHPublicKey {
113 let mut public_key_bytes = [0u8; 32];
114 provider::curve25519::secret_to_public(&mut public_key_bytes, &scalar);
115 DHPublicKey::from_bytes(public_key_bytes)
116}
117
118pub fn dh_shared_secret(
120 public_key: &DHPublicKey,
121 private_scalar: [u8; 32],
122) -> Result<DHSharedSecret, Error> {
123 let mut shared_secret_bytes = [0u8; 32];
124 ecdh(&mut shared_secret_bytes, &public_key.0, &private_scalar)
125 .map_err(|_| anyhow::anyhow!("X25519 DH failed"))?;
126 Ok(DHSharedSecret(shared_secret_bytes))
127}
128
129#[cfg(test)]
130mod tests {
131 use crate::primitives::provider::curve25519::LEN_DH_SHARE;
132
133 use super::*;
134 use proptest::prelude::*;
135 use rand_chacha::ChaCha20Rng;
136 use rand_core::SeedableRng;
137
138 fn get_rng() -> ChaCha20Rng {
140 let mut seed = [0u8; 32];
141 getrandom::fill(&mut seed).expect("OS random source failed");
142 ChaCha20Rng::from_seed(seed)
143 }
144
145 #[test]
146 fn test_deterministic_dh_keygen() {
147 proptest!(|(randomness in proptest::array::uniform32(any::<u8>()))| {
148 let (private_key, public_key) = deterministic_dh_keygen(randomness).unwrap();
149 });
150 }
151
152 #[test]
153 fn test_dh_shared_secret() {
154 let mut rng = get_rng();
155
156 let (sk1, pk1) = generate_dh_keypair(&mut rng).expect("need dh keygen");
157
158 let (sk2, pk2) = generate_dh_keypair(&mut rng).expect("need dh keygen");
159
160 let ss1 = dh_shared_secret(&pk1, sk2.into_bytes()).expect("need shared secret 1");
161 let ss2 = dh_shared_secret(&pk2, sk1.into_bytes()).expect("need shared secret 2");
162
163 assert_eq!(ss1.clone().into_bytes(), ss2.into_bytes());
164 assert_ne!(ss1.into_bytes(), [0u8; LEN_DH_SHARE])
165 }
166}