miden_crypto/ecdh/
k256.rs1use alloc::{string::ToString, vec::Vec};
16
17use hkdf::{Hkdf, hmac::SimpleHmac};
18use k256::{AffinePoint, elliptic_curve::sec1::ToEncodedPoint, sha2::Sha256};
19use rand::{CryptoRng, RngCore};
20
21use crate::{
22 dsa::ecdsa_k256_keccak::{PUBLIC_KEY_BYTES, 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: k256::ecdh::SharedSecret,
39}
40
41impl SharedSecret {
42 pub(crate) fn new(inner: k256::ecdh::SharedSecret) -> SharedSecret {
43 Self { inner }
44 }
45
46 pub fn extract(&self, salt: Option<&[u8]>) -> Hkdf<Sha256, SimpleHmac<Sha256>> {
52 self.inner.extract(salt)
53 }
54}
55
56impl AsRef<[u8]> for SharedSecret {
57 fn as_ref(&self) -> &[u8] {
58 self.inner.raw_secret_bytes()
59 }
60}
61
62impl Zeroize for SharedSecret {
63 fn zeroize(&mut self) {
76 let bytes = self.inner.raw_secret_bytes();
77 for byte in
78 unsafe { core::slice::from_raw_parts_mut(bytes.as_ptr() as *mut u8, bytes.len()) }
79 {
80 unsafe {
81 core::ptr::write_volatile(byte, 0u8);
82 }
83 }
84 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
85 }
86}
87
88impl ZeroizeOnDrop for SharedSecret {}
90
91pub struct EphemeralSecretKey {
99 inner: k256::ecdh::EphemeralSecret,
100}
101
102impl EphemeralSecretKey {
103 #[cfg(feature = "std")]
105 #[allow(clippy::new_without_default)]
106 pub fn new() -> Self {
107 let mut rng = rand::rng();
108
109 Self::with_rng(&mut rng)
110 }
111
112 pub fn with_rng<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
114 use k256::elliptic_curve::rand_core::SeedableRng;
119 let mut seed = [0_u8; 32];
120 rand::RngCore::fill_bytes(rng, &mut seed);
121 let mut rng = rand_hc::Hc128Rng::from_seed(seed);
122
123 let sk_e = k256::ecdh::EphemeralSecret::random(&mut rng);
124 Self { inner: sk_e }
125 }
126
127 pub fn public_key(&self) -> EphemeralPublicKey {
129 let pk = self.inner.public_key();
130 EphemeralPublicKey { inner: pk }
131 }
132
133 pub fn diffie_hellman(&self, pk_other: PublicKey) -> SharedSecret {
136 let shared_secret_inner = self.inner.diffie_hellman(&pk_other.inner.into());
137
138 SharedSecret { inner: shared_secret_inner }
139 }
140}
141
142impl ZeroizeOnDrop for EphemeralSecretKey {}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct EphemeralPublicKey {
150 pub(crate) inner: k256::PublicKey,
151}
152
153impl EphemeralPublicKey {
154 pub fn as_affine(&self) -> &AffinePoint {
157 self.inner.as_affine()
158 }
159}
160
161impl Serializable for EphemeralPublicKey {
162 fn write_into<W: ByteWriter>(&self, target: &mut W) {
163 let encoded = self.inner.to_encoded_point(true);
165
166 target.write_bytes(encoded.as_bytes());
167 }
168}
169
170impl Deserializable for EphemeralPublicKey {
171 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
172 let bytes: [u8; PUBLIC_KEY_BYTES] = source.read_array()?;
173
174 let inner = k256::PublicKey::from_sec1_bytes(&bytes)
175 .map_err(|_| DeserializationError::InvalidValue("Invalid public key".to_string()))?;
176
177 Ok(Self { inner })
178 }
179}
180
181pub struct K256;
185
186impl KeyAgreementScheme for K256 {
187 type EphemeralSecretKey = EphemeralSecretKey;
188 type EphemeralPublicKey = EphemeralPublicKey;
189
190 type SecretKey = SecretKey;
191 type PublicKey = PublicKey;
192
193 type SharedSecret = SharedSecret;
194
195 fn generate_ephemeral_keypair<R: CryptoRng + RngCore>(
196 rng: &mut R,
197 ) -> (Self::EphemeralSecretKey, Self::EphemeralPublicKey) {
198 let sk = EphemeralSecretKey::with_rng(rng);
199 let pk = sk.public_key();
200
201 (sk, pk)
202 }
203
204 fn exchange_ephemeral_static(
205 ephemeral_sk: Self::EphemeralSecretKey,
206 static_pk: &Self::PublicKey,
207 ) -> Result<Self::SharedSecret, super::KeyAgreementError> {
208 Ok(ephemeral_sk.diffie_hellman(static_pk.clone()))
209 }
210
211 fn exchange_static_ephemeral(
212 static_sk: &Self::SecretKey,
213 ephemeral_pk: &Self::EphemeralPublicKey,
214 ) -> Result<Self::SharedSecret, super::KeyAgreementError> {
215 Ok(static_sk.get_shared_secret(ephemeral_pk.clone()))
216 }
217
218 fn extract_key_material(
219 shared_secret: &Self::SharedSecret,
220 length: usize,
221 ) -> Result<Vec<u8>, super::KeyAgreementError> {
222 let hkdf = shared_secret.extract(None);
223 let mut buf = vec![0_u8; length];
224 hkdf.expand(&[], &mut buf)
225 .map_err(|_| super::KeyAgreementError::HkdfExpansionFailed)?;
226 Ok(buf)
227 }
228}
229
230#[cfg(test)]
234mod test {
235 use super::{EphemeralPublicKey, EphemeralSecretKey};
236 use crate::{
237 dsa::ecdsa_k256_keccak::SecretKey,
238 rand::test_utils::seeded_rng,
239 utils::{Deserializable, Serializable},
240 };
241
242 #[test]
243 fn key_agreement() {
244 let mut rng = seeded_rng([0u8; 32]);
245
246 let sk = SecretKey::with_rng(&mut rng);
248 let pk = sk.public_key();
249
250 let sk_e = EphemeralSecretKey::with_rng(&mut rng);
252 let pk_e = sk_e.public_key();
253
254 let shared_secret_key_1 = sk_e.diffie_hellman(pk);
257
258 let shared_secret_key_2 = sk.get_shared_secret(pk_e);
262
263 assert_eq!(
265 shared_secret_key_1.inner.raw_secret_bytes(),
266 shared_secret_key_2.inner.raw_secret_bytes()
267 );
268 }
269
270 #[test]
271 fn test_serialization_round_trip() {
272 let mut rng = seeded_rng([1u8; 32]);
273
274 let sk_e = EphemeralSecretKey::with_rng(&mut rng);
275 let pk_e = sk_e.public_key();
276
277 let pk_e_bytes = pk_e.to_bytes();
278 let pk_e_serialized = EphemeralPublicKey::read_from_bytes(&pk_e_bytes)
279 .expect("failed to desrialize ephemeral public key");
280 assert_eq!(pk_e_serialized, pk_e);
281 }
282}