miden_crypto/dsa/ecdsa_k256_keccak/
mod.rs1use alloc::{format, string::ToString, vec::Vec};
5use core::fmt;
6
7use k256::{
8 AffinePoint,
9 ecdh::diffie_hellman,
10 ecdsa,
11 ecdsa::{RecoveryId, VerifyingKey, signature::hazmat::PrehashVerifier},
12 elliptic_curve::{Generate, point::AffineCoordinates, scalar::IsHigh},
13 pkcs8::DecodePublicKey,
14};
15use miden_crypto_derive::{SilentDebug, SilentDisplay};
16use rand::CryptoRng;
17use thiserror::Error;
18
19use crate::{
20 Felt, SequentialCommit, Word,
21 ecdh::k256::{EphemeralPublicKey, SharedSecret},
22 utils::{
23 ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable,
24 hex_to_bytes, read_sensitive_array, zeroize::ZeroizeOnDrop,
25 },
26};
27
28mod tests;
29
30const SECRET_KEY_BYTES: usize = 32;
35pub(crate) const PUBLIC_KEY_BYTES: usize = 33;
37const PUBLIC_KEY_UNCOMPRESSED_BYTES: usize = 65;
39const SIGNATURE_BYTES: usize = 65;
41const SIGNATURE_STANDARD_BYTES: usize = 64;
43const SCALARS_SIZE_BYTES: usize = 32;
45
46#[derive(Clone, SilentDebug, SilentDisplay)]
51struct SecretKey {
52 inner: ecdsa::SigningKey,
53}
54
55impl SecretKey {
56 fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
58 let signing_key = ecdsa::SigningKey::generate_from_rng(rng);
59
60 Self { inner: signing_key }
61 }
62
63 fn public_key(&self) -> PublicKey {
65 let verifying_key = self.inner.verifying_key();
66 PublicKey { inner: *verifying_key }
67 }
68
69 fn sign(&self, message: Word) -> Signature {
71 let message_digest = hash_message(message);
72 self.sign_prehash(message_digest)
73 }
74
75 fn sign_prehash(&self, message_digest: [u8; 32]) -> Signature {
77 let (signature_inner, recovery_id) = self.inner.sign_prehash_recoverable(&message_digest);
78
79 let (r, s) = signature_inner.split_scalars();
80
81 Signature {
82 r: r.to_bytes().into(),
83 s: s.to_bytes().into(),
84 v: recovery_id.into(),
85 }
86 }
87
88 fn get_shared_secret(&self, pk_e: &EphemeralPublicKey) -> SharedSecret {
91 let shared_secret_inner = diffie_hellman(self.inner.as_nonzero_scalar(), pk_e.as_affine());
92
93 SharedSecret::new(shared_secret_inner)
94 }
95}
96
97impl ZeroizeOnDrop for SecretKey {}
100
101impl PartialEq for SecretKey {
102 fn eq(&self, other: &Self) -> bool {
103 use subtle::ConstantTimeEq;
104 self.to_bytes().ct_eq(&other.to_bytes()).into()
105 }
106}
107
108impl Eq for SecretKey {}
109
110#[derive(Clone, Eq, PartialEq, SilentDebug, SilentDisplay)] pub struct SigningKey(SecretKey);
116
117impl SigningKey {
118 #[cfg(feature = "std")]
122 #[allow(clippy::new_without_default)]
123 pub fn new() -> Self {
124 let mut rng = rand::rng();
125 Self::with_rng(&mut rng)
126 }
127
128 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
130 Self(SecretKey::with_rng(rng))
131 }
132
133 pub fn public_key(&self) -> PublicKey {
135 self.0.public_key()
136 }
137
138 pub fn sign(&self, message: Word) -> Signature {
140 self.0.sign(message)
141 }
142
143 pub fn sign_prehash(&self, message_digest: [u8; 32]) -> Signature {
145 self.0.sign_prehash(message_digest)
146 }
147}
148
149impl From<SecretKey> for SigningKey {
150 fn from(secret_key: SecretKey) -> Self {
151 Self(secret_key)
152 }
153}
154
155impl ZeroizeOnDrop for SigningKey {}
158
159impl Serializable for SigningKey {
160 fn write_into<W: ByteWriter>(&self, target: &mut W) {
161 self.0.write_into(target);
162 }
163}
164
165impl Deserializable for SigningKey {
166 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
167 Ok(Self(SecretKey::read_from(source)?))
168 }
169}
170
171#[derive(Clone, Eq, PartialEq, SilentDebug, SilentDisplay)] pub struct KeyExchangeKey(SecretKey);
177
178impl KeyExchangeKey {
179 #[cfg(feature = "std")]
183 #[allow(clippy::new_without_default)]
184 pub fn new() -> Self {
185 let mut rng = rand::rng();
186 Self::with_rng(&mut rng)
187 }
188
189 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
191 Self(SecretKey::with_rng(rng))
192 }
193
194 pub fn public_key(&self) -> PublicKey {
196 self.0.public_key()
197 }
198
199 pub fn get_shared_secret(&self, pk_e: EphemeralPublicKey) -> SharedSecret {
202 self.0.get_shared_secret(&pk_e)
203 }
204}
205
206impl From<SecretKey> for KeyExchangeKey {
207 fn from(value: SecretKey) -> Self {
208 Self(value)
209 }
210}
211
212impl ZeroizeOnDrop for KeyExchangeKey {}
215
216impl Serializable for KeyExchangeKey {
217 fn write_into<W: ByteWriter>(&self, target: &mut W) {
218 self.0.write_into(target);
219 }
220}
221
222impl Deserializable for KeyExchangeKey {
223 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
224 Ok(Self(SecretKey::read_from(source)?))
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Eq)]
233pub struct PublicKey {
234 pub(crate) inner: VerifyingKey,
235}
236
237impl PublicKey {
238 pub fn to_commitment(&self) -> Word {
244 <Self as SequentialCommit>::to_commitment(self)
245 }
246
247 pub fn as_affine(&self) -> &AffinePoint {
249 self.inner.as_affine()
250 }
251
252 pub fn verify(&self, message: Word, signature: &Signature) -> bool {
254 let message_digest = hash_message(message);
255 self.verify_prehash(message_digest, signature)
256 }
257
258 pub fn verify_prehash(&self, message_digest: [u8; 32], signature: &Signature) -> bool {
260 let signature_inner = ecdsa::Signature::from_scalars(*signature.r(), *signature.s());
261
262 match signature_inner {
263 Ok(signature) => self.inner.verify_prehash(&message_digest, &signature).is_ok(),
264 Err(_) => false,
265 }
266 }
267
268 pub fn recover_from(message: Word, signature: &Signature) -> Result<Self, PublicKeyError> {
271 let message_digest = hash_message(message);
272 Self::recover_from_prehash(message_digest, signature)
273 }
274
275 pub fn recover_from_prehash(
287 message_digest: [u8; 32],
288 signature: &Signature,
289 ) -> Result<Self, PublicKeyError> {
290 let signature_data = ecdsa::Signature::from_scalars(*signature.r(), *signature.s())
291 .map_err(|_| PublicKeyError::RecoveryFailed)?;
292
293 let verifying_key = VerifyingKey::recover_from_prehash(
294 &message_digest,
295 &signature_data,
296 RecoveryId::from_byte(signature.v()).ok_or(PublicKeyError::RecoveryFailed)?,
297 )
298 .map_err(|_| PublicKeyError::RecoveryFailed)?;
299
300 Ok(Self { inner: verifying_key })
301 }
302
303 pub fn from_der(bytes: &[u8]) -> Result<Self, DeserializationError> {
308 let verifying_key = VerifyingKey::from_public_key_der(bytes)
309 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
310 Ok(PublicKey { inner: verifying_key })
311 }
312
313 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
320 if bytes.len() != PUBLIC_KEY_BYTES && bytes.len() != PUBLIC_KEY_UNCOMPRESSED_BYTES {
321 return Err(DeserializationError::InvalidValue(format!(
322 "unsupported ECDSA public key length: expected {PUBLIC_KEY_BYTES} (compressed) or \
323 {PUBLIC_KEY_UNCOMPRESSED_BYTES} (uncompressed) bytes, got {}",
324 bytes.len()
325 )));
326 }
327
328 let tag = bytes[0];
330 if !matches!(
331 (bytes.len(), tag),
332 (PUBLIC_KEY_BYTES, 0x02 | 0x03) | (PUBLIC_KEY_UNCOMPRESSED_BYTES, 0x04)
333 ) {
334 return Err(DeserializationError::InvalidValue(format!(
335 "unsupported ECDSA public key tag byte {tag:#04x}: expected 0x02 or 0x03 \
336 ({PUBLIC_KEY_BYTES}-byte compressed) or 0x04 \
337 ({PUBLIC_KEY_UNCOMPRESSED_BYTES}-byte uncompressed)"
338 )));
339 }
340
341 let verifying_key = VerifyingKey::from_sec1_bytes(bytes)
342 .map_err(|_| DeserializationError::InvalidValue("Invalid public key".to_string()))?;
343 Ok(PublicKey { inner: verifying_key })
344 }
345
346 pub fn from_hex(encoded: &str) -> Result<Self, DeserializationError> {
352 let invalid_hex = |err: HexParseError| DeserializationError::InvalidValue(err.to_string());
353
354 let hex_digits = encoded.strip_prefix("0x").ok_or_else(|| {
355 DeserializationError::InvalidValue(
356 "ECDSA public key must use a 0x-prefixed hexadecimal encoding".to_string(),
357 )
358 })?;
359
360 match hex_digits.len() {
361 len if len == PUBLIC_KEY_BYTES * 2 => Self::from_sec1_bytes(
362 &hex_to_bytes::<PUBLIC_KEY_BYTES>(encoded).map_err(invalid_hex)?,
363 ),
364 len if len == PUBLIC_KEY_UNCOMPRESSED_BYTES * 2 => Self::from_sec1_bytes(
365 &hex_to_bytes::<PUBLIC_KEY_UNCOMPRESSED_BYTES>(encoded).map_err(invalid_hex)?,
366 ),
367 len => Err(DeserializationError::InvalidValue(format!(
368 "unsupported ECDSA public key length: expected {} (compressed) or {} \
369 (uncompressed) hexadecimal digits after the 0x prefix, got {len}",
370 PUBLIC_KEY_BYTES * 2,
371 PUBLIC_KEY_UNCOMPRESSED_BYTES * 2,
372 ))),
373 }
374 }
375}
376
377impl SequentialCommit for PublicKey {
378 type Commitment = Word;
379
380 fn to_elements(&self) -> Vec<Felt> {
381 affine_point_to_elements(self.as_affine()).to_vec()
382 }
383}
384
385#[derive(Debug, Error)]
386pub enum PublicKeyError {
387 #[error("Could not recover the public key from the message and signature")]
388 RecoveryFailed,
389}
390
391#[derive(Debug, Clone, PartialEq, Eq)]
437pub struct Signature {
438 r: [u8; SCALARS_SIZE_BYTES],
439 s: [u8; SCALARS_SIZE_BYTES],
440 v: u8,
441}
442
443impl Signature {
444 pub fn r(&self) -> &[u8; SCALARS_SIZE_BYTES] {
446 &self.r
447 }
448
449 pub fn s(&self) -> &[u8; SCALARS_SIZE_BYTES] {
451 &self.s
452 }
453
454 pub fn v(&self) -> u8 {
456 self.v
457 }
458
459 pub fn verify(&self, message: Word, pub_key: &PublicKey) -> bool {
461 pub_key.verify(message, self)
462 }
463
464 pub fn to_sec1_bytes(&self) -> [u8; SIGNATURE_STANDARD_BYTES] {
468 let mut bytes = [0u8; 2 * SCALARS_SIZE_BYTES];
469 bytes[0..SCALARS_SIZE_BYTES].copy_from_slice(self.r());
470 bytes[SCALARS_SIZE_BYTES..2 * SCALARS_SIZE_BYTES].copy_from_slice(self.s());
471 bytes
472 }
473
474 pub fn from_sec1_bytes_and_recovery_id(
480 bytes: [u8; SIGNATURE_STANDARD_BYTES],
481 recovery_id: u8,
482 ) -> Result<Self, DeserializationError> {
483 let mut r = [0u8; SCALARS_SIZE_BYTES];
484 let mut s = [0u8; SCALARS_SIZE_BYTES];
485 r.copy_from_slice(&bytes[0..SCALARS_SIZE_BYTES]);
486 s.copy_from_slice(&bytes[SCALARS_SIZE_BYTES..2 * SCALARS_SIZE_BYTES]);
487
488 if recovery_id > 3 {
489 return Err(DeserializationError::InvalidValue(r#"Invalid recovery ID"#.to_string()));
490 }
491
492 Ok(Signature { r, s, v: recovery_id })
493 }
494
495 pub fn from_der(bytes: &[u8], mut recovery_id: u8) -> Result<Self, DeserializationError> {
501 if recovery_id > 3 {
502 return Err(DeserializationError::InvalidValue(r#"Invalid recovery ID"#.to_string()));
503 }
504
505 let sig = ecdsa::Signature::from_der(bytes)
506 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
507
508 let high_s = sig.s().is_high();
511 if bool::from(high_s) {
512 recovery_id ^= 1;
517 }
518 let sig = sig.normalize_s();
519
520 let (r, s) = sig.split_scalars();
521
522 Ok(Signature {
523 r: <[u8; SCALARS_SIZE_BYTES]>::from(r.to_bytes()),
524 s: <[u8; SCALARS_SIZE_BYTES]>::from(s.to_bytes()),
525 v: recovery_id,
526 })
527 }
528}
529
530impl Serializable for SecretKey {
534 fn write_into<W: ByteWriter>(&self, target: &mut W) {
535 let mut buffer = Vec::with_capacity(SECRET_KEY_BYTES);
536 let sk_bytes: [u8; SECRET_KEY_BYTES] = self.inner.to_bytes().into();
537 buffer.extend_from_slice(&sk_bytes);
538
539 target.write_bytes(&buffer);
540 }
541}
542
543impl Deserializable for SecretKey {
544 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
545 let bytes = read_sensitive_array::<SECRET_KEY_BYTES, _>(source)?;
546
547 let signing_key = ecdsa::SigningKey::from_slice(bytes.as_slice())
548 .map_err(|_| DeserializationError::InvalidValue("Invalid secret key".to_string()))?;
549
550 Ok(Self { inner: signing_key })
551 }
552}
553
554impl Serializable for PublicKey {
555 fn write_into<W: ByteWriter>(&self, target: &mut W) {
556 let encoded = self.inner.to_sec1_point(true);
557 target.write_bytes(encoded.as_bytes());
558 }
559}
560
561impl Deserializable for PublicKey {
562 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
563 let bytes: [u8; PUBLIC_KEY_BYTES] = source.read_array()?;
564
565 let verifying_key = VerifyingKey::from_sec1_bytes(&bytes)
566 .map_err(|_| DeserializationError::InvalidValue("Invalid public key".to_string()))?;
567
568 Ok(Self { inner: verifying_key })
569 }
570}
571
572impl fmt::Display for PublicKey {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 crate::utils::write_hex(f, &self.to_bytes())
575 }
576}
577
578impl Serializable for Signature {
579 fn write_into<W: ByteWriter>(&self, target: &mut W) {
580 let mut bytes = [0u8; SIGNATURE_BYTES];
581 bytes[0..SCALARS_SIZE_BYTES].copy_from_slice(self.r());
582 bytes[SCALARS_SIZE_BYTES..2 * SCALARS_SIZE_BYTES].copy_from_slice(self.s());
583 bytes[2 * SCALARS_SIZE_BYTES] = self.v();
584 target.write_bytes(&bytes);
585 }
586}
587
588impl Deserializable for Signature {
589 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
590 let r: [u8; SCALARS_SIZE_BYTES] = source.read_array()?;
591 let s: [u8; SCALARS_SIZE_BYTES] = source.read_array()?;
592 let v: u8 = source.read_u8()?;
593
594 if v > 3 {
595 Err(DeserializationError::InvalidValue(r#"Invalid recovery ID"#.to_string()))
596 } else {
597 Ok(Signature { r, s, v })
598 }
599 }
600}
601
602impl fmt::Display for Signature {
603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604 crate::utils::write_hex(f, &self.to_bytes())
605 }
606}
607
608fn affine_point_to_elements(point: &AffinePoint) -> [Felt; 16] {
612 let qx = be_bytes_to_le_u32_limbs(point.x().as_ref());
613 let qy = be_bytes_to_le_u32_limbs(point.y().as_ref());
614
615 core::array::from_fn(|idx| {
616 if idx < 8 {
617 Felt::from_u32(qx[idx])
618 } else {
619 Felt::from_u32(qy[idx - 8])
620 }
621 })
622}
623
624fn be_bytes_to_le_u32_limbs(bytes: &[u8]) -> [u32; 8] {
625 debug_assert_eq!(bytes.len(), SCALARS_SIZE_BYTES);
626 core::array::from_fn(|idx| {
627 let start = SCALARS_SIZE_BYTES - 4 * (idx + 1);
628 u32::from_be_bytes(bytes[start..start + 4].try_into().expect("chunk is exactly 4 bytes"))
629 })
630}
631
632fn hash_message(message: Word) -> [u8; 32] {
634 use sha3::{Digest, Keccak256};
635 let mut hasher = Keccak256::new();
636 let message_bytes: [u8; 32] = message.into();
637 hasher.update(message_bytes);
638 hasher.finalize().into()
639}