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::{PublicKey, SecretKey},
23 ecdh::KeyAgreementScheme,
24 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
25 zeroize::{Zeroize, ZeroizeOnDrop},
26};
27
28pub struct SharedSecret {
36 pub(crate) inner: x25519_dalek::SharedSecret,
37}
38impl SharedSecret {
39 pub(crate) fn new(inner: x25519_dalek::SharedSecret) -> SharedSecret {
40 Self { inner }
41 }
42
43 pub fn extract(&self, salt: Option<&[u8]>) -> Hkdf<Sha256, SimpleHmac<Sha256>> {
45 Hkdf::new(salt, self.inner.as_bytes())
46 }
47}
48
49impl Zeroize for SharedSecret {
50 fn zeroize(&mut self) {
63 let bytes = self.inner.as_bytes();
64 for byte in
65 unsafe { core::slice::from_raw_parts_mut(bytes.as_ptr() as *mut u8, bytes.len()) }
66 {
67 unsafe {
68 core::ptr::write_volatile(byte, 0u8);
69 }
70 }
71 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
72 }
73}
74
75impl ZeroizeOnDrop for SharedSecret {}
77
78impl AsRef<[u8]> for SharedSecret {
79 fn as_ref(&self) -> &[u8] {
80 self.inner.as_bytes()
81 }
82}
83
84pub struct EphemeralSecretKey {
92 inner: x25519_dalek::EphemeralSecret,
93}
94
95impl ZeroizeOnDrop for EphemeralSecretKey {}
96
97impl EphemeralSecretKey {
98 #[cfg(feature = "std")]
100 #[allow(clippy::new_without_default)]
101 pub fn new() -> Self {
102 let mut rng = rand::rng();
103
104 Self::with_rng(&mut rng)
105 }
106
107 pub fn with_rng<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
109 use k256::elliptic_curve::rand_core::SeedableRng;
115 let mut seed = [0_u8; 32];
116 rand::RngCore::fill_bytes(rng, &mut seed);
117 let rng = rand_hc::Hc128Rng::from_seed(seed);
118
119 let sk = x25519_dalek::EphemeralSecret::random_from_rng(rng);
120 Self { inner: sk }
121 }
122
123 pub fn public_key(&self) -> EphemeralPublicKey {
125 EphemeralPublicKey {
126 inner: x25519_dalek::PublicKey::from(&self.inner),
127 }
128 }
129
130 pub fn diffie_hellman(self, pk_other: &PublicKey) -> SharedSecret {
133 let shared = self.inner.diffie_hellman(&pk_other.to_x25519());
134 SharedSecret::new(shared)
135 }
136}
137
138#[derive(Debug, Clone, PartialEq, Eq)]
143pub struct EphemeralPublicKey {
144 pub(crate) inner: x25519_dalek::PublicKey,
145}
146
147impl Serializable for EphemeralPublicKey {
148 fn write_into<W: ByteWriter>(&self, target: &mut W) {
149 target.write_bytes(self.inner.as_bytes());
150 }
151}
152
153impl Deserializable for EphemeralPublicKey {
154 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
155 let bytes: [u8; 32] = source.read_array()?;
156 Ok(Self {
157 inner: x25519_dalek::PublicKey::from(bytes),
158 })
159 }
160}
161
162pub struct X25519;
166
167impl KeyAgreementScheme for X25519 {
168 type EphemeralSecretKey = EphemeralSecretKey;
169 type EphemeralPublicKey = EphemeralPublicKey;
170
171 type SecretKey = SecretKey;
172 type PublicKey = PublicKey;
173
174 type SharedSecret = SharedSecret;
175
176 fn generate_ephemeral_keypair<R: CryptoRng + RngCore>(
177 rng: &mut R,
178 ) -> (Self::EphemeralSecretKey, Self::EphemeralPublicKey) {
179 let sk = EphemeralSecretKey::with_rng(rng);
180 let pk = sk.public_key();
181
182 (sk, pk)
183 }
184
185 fn exchange_ephemeral_static(
186 ephemeral_sk: Self::EphemeralSecretKey,
187 static_pk: &Self::PublicKey,
188 ) -> Result<Self::SharedSecret, super::KeyAgreementError> {
189 Ok(ephemeral_sk.diffie_hellman(static_pk))
190 }
191
192 fn exchange_static_ephemeral(
193 static_sk: &Self::SecretKey,
194 ephemeral_pk: &Self::EphemeralPublicKey,
195 ) -> Result<Self::SharedSecret, super::KeyAgreementError> {
196 Ok(static_sk.get_shared_secret(ephemeral_pk.clone()))
197 }
198
199 fn extract_key_material(
200 shared_secret: &Self::SharedSecret,
201 length: usize,
202 ) -> Result<Vec<u8>, super::KeyAgreementError> {
203 let hkdf = shared_secret.extract(None);
204 let mut buf = vec![0_u8; length];
205 hkdf.expand(&[], &mut buf)
206 .map_err(|_| super::KeyAgreementError::HkdfExpansionFailed)?;
207 Ok(buf)
208 }
209}
210
211#[cfg(test)]
215mod tests {
216 use rand::rng;
217
218 use super::*;
219 use crate::dsa::eddsa_25519::SecretKey;
220
221 #[test]
222 fn key_agreement() {
223 let mut rng = rng();
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}