securedrop_protocol_minimal/primitives/
ristretto255.rs1use anyhow::Error;
3use rand_core::{CryptoRng, RngCore};
4
5use crate::primitives::provider;
6use crate::primitives::provider::ristretto255::{Point, Scalar};
7
8pub const DH_PUBLIC_KEY_LEN: usize = provider::ristretto255::PK_LEN;
9pub(crate) const DH_PRIVATE_KEY_LEN: usize = provider::ristretto255::SK_LEN;
10
11pub const DH_SEED_LEN: usize = provider::ristretto255::SEED_LEN;
13
14#[derive(Debug, Clone, Copy)]
23pub struct DHPublicKey(Point);
24
25#[derive(Debug, Clone)]
30pub struct DHPrivateKey(Scalar);
31
32impl DHPublicKey {
33 pub fn decode(bytes: [u8; DH_PUBLIC_KEY_LEN]) -> Result<Self, Error> {
39 provider::ristretto255::decode(&bytes)
40 .map(Self)
41 .ok_or_else(|| anyhow::anyhow!("invalid ristretto255 point encoding"))
42 }
43
44 pub fn into_bytes(self) -> [u8; DH_PUBLIC_KEY_LEN] {
46 provider::ristretto255::encode(&self.0)
47 }
48}
49
50impl DHPrivateKey {
51 pub fn decode(bytes: [u8; DH_PRIVATE_KEY_LEN]) -> Result<Self, Error> {
54 provider::ristretto255::scalar_decode(&bytes)
55 .map(Self)
56 .ok_or_else(|| anyhow::anyhow!("non-canonical ristretto255 scalar"))
57 }
58
59 pub fn public_key(&self) -> DHPublicKey {
61 DHPublicKey(provider::ristretto255::secret_to_public(&self.0))
62 }
63
64 pub fn to_bytes(&self) -> [u8; DH_PRIVATE_KEY_LEN] {
66 provider::ristretto255::scalar_encode(&self.0)
67 }
68}
69
70pub(crate) fn deterministic_dh_keygen(
75 randomness: [u8; DH_SEED_LEN],
76) -> (DHPrivateKey, DHPublicKey) {
77 let secret_key = DHPrivateKey(provider::ristretto255::scalar_from_wide(&randomness));
78 let public_key = secret_key.public_key();
79
80 (secret_key, public_key)
81}
82
83pub fn generate_dh_keypair<R: RngCore + CryptoRng>(rng: &mut R) -> (DHPrivateKey, DHPublicKey) {
85 let mut randomness = [0u8; DH_SEED_LEN];
86 provider::rng::fill_bytes(rng, &mut randomness);
87
88 deterministic_dh_keygen(randomness)
89}
90
91const PLACEHOLDER_SEED: &[u8] = b"securedrop-protocol-placeholder-v1";
93
94pub fn placeholder_public_key() -> DHPublicKey {
96 let seed = provider::sha2::sha512(PLACEHOLDER_SEED);
97
98 DHPublicKey(provider::ristretto255::from_uniform_bytes(&seed))
99}
100
101pub fn random_dh_public_key<R: RngCore + CryptoRng>(rng: &mut R) -> DHPublicKey {
103 let mut randomness = [0u8; DH_SEED_LEN];
104 provider::rng::fill_bytes(rng, &mut randomness);
105
106 DHPublicKey(provider::ristretto255::from_uniform_bytes(&randomness))
107}
108
109pub fn generate_random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> DHPrivateKey {
111 let mut randomness = [0u8; DH_SEED_LEN];
112 provider::rng::fill_bytes(rng, &mut randomness);
113
114 DHPrivateKey(provider::ristretto255::scalar_from_wide(&randomness))
115}
116
117pub fn dh_shared_secret(public_key: &DHPublicKey, scalar: &DHPrivateKey) -> DHPublicKey {
119 DHPublicKey(provider::ristretto255::dh(&public_key.0, &scalar.0))
120}
121
122#[cfg_attr(hax, hax_lib::exclude)]
123impl serde::Serialize for DHPublicKey {
124 fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
125 ser.serialize_str(&hex::encode(self.into_bytes()))
126 }
127}
128
129#[cfg_attr(hax, hax_lib::exclude)]
130impl<'de> serde::Deserialize<'de> for DHPublicKey {
131 fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
132 use serde::de::Error as _;
133 let s = alloc::string::String::deserialize(de)?;
134 let mut bytes = [0u8; DH_PUBLIC_KEY_LEN];
135 hex::decode_to_slice(s.trim(), &mut bytes).map_err(D::Error::custom)?;
136 Self::decode(bytes).map_err(D::Error::custom)
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use proptest::prelude::*;
145 use rand_chacha::ChaCha20Rng;
146 use rand_core::SeedableRng;
147
148 fn get_rng() -> ChaCha20Rng {
149 let mut seed = [0u8; 32];
150 getrandom::fill(&mut seed).expect("OS random source failed");
151 ChaCha20Rng::from_seed(seed)
152 }
153
154 #[test]
155 fn test_deterministic_dh_keygen() {
156 proptest!(|(randomness in proptest::collection::vec(any::<u8>(), DH_SEED_LEN))| {
157 let seed: [u8; DH_SEED_LEN] = randomness.try_into().unwrap();
158 let (sk, pk) = deterministic_dh_keygen(seed);
159
160 let (sk2, pk2) = deterministic_dh_keygen(seed);
161 prop_assert_eq!(sk.to_bytes(), sk2.to_bytes());
162 prop_assert_eq!(pk.into_bytes(), pk2.into_bytes());
163 prop_assert_eq!(pk.into_bytes(), sk.public_key().into_bytes());
164 });
165 }
166
167 #[test]
168 fn test_dh_shared_secret() {
169 let mut rng = get_rng();
170
171 let (sk1, pk1) = generate_dh_keypair(&mut rng);
172
173 let (sk2, pk2) = generate_dh_keypair(&mut rng);
174
175 let ss1 = dh_shared_secret(&pk1, &sk2);
176 let ss2 = dh_shared_secret(&pk2, &sk1);
177
178 assert_eq!(ss1.into_bytes(), ss2.into_bytes());
179 assert_ne!(ss1.into_bytes(), [0u8; DH_PUBLIC_KEY_LEN])
180 }
181
182 #[test]
183 fn test_three_party_dh() {
184 let mut rng = get_rng();
185
186 let (sk_r, pk_r) = generate_dh_keypair(&mut rng);
187 let (x, big_x) = generate_dh_keypair(&mut rng);
188 let y = generate_random_scalar(&mut rng);
189
190 let z = dh_shared_secret(&pk_r, &x);
191 let server = dh_shared_secret(&z, &y);
192
193 let pmgdh = dh_shared_secret(&big_x, &y);
194 let recipient = dh_shared_secret(&pmgdh, &sk_r);
195
196 assert_eq!(server.into_bytes(), recipient.into_bytes());
197 }
198}