use alloc::{string::ToString, vec::Vec};
use k256::{
ecdh::diffie_hellman,
ecdsa,
ecdsa::{RecoveryId, VerifyingKey, signature::hazmat::PrehashVerifier},
pkcs8::DecodePublicKey,
};
use miden_crypto_derive::{SilentDebug, SilentDisplay};
use rand::{CryptoRng, RngCore};
use thiserror::Error;
use crate::{
Felt, SequentialCommit, Word,
ecdh::k256::{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 = 33;
const SIGNATURE_BYTES: usize = 65;
const SIGNATURE_STANDARD_BYTES: usize = 64;
const SCALARS_SIZE_BYTES: usize = 32;
#[derive(Clone, SilentDebug, SilentDisplay)]
struct SecretKey {
inner: ecdsa::SigningKey,
}
impl SecretKey {
fn with_rng<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
use k256::elliptic_curve::rand_core::SeedableRng;
let mut seed = [0_u8; 32];
RngCore::fill_bytes(rng, &mut seed);
let mut rng = rand_hc::Hc128Rng::from_seed(seed);
let signing_key = ecdsa::SigningKey::random(&mut rng);
seed.zeroize();
Self { inner: signing_key }
}
fn public_key(&self) -> PublicKey {
let verifying_key = self.inner.verifying_key();
PublicKey { inner: *verifying_key }
}
fn sign(&self, message: Word) -> Signature {
let message_digest = hash_message(message);
self.sign_prehash(message_digest)
}
fn sign_prehash(&self, message_digest: [u8; 32]) -> Signature {
let (signature_inner, recovery_id) = self
.inner
.sign_prehash_recoverable(&message_digest)
.expect("failed to generate signature");
let (r, s) = signature_inner.split_scalars();
Signature {
r: r.to_bytes().into(),
s: s.to_bytes().into(),
v: recovery_id.into(),
}
}
fn get_shared_secret(&self, pk_e: EphemeralPublicKey) -> SharedSecret {
let shared_secret_inner = diffie_hellman(self.inner.as_nonzero_scalar(), pk_e.as_affine());
SharedSecret::new(shared_secret_inner)
}
}
impl ZeroizeOnDrop for SecretKey {}
impl PartialEq for SecretKey {
fn eq(&self, other: &Self) -> bool {
use subtle::ConstantTimeEq;
self.to_bytes().ct_eq(&other.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)
}
pub fn sign_prehash(&self, message_digest: [u8; 32]) -> Signature {
self.0.sign_prehash(message_digest)
}
}
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(value: SecretKey) -> Self {
Self(value)
}
}
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: 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_digest = hash_message(message);
self.verify_prehash(message_digest, signature)
}
pub fn verify_prehash(&self, message_digest: [u8; 32], signature: &Signature) -> bool {
let signature_inner = ecdsa::Signature::from_scalars(*signature.r(), *signature.s());
match signature_inner {
Ok(signature) => self.inner.verify_prehash(&message_digest, &signature).is_ok(),
Err(_) => false,
}
}
pub fn recover_from(message: Word, signature: &Signature) -> Result<Self, PublicKeyError> {
let message_digest = hash_message(message);
let signature_data = ecdsa::Signature::from_scalars(*signature.r(), *signature.s())
.map_err(|_| PublicKeyError::RecoveryFailed)?;
let verifying_key = VerifyingKey::recover_from_prehash(
&message_digest,
&signature_data,
RecoveryId::from_byte(signature.v()).ok_or(PublicKeyError::RecoveryFailed)?,
)
.map_err(|_| PublicKeyError::RecoveryFailed)?;
Ok(Self { inner: verifying_key })
}
pub fn from_der(bytes: &[u8]) -> Result<Self, DeserializationError> {
let verifying_key = VerifyingKey::from_public_key_der(bytes)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
Ok(PublicKey { inner: verifying_key })
}
}
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 recover the public key from the message and signature")]
RecoveryFailed,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature {
r: [u8; SCALARS_SIZE_BYTES],
s: [u8; SCALARS_SIZE_BYTES],
v: u8,
}
impl Signature {
pub fn r(&self) -> &[u8; SCALARS_SIZE_BYTES] {
&self.r
}
pub fn s(&self) -> &[u8; SCALARS_SIZE_BYTES] {
&self.s
}
pub fn v(&self) -> u8 {
self.v
}
pub fn verify(&self, message: Word, pub_key: &PublicKey) -> bool {
pub_key.verify(message, self)
}
pub fn to_sec1_bytes(&self) -> [u8; SIGNATURE_STANDARD_BYTES] {
let mut bytes = [0u8; 2 * SCALARS_SIZE_BYTES];
bytes[0..SCALARS_SIZE_BYTES].copy_from_slice(self.r());
bytes[SCALARS_SIZE_BYTES..2 * SCALARS_SIZE_BYTES].copy_from_slice(self.s());
bytes
}
pub fn from_sec1_bytes_and_recovery_id(
bytes: [u8; SIGNATURE_STANDARD_BYTES],
recovery_id: u8,
) -> Result<Self, DeserializationError> {
let mut r = [0u8; SCALARS_SIZE_BYTES];
let mut s = [0u8; SCALARS_SIZE_BYTES];
r.copy_from_slice(&bytes[0..SCALARS_SIZE_BYTES]);
s.copy_from_slice(&bytes[SCALARS_SIZE_BYTES..2 * SCALARS_SIZE_BYTES]);
if recovery_id > 3 {
return Err(DeserializationError::InvalidValue(r#"Invalid recovery ID"#.to_string()));
}
Ok(Signature { r, s, v: recovery_id })
}
pub fn from_der(bytes: &[u8], mut recovery_id: u8) -> Result<Self, DeserializationError> {
if recovery_id > 3 {
return Err(DeserializationError::InvalidValue(r#"Invalid recovery ID"#.to_string()));
}
let sig = ecdsa::Signature::from_der(bytes)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
let sig = if let Some(norm) = sig.normalize_s() {
recovery_id ^= 1;
norm
} else {
sig
};
let (r, s) = sig.split_scalars();
Ok(Signature {
r: r.to_bytes().into(),
s: s.to_bytes().into(),
v: recovery_id,
})
}
}
impl Serializable for SecretKey {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let mut buffer = Vec::with_capacity(SECRET_KEY_BYTES);
let sk_bytes: [u8; SECRET_KEY_BYTES] = self.inner.to_bytes().into();
buffer.extend_from_slice(&sk_bytes);
target.write_bytes(&buffer);
}
}
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 signing_key = ecdsa::SigningKey::from_slice(&bytes)
.map_err(|_| DeserializationError::InvalidValue("Invalid secret key".to_string()))?;
bytes.zeroize();
Ok(Self { inner: signing_key })
}
}
impl Serializable for PublicKey {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let encoded = self.inner.to_encoded_point(true);
target.write_bytes(encoded.as_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 verifying_key = VerifyingKey::from_sec1_bytes(&bytes)
.map_err(|_| DeserializationError::InvalidValue("Invalid public key".to_string()))?;
Ok(Self { inner: verifying_key })
}
}
impl Serializable for Signature {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let mut bytes = [0u8; SIGNATURE_BYTES];
bytes[0..SCALARS_SIZE_BYTES].copy_from_slice(self.r());
bytes[SCALARS_SIZE_BYTES..2 * SCALARS_SIZE_BYTES].copy_from_slice(self.s());
bytes[2 * SCALARS_SIZE_BYTES] = self.v();
target.write_bytes(&bytes);
}
}
impl Deserializable for Signature {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let r: [u8; SCALARS_SIZE_BYTES] = source.read_array()?;
let s: [u8; SCALARS_SIZE_BYTES] = source.read_array()?;
let v: u8 = source.read_u8()?;
if v > 3 {
Err(DeserializationError::InvalidValue(r#"Invalid recovery ID"#.to_string()))
} else {
Ok(Signature { r, s, v })
}
}
}
fn hash_message(message: Word) -> [u8; 32] {
use sha3::{Digest, Keccak256};
let mut hasher = Keccak256::new();
let message_bytes: [u8; 32] = message.into();
hasher.update(message_bytes);
hasher.finalize().into()
}