use alloc::{string::ToString, vec::Vec};
use der::{Decode, asn1::BitStringRef};
use ed25519_dalek::{Signer, Verifier};
use miden_crypto_derive::{SilentDebug, SilentDisplay};
use rand::{CryptoRng, RngCore};
use thiserror::Error;
use crate::{
Felt, SequentialCommit, Word,
ecdh::x25519::{EphemeralPublicKey, SharedSecret},
utils::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
bytes_to_packed_u32_elements,
zeroize::{Zeroize, ZeroizeOnDrop},
},
};
mod tests;
const SECRET_KEY_BYTES: usize = 32;
pub(crate) const PUBLIC_KEY_BYTES: usize = 32;
const SIGNATURE_BYTES: usize = 64;
#[derive(Clone, SilentDebug, SilentDisplay)]
struct SecretKey {
inner: ed25519_dalek::SigningKey,
}
impl SecretKey {
fn with_rng<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
let mut seed = [0u8; SECRET_KEY_BYTES];
RngCore::fill_bytes(rng, &mut seed);
let inner = ed25519_dalek::SigningKey::from_bytes(&seed);
seed.zeroize();
Self { inner }
}
fn public_key(&self) -> PublicKey {
PublicKey { inner: self.inner.verifying_key() }
}
fn sign(&self, message: Word) -> Signature {
let message_bytes: [u8; 32] = message.into();
let sig = self.inner.sign(&message_bytes);
Signature { inner: sig }
}
fn get_shared_secret(&self, pk_e: EphemeralPublicKey) -> SharedSecret {
let shared = self.to_x25519().diffie_hellman(&pk_e.inner);
SharedSecret::new(shared)
}
fn to_x25519(&self) -> x25519_dalek::StaticSecret {
let mut scalar_bytes = self.inner.to_scalar_bytes();
let static_secret = x25519_dalek::StaticSecret::from(scalar_bytes);
scalar_bytes.zeroize();
static_secret
}
}
impl ZeroizeOnDrop for SecretKey {}
impl PartialEq for SecretKey {
fn eq(&self, other: &Self) -> bool {
use subtle::ConstantTimeEq;
self.inner.to_bytes().ct_eq(&other.inner.to_bytes()).into()
}
}
impl Eq for SecretKey {}
#[derive(Clone, Eq, PartialEq, SilentDebug, SilentDisplay)] pub struct SigningKey(SecretKey);
impl SigningKey {
#[cfg(feature = "std")]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let mut rng = rand::rng();
Self::with_rng(&mut rng)
}
pub fn with_rng<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
Self(SecretKey::with_rng(rng))
}
pub fn public_key(&self) -> PublicKey {
self.0.public_key()
}
pub fn sign(&self, message: Word) -> Signature {
self.0.sign(message)
}
}
impl From<SecretKey> for SigningKey {
fn from(secret_key: SecretKey) -> Self {
Self(secret_key)
}
}
impl ZeroizeOnDrop for SigningKey {}
impl Serializable for SigningKey {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.0.write_into(target);
}
}
impl Deserializable for SigningKey {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
Ok(Self(SecretKey::read_from(source)?))
}
}
#[derive(Clone, Eq, PartialEq, SilentDebug, SilentDisplay)] pub struct KeyExchangeKey(SecretKey);
impl KeyExchangeKey {
#[cfg(feature = "std")]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let mut rng = rand::rng();
Self::with_rng(&mut rng)
}
pub fn with_rng<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
Self(SecretKey::with_rng(rng))
}
pub fn public_key(&self) -> PublicKey {
self.0.public_key()
}
pub fn get_shared_secret(&self, pk_e: EphemeralPublicKey) -> SharedSecret {
self.0.get_shared_secret(pk_e)
}
}
impl From<SecretKey> for KeyExchangeKey {
fn from(secret_key: SecretKey) -> Self {
Self(secret_key)
}
}
impl ZeroizeOnDrop for KeyExchangeKey {}
impl Serializable for KeyExchangeKey {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.0.write_into(target);
}
}
impl Deserializable for KeyExchangeKey {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
Ok(Self(SecretKey::read_from(source)?))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicKey {
pub(crate) inner: ed25519_dalek::VerifyingKey,
}
impl PublicKey {
pub fn to_commitment(&self) -> Word {
<Self as SequentialCommit>::to_commitment(self)
}
pub fn verify(&self, message: Word, signature: &Signature) -> bool {
let message_bytes: [u8; 32] = message.into();
self.inner.verify(&message_bytes, &signature.inner).is_ok()
}
pub fn compute_challenge_k(&self, message: Word, signature: &Signature) -> [u8; 64] {
use sha2::Digest;
let message_bytes: [u8; 32] = message.into();
let sig_bytes = signature.inner.to_bytes();
let r_bytes = &sig_bytes[0..32];
let mut hasher = sha2::Sha512::new();
hasher.update(r_bytes);
hasher.update(self.inner.to_bytes());
hasher.update(message_bytes);
let k_hash = hasher.finalize();
k_hash.into()
}
pub fn verify_with_unchecked_k(
&self,
k_hash: [u8; 64],
signature: &Signature,
) -> Result<(), UncheckedVerificationError> {
use curve25519_dalek::{
edwards::{CompressedEdwardsY, EdwardsPoint},
scalar::Scalar,
};
let k_scalar = Scalar::from_bytes_mod_order_wide(&k_hash);
let sig_bytes = signature.inner.to_bytes();
let r_bytes: [u8; 32] =
sig_bytes[..32].try_into().expect("signature R component is exactly 32 bytes");
let s_bytes: [u8; 32] =
sig_bytes[32..].try_into().expect("signature s component is exactly 32 bytes");
let s_candidate = Scalar::from_canonical_bytes(s_bytes);
if s_candidate.is_none().into() {
return Err(UncheckedVerificationError::NonCanonicalScalar);
}
let s_scalar = s_candidate.unwrap();
let r_compressed = CompressedEdwardsY(r_bytes);
let Some(r_point) = r_compressed.decompress() else {
return Err(UncheckedVerificationError::InvalidSignaturePoint);
};
let a_point = self.inner.to_edwards();
if r_point.is_small_order() {
return Err(UncheckedVerificationError::SmallOrderSignature);
}
if a_point.is_small_order() {
return Err(UncheckedVerificationError::SmallOrderPublicKey);
}
let minus_a = -a_point;
let expected_r =
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k_scalar, &minus_a, &s_scalar)
.compress();
if expected_r == r_compressed {
Ok(())
} else {
Err(UncheckedVerificationError::EquationMismatch)
}
}
pub(crate) fn to_x25519(&self) -> x25519_dalek::PublicKey {
let mont_point = self.inner.to_montgomery();
x25519_dalek::PublicKey::from(mont_point.to_bytes())
}
}
impl SequentialCommit for PublicKey {
type Commitment = Word;
fn to_elements(&self) -> Vec<Felt> {
bytes_to_packed_u32_elements(&self.to_bytes())
}
}
#[derive(Debug, Error)]
pub enum PublicKeyError {
#[error("Could not verify with given public key and signature")]
VerificationFailed,
}
#[derive(Debug, Error)]
pub enum UncheckedVerificationError {
#[error("challenge scalar is not canonical")]
NonCanonicalScalar,
#[error("signature R component failed to decompress")]
InvalidSignaturePoint,
#[error("small-order component detected in signature R")]
SmallOrderSignature,
#[error("small-order component detected in public key")]
SmallOrderPublicKey,
#[error("verification equation was not satisfied")]
EquationMismatch,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature {
inner: ed25519_dalek::Signature,
}
impl Signature {
pub fn verify(&self, message: Word, pub_key: &PublicKey) -> bool {
pub_key.verify(message, self)
}
pub fn from_der(bytes: &[u8]) -> Result<Self, DeserializationError> {
if bytes.len() == SIGNATURE_BYTES {
let inner = ed25519_dalek::Signature::from_bytes(
bytes.try_into().expect("length verified above"),
);
return Ok(Self { inner });
}
let bit_string = BitStringRef::from_der(bytes)
.map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
let raw = bit_string.as_bytes().ok_or_else(|| {
DeserializationError::InvalidValue("BIT STRING has non-zero unused bits".into())
})?;
let sig_bytes: &[u8; SIGNATURE_BYTES] = raw.try_into().map_err(|e| {
DeserializationError::InvalidValue(alloc::format!(
"expected {SIGNATURE_BYTES} signature bytes, got {}: {e}",
raw.len()
))
})?;
Ok(Self {
inner: ed25519_dalek::Signature::from_bytes(sig_bytes),
})
}
}
impl Serializable for SecretKey {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(&self.inner.to_bytes());
}
}
impl Deserializable for SecretKey {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let mut bytes: [u8; SECRET_KEY_BYTES] = source.read_array()?;
let inner = ed25519_dalek::SigningKey::from_bytes(&bytes);
bytes.zeroize();
Ok(Self { inner })
}
}
impl Serializable for PublicKey {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(&self.inner.to_bytes());
}
}
impl Deserializable for PublicKey {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let bytes: [u8; PUBLIC_KEY_BYTES] = source.read_array()?;
let inner = ed25519_dalek::VerifyingKey::from_bytes(&bytes).map_err(|_| {
DeserializationError::InvalidValue("Invalid Ed25519 public key".to_string())
})?;
Ok(Self { inner })
}
}
impl Serializable for Signature {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(&self.inner.to_bytes())
}
}
impl Deserializable for Signature {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let bytes: [u8; SIGNATURE_BYTES] = source.read_array()?;
let inner = ed25519_dalek::Signature::from_bytes(&bytes);
Ok(Self { inner })
}
}