miden_crypto/dsa/eddsa_25519_sha512/
mod.rs1use alloc::{string::ToString, vec::Vec};
5use core::fmt;
6
7use der::{Decode, asn1::BitStringRef};
8use ed25519_dalek::{Signer, Verifier};
9use miden_crypto_derive::{SilentDebug, SilentDisplay};
10use rand::CryptoRng;
11use thiserror::Error;
12
13use crate::{
14 Felt, SequentialCommit, Word,
15 ecdh::x25519::{EphemeralPublicKey, SharedSecret},
16 utils::{
17 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
18 bytes_to_packed_u32_elements, read_sensitive_array,
19 zeroize::{Zeroize, ZeroizeOnDrop},
20 },
21};
22
23mod tests;
24
25const SECRET_KEY_BYTES: usize = 32;
30pub(crate) const PUBLIC_KEY_BYTES: usize = 32;
32const SIGNATURE_BYTES: usize = 64;
34
35#[derive(Clone, SilentDebug, SilentDisplay)]
40#[cfg_attr(
41 all(feature = "arbitrary", test),
42 miden_test_serialization_macros::serialization_test
43)]
44struct SecretKey {
45 inner: ed25519_dalek::SigningKey,
46}
47
48impl SecretKey {
49 fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
51 let mut seed = [0u8; SECRET_KEY_BYTES];
52 rng.fill_bytes(&mut seed);
53
54 let inner = ed25519_dalek::SigningKey::from_bytes(&seed);
55
56 seed.zeroize();
58
59 Self { inner }
60 }
61
62 fn public_key(&self) -> PublicKey {
64 PublicKey { inner: self.inner.verifying_key() }
65 }
66
67 fn sign(&self, message: Word) -> Signature {
69 let message_bytes: [u8; 32] = message.into();
70 let sig = self.inner.sign(&message_bytes);
71 Signature { inner: sig }
72 }
73
74 fn get_shared_secret(&self, pk_e: &EphemeralPublicKey) -> SharedSecret {
77 let shared = self.to_x25519().diffie_hellman(&pk_e.inner);
78 SharedSecret::new(shared)
79 }
80
81 fn to_x25519(&self) -> x25519_dalek::StaticSecret {
88 let mut scalar_bytes = self.inner.to_scalar_bytes();
89 let static_secret = x25519_dalek::StaticSecret::from(scalar_bytes);
90
91 scalar_bytes.zeroize();
93
94 static_secret
95 }
96}
97
98impl ZeroizeOnDrop for SecretKey {}
101
102impl PartialEq for SecretKey {
103 fn eq(&self, other: &Self) -> bool {
104 use subtle::ConstantTimeEq;
105 self.inner.to_bytes().ct_eq(&other.inner.to_bytes()).into()
106 }
107}
108
109impl Eq for SecretKey {}
110
111#[derive(Clone, Eq, PartialEq, SilentDebug, SilentDisplay)]
116#[cfg_attr(
117 all(feature = "arbitrary", test),
118 miden_test_serialization_macros::serialization_test
119)] pub struct SigningKey(SecretKey);
121
122impl SigningKey {
123 #[cfg(feature = "std")]
127 #[allow(clippy::new_without_default)]
128 pub fn new() -> Self {
129 let mut rng = rand::rng();
130 Self::with_rng(&mut rng)
131 }
132
133 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
135 Self(SecretKey::with_rng(rng))
136 }
137
138 pub fn public_key(&self) -> PublicKey {
140 self.0.public_key()
141 }
142
143 pub fn sign(&self, message: Word) -> Signature {
145 self.0.sign(message)
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)]
176#[cfg_attr(
177 all(feature = "arbitrary", test),
178 miden_test_serialization_macros::serialization_test
179)] pub struct KeyExchangeKey(SecretKey);
181
182impl KeyExchangeKey {
183 #[cfg(feature = "std")]
187 #[allow(clippy::new_without_default)]
188 pub fn new() -> Self {
189 let mut rng = rand::rng();
190 Self::with_rng(&mut rng)
191 }
192
193 pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
195 Self(SecretKey::with_rng(rng))
196 }
197
198 pub fn public_key(&self) -> PublicKey {
200 self.0.public_key()
201 }
202
203 pub fn get_shared_secret(&self, pk_e: EphemeralPublicKey) -> SharedSecret {
206 self.0.get_shared_secret(&pk_e)
207 }
208}
209
210impl From<SecretKey> for KeyExchangeKey {
211 fn from(secret_key: SecretKey) -> Self {
212 Self(secret_key)
213 }
214}
215
216impl ZeroizeOnDrop for KeyExchangeKey {}
219
220impl Serializable for KeyExchangeKey {
221 fn write_into<W: ByteWriter>(&self, target: &mut W) {
222 self.0.write_into(target);
223 }
224}
225
226impl Deserializable for KeyExchangeKey {
227 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
228 Ok(Self(SecretKey::read_from(source)?))
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Eq)]
236#[cfg_attr(
237 all(feature = "arbitrary", test),
238 miden_test_serialization_macros::serialization_test
239)]
240pub struct PublicKey {
241 pub(crate) inner: ed25519_dalek::VerifyingKey,
242}
243
244impl PublicKey {
245 pub fn to_commitment(&self) -> Word {
250 <Self as SequentialCommit>::to_commitment(self)
251 }
252
253 pub fn verify(&self, message: Word, signature: &Signature) -> bool {
255 let message_bytes: [u8; 32] = message.into();
256 self.inner.verify(&message_bytes, &signature.inner).is_ok()
257 }
258
259 pub fn compute_challenge_k(&self, message: Word, signature: &Signature) -> [u8; 64] {
300 use sha2::Digest;
301
302 let message_bytes: [u8; 32] = message.into();
303 let sig_bytes = signature.inner.to_bytes();
304 let r_bytes = &sig_bytes[0..32];
305
306 let mut hasher = sha2::Sha512::new();
308 hasher.update(r_bytes);
309 hasher.update(self.inner.to_bytes());
310 hasher.update(message_bytes);
311 let k_hash = hasher.finalize();
312
313 k_hash.into()
314 }
315
316 pub fn verify_with_unchecked_k(
374 &self,
375 k_hash: [u8; 64],
376 signature: &Signature,
377 ) -> Result<(), UncheckedVerificationError> {
378 use curve25519_dalek::{
379 edwards::{CompressedEdwardsY, EdwardsPoint},
380 scalar::Scalar,
381 };
382
383 let k_scalar = Scalar::from_bytes_mod_order_wide(&k_hash);
385
386 let sig_bytes = signature.inner.to_bytes();
388 let r_bytes: [u8; 32] =
389 sig_bytes[..32].try_into().expect("signature R component is exactly 32 bytes");
390 let s_bytes: [u8; 32] =
391 sig_bytes[32..].try_into().expect("signature s component is exactly 32 bytes");
392
393 let s_candidate = Scalar::from_canonical_bytes(s_bytes);
395 if s_candidate.is_none().into() {
396 return Err(UncheckedVerificationError::NonCanonicalScalar);
397 }
398 let s_scalar = s_candidate.unwrap();
399
400 let r_compressed = CompressedEdwardsY(r_bytes);
401 let Some(r_point) = r_compressed.decompress() else {
402 return Err(UncheckedVerificationError::InvalidSignaturePoint);
403 };
404
405 let a_point = self.inner.to_edwards();
406
407 if r_point.is_small_order() {
411 return Err(UncheckedVerificationError::SmallOrderSignature);
412 }
413 if a_point.is_small_order() {
414 return Err(UncheckedVerificationError::SmallOrderPublicKey);
415 }
416
417 let minus_a = -a_point;
420 let expected_r =
421 EdwardsPoint::vartime_double_scalar_mul_basepoint(&k_scalar, &minus_a, &s_scalar)
422 .compress();
423
424 if expected_r == r_compressed {
425 Ok(())
426 } else {
427 Err(UncheckedVerificationError::EquationMismatch)
428 }
429 }
430
431 pub(crate) fn to_x25519(&self) -> x25519_dalek::PublicKey {
441 let mont_point = self.inner.to_montgomery();
442 x25519_dalek::PublicKey::from(mont_point.to_bytes())
443 }
444}
445
446impl SequentialCommit for PublicKey {
447 type Commitment = Word;
448
449 fn to_elements(&self) -> Vec<Felt> {
450 bytes_to_packed_u32_elements(&self.to_bytes())
451 }
452}
453
454#[derive(Debug, Error)]
455pub enum PublicKeyError {
456 #[error("Could not verify with given public key and signature")]
457 VerificationFailed,
458}
459
460#[derive(Debug, Error)]
462pub enum UncheckedVerificationError {
463 #[error("challenge scalar is not canonical")]
464 NonCanonicalScalar,
465 #[error("signature R component failed to decompress")]
466 InvalidSignaturePoint,
467 #[error("small-order component detected in signature R")]
468 SmallOrderSignature,
469 #[error("small-order component detected in public key")]
470 SmallOrderPublicKey,
471 #[error("verification equation was not satisfied")]
472 EquationMismatch,
473}
474
475#[derive(Debug, Clone, PartialEq, Eq)]
480#[cfg_attr(
481 all(feature = "arbitrary", test),
482 miden_test_serialization_macros::serialization_test
483)]
484pub struct Signature {
485 inner: ed25519_dalek::Signature,
486}
487
488impl Signature {
489 pub fn verify(&self, message: Word, pub_key: &PublicKey) -> bool {
491 pub_key.verify(message, self)
492 }
493
494 pub fn from_der(bytes: &[u8]) -> Result<Self, DeserializationError> {
500 if bytes.len() == SIGNATURE_BYTES {
501 let inner = ed25519_dalek::Signature::from_bytes(
502 bytes.try_into().expect("length verified above"),
503 );
504 return Ok(Self { inner });
505 }
506
507 let bit_string = BitStringRef::from_der(bytes)
508 .map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
509
510 let raw = bit_string.as_bytes().ok_or_else(|| {
511 DeserializationError::InvalidValue("BIT STRING has non-zero unused bits".into())
512 })?;
513
514 let sig_bytes: &[u8; SIGNATURE_BYTES] = raw.try_into().map_err(|e| {
515 DeserializationError::InvalidValue(alloc::format!(
516 "expected {SIGNATURE_BYTES} signature bytes, got {}: {e}",
517 raw.len()
518 ))
519 })?;
520
521 Ok(Self {
522 inner: ed25519_dalek::Signature::from_bytes(sig_bytes),
523 })
524 }
525}
526
527impl Serializable for SecretKey {
531 fn write_into<W: ByteWriter>(&self, target: &mut W) {
532 target.write_bytes(&self.inner.to_bytes());
533 }
534}
535
536impl Deserializable for SecretKey {
537 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
538 let bytes = read_sensitive_array::<SECRET_KEY_BYTES, _>(source)?;
539 let inner = ed25519_dalek::SigningKey::from_bytes(&bytes);
540
541 Ok(Self { inner })
542 }
543}
544
545impl Serializable for PublicKey {
546 fn write_into<W: ByteWriter>(&self, target: &mut W) {
547 target.write_bytes(&self.inner.to_bytes());
548 }
549}
550
551impl Deserializable for PublicKey {
552 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
553 let bytes: [u8; PUBLIC_KEY_BYTES] = source.read_array()?;
554 let inner = ed25519_dalek::VerifyingKey::from_bytes(&bytes).map_err(|_| {
555 DeserializationError::InvalidValue("Invalid Ed25519 public key".to_string())
556 })?;
557 Ok(Self { inner })
558 }
559}
560
561impl fmt::Display for PublicKey {
562 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563 crate::utils::write_hex(f, &self.to_bytes())
564 }
565}
566
567impl Serializable for Signature {
568 fn write_into<W: ByteWriter>(&self, target: &mut W) {
569 target.write_bytes(&self.inner.to_bytes())
570 }
571}
572
573impl Deserializable for Signature {
574 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
575 let bytes: [u8; SIGNATURE_BYTES] = source.read_array()?;
576 let inner = ed25519_dalek::Signature::from_bytes(&bytes);
577 Ok(Self { inner })
578 }
579}
580
581impl fmt::Display for Signature {
582 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583 crate::utils::write_hex(f, &self.to_bytes())
584 }
585}
586
587#[cfg(any(test, feature = "arbitrary"))]
591mod key_arbitrary {
592 use proptest::prelude::*;
593
594 use super::{
595 KeyExchangeKey, PublicKey, SECRET_KEY_BYTES, SIGNATURE_BYTES, SecretKey, Signature,
596 SigningKey,
597 };
598
599 impl Arbitrary for SecretKey {
600 type Parameters = ();
601 type Strategy = BoxedStrategy<Self>;
602
603 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
604 any::<[u8; SECRET_KEY_BYTES]>()
606 .prop_map(|bytes| Self {
607 inner: ed25519_dalek::SigningKey::from_bytes(&bytes),
608 })
609 .boxed()
610 }
611 }
612
613 impl Arbitrary for SigningKey {
614 type Parameters = ();
615 type Strategy = BoxedStrategy<Self>;
616
617 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
618 any::<SecretKey>().prop_map(Self).boxed()
619 }
620 }
621
622 impl Arbitrary for KeyExchangeKey {
623 type Parameters = ();
624 type Strategy = BoxedStrategy<Self>;
625
626 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
627 any::<SecretKey>().prop_map(Self).boxed()
628 }
629 }
630
631 impl Arbitrary for PublicKey {
632 type Parameters = ();
633 type Strategy = BoxedStrategy<Self>;
634
635 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
636 any::<SigningKey>()
639 .prop_map(|signing_key| Self {
640 inner: signing_key.0.inner.verifying_key(),
641 })
642 .boxed()
643 }
644 }
645
646 impl Arbitrary for Signature {
647 type Parameters = ();
648 type Strategy = BoxedStrategy<Self>;
649
650 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
651 any::<[u8; SIGNATURE_BYTES]>()
653 .prop_map(|bytes| Self {
654 inner: ed25519_dalek::Signature::from_bytes(&bytes),
655 })
656 .boxed()
657 }
658 }
659}