miden_crypto/ecdh/
x25519.rs1use alloc::vec::Vec;
16
17use hkdf::{Hkdf, hmac::SimpleHmac};
18use k256::sha2::Sha256;
19use rand::{CryptoRng, RngCore};
20
21use crate::{
22 dsa::eddsa_25519_sha512::{PublicKey, SecretKey},
23 ecdh::KeyAgreementScheme,
24 utils::{
25 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
26 zeroize::{Zeroize, ZeroizeOnDrop},
27 },
28};
29
30pub struct SharedSecret {
38 pub(crate) inner: x25519_dalek::SharedSecret,
39}
40impl SharedSecret {
41 pub(crate) fn new(inner: x25519_dalek::SharedSecret) -> SharedSecret {
42 Self { inner }
43 }
44
45 pub fn extract(&self, salt: Option<&[u8]>) -> Hkdf<Sha256, SimpleHmac<Sha256>> {
47 Hkdf::new(salt, self.inner.as_bytes())
48 }
49}
50
51impl Zeroize for SharedSecret {
52 fn zeroize(&mut self) {
65 let bytes = self.inner.as_bytes();
66 for byte in
67 unsafe { core::slice::from_raw_parts_mut(bytes.as_ptr() as *mut u8, bytes.len()) }
68 {
69 unsafe {
70 core::ptr::write_volatile(byte, 0u8);
71 }
72 }
73 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
74 }
75}
76
77impl ZeroizeOnDrop for SharedSecret {}
79
80impl AsRef<[u8]> for SharedSecret {
81 fn as_ref(&self) -> &[u8] {
82 self.inner.as_bytes()
83 }
84}
85
86pub struct EphemeralSecretKey {
94 inner: x25519_dalek::EphemeralSecret,
95}
96
97impl ZeroizeOnDrop for EphemeralSecretKey {}
98
99impl EphemeralSecretKey {
100 #[cfg(feature = "std")]
102 #[allow(clippy::new_without_default)]
103 pub fn new() -> Self {
104 let mut rng = rand::rng();
105
106 Self::with_rng(&mut rng)
107 }
108
109 pub fn with_rng<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
111 use k256::elliptic_curve::rand_core::SeedableRng;
117 let mut seed = [0_u8; 32];
118 rand::RngCore::fill_bytes(rng, &mut seed);
119 let rng = rand_hc::Hc128Rng::from_seed(seed);
120
121 let sk = x25519_dalek::EphemeralSecret::random_from_rng(rng);
122 Self { inner: sk }
123 }
124
125 pub fn public_key(&self) -> EphemeralPublicKey {
127 EphemeralPublicKey {
128 inner: x25519_dalek::PublicKey::from(&self.inner),
129 }
130 }
131
132 pub fn diffie_hellman(self, pk_other: &PublicKey) -> SharedSecret {
135 let shared = self.inner.diffie_hellman(&pk_other.to_x25519());
136 SharedSecret::new(shared)
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
145pub struct EphemeralPublicKey {
146 pub(crate) inner: x25519_dalek::PublicKey,
147}
148
149impl Serializable for EphemeralPublicKey {
150 fn write_into<W: ByteWriter>(&self, target: &mut W) {
151 target.write_bytes(self.inner.as_bytes());
152 }
153}
154
155impl Deserializable for EphemeralPublicKey {
156 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
157 let bytes: [u8; 32] = source.read_array()?;
158 Ok(Self {
159 inner: x25519_dalek::PublicKey::from(bytes),
160 })
161 }
162}
163
164pub struct X25519;
168
169impl KeyAgreementScheme for X25519 {
170 type EphemeralSecretKey = EphemeralSecretKey;
171 type EphemeralPublicKey = EphemeralPublicKey;
172
173 type SecretKey = SecretKey;
174 type PublicKey = PublicKey;
175
176 type SharedSecret = SharedSecret;
177
178 fn generate_ephemeral_keypair<R: CryptoRng + RngCore>(
179 rng: &mut R,
180 ) -> (Self::EphemeralSecretKey, Self::EphemeralPublicKey) {
181 let sk = EphemeralSecretKey::with_rng(rng);
182 let pk = sk.public_key();
183
184 (sk, pk)
185 }
186
187 fn exchange_ephemeral_static(
188 ephemeral_sk: Self::EphemeralSecretKey,
189 static_pk: &Self::PublicKey,
190 ) -> Result<Self::SharedSecret, super::KeyAgreementError> {
191 Ok(ephemeral_sk.diffie_hellman(static_pk))
192 }
193
194 fn exchange_static_ephemeral(
195 static_sk: &Self::SecretKey,
196 ephemeral_pk: &Self::EphemeralPublicKey,
197 ) -> Result<Self::SharedSecret, super::KeyAgreementError> {
198 Ok(static_sk.get_shared_secret(ephemeral_pk.clone()))
199 }
200
201 fn extract_key_material(
202 shared_secret: &Self::SharedSecret,
203 length: usize,
204 ) -> Result<Vec<u8>, super::KeyAgreementError> {
205 let hkdf = shared_secret.extract(None);
206 let mut buf = vec![0_u8; length];
207 hkdf.expand(&[], &mut buf)
208 .map_err(|_| super::KeyAgreementError::HkdfExpansionFailed)?;
209 Ok(buf)
210 }
211}
212
213#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::{dsa::eddsa_25519_sha512::SecretKey, rand::test_utils::seeded_rng};
220
221 #[test]
222 fn key_agreement() {
223 let mut rng = seeded_rng([0u8; 32]);
224
225 let sk = SecretKey::with_rng(&mut rng);
227 let pk = sk.public_key();
228
229 let sk_e = EphemeralSecretKey::with_rng(&mut rng);
231 let pk_e = sk_e.public_key();
232
233 let shared_secret_key_1 = sk_e.diffie_hellman(&pk);
236
237 let shared_secret_key_2 = sk.get_shared_secret(pk_e);
241
242 assert_eq!(shared_secret_key_1.inner.to_bytes(), shared_secret_key_2.inner.to_bytes());
244 }
245}