use alloc::{vec, vec::Vec};
use miden_core::{
EventName,
precompile::{PrecompileCommitment, PrecompileError, PrecompileRequest, PrecompileVerifier},
utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
};
use miden_crypto::{
ZERO,
dsa::ecdsa_k256_keccak::{PublicKey, Signature},
hash::rpo::Rpo256,
};
use miden_processor::{AdviceMutation, EventError, EventHandler, ProcessState};
use crate::handlers::{bytes_to_packed_u32_felts, read_memory_packed_u32};
pub const ECDSA_VERIFY_EVENT_NAME: EventName = EventName::new("stdlib::crypto::dsa::ecdsa::verify");
const PUBLIC_KEY_LEN_BYTES: usize = 33;
const MESSAGE_DIGEST_LEN_BYTES: usize = 32;
const SIGNATURE_LEN_BYTES: usize = 66;
const PRECOMPILE_REQUEST_LEN: usize =
PUBLIC_KEY_LEN_BYTES + MESSAGE_DIGEST_LEN_BYTES + SIGNATURE_LEN_BYTES;
pub struct EcdsaPrecompile;
impl EventHandler for EcdsaPrecompile {
fn on_event(&self, process: &ProcessState) -> Result<Vec<AdviceMutation>, EventError> {
let ptr_pk = process.get_stack_item(1).as_int();
let ptr_digest = process.get_stack_item(2).as_int();
let ptr_sig = process.get_stack_item(3).as_int();
let pk = {
let data_type = DataType::PublicKey;
let bytes = read_memory_packed_u32(process, ptr_pk, PUBLIC_KEY_LEN_BYTES)
.map_err(|source| EcdsaError::ReadError { data_type, source })?;
PublicKey::read_from_bytes(&bytes)
.map_err(|source| EcdsaError::DeserializeError { data_type, source })?
};
let sig = {
let data_type = DataType::Signature;
let bytes = read_memory_packed_u32(process, ptr_sig, SIGNATURE_LEN_BYTES)
.map_err(|source| EcdsaError::ReadError { data_type, source })?;
Signature::read_from_bytes(&bytes)
.map_err(|source| EcdsaError::DeserializeError { data_type, source })?
};
let digest = read_memory_packed_u32(process, ptr_digest, MESSAGE_DIGEST_LEN_BYTES)
.map_err(|source| EcdsaError::ReadError { data_type: DataType::Digest, source })?
.try_into()
.expect("digest is exactly 32 bytes");
let request = EcdsaRequest::new(pk, digest, sig);
let result = request.result();
Ok(vec![
AdviceMutation::extend_stack([result.into()]),
AdviceMutation::extend_precompile_requests([request.into()]),
])
}
}
impl PrecompileVerifier for EcdsaPrecompile {
fn verify(&self, calldata: &[u8]) -> Result<PrecompileCommitment, PrecompileError> {
let request = EcdsaRequest::read_from_bytes(calldata)?;
Ok(request.as_precompile_commitment())
}
}
pub struct EcdsaRequest {
pk: PublicKey,
digest: [u8; MESSAGE_DIGEST_LEN_BYTES],
sig: Signature,
}
impl EcdsaRequest {
pub fn new(pk: PublicKey, digest: [u8; MESSAGE_DIGEST_LEN_BYTES], sig: Signature) -> Self {
Self { pk, digest, sig }
}
pub fn pk(&self) -> &PublicKey {
&self.pk
}
pub fn digest(&self) -> &[u8; MESSAGE_DIGEST_LEN_BYTES] {
&self.digest
}
pub fn sig(&self) -> &Signature {
&self.sig
}
pub fn as_precompile_request(&self) -> PrecompileRequest {
let mut calldata = Vec::with_capacity(PRECOMPILE_REQUEST_LEN);
self.write_into(&mut calldata);
PrecompileRequest::new(ECDSA_VERIFY_EVENT_NAME.to_event_id(), calldata)
}
pub fn result(&self) -> bool {
self.pk.verify_prehash(self.digest, &self.sig)
}
pub fn as_precompile_commitment(&self) -> PrecompileCommitment {
let result = self.result().into();
let tag = [ECDSA_VERIFY_EVENT_NAME.to_event_id().as_felt(), result, ZERO, ZERO].into();
let pk_comm = {
let felts = bytes_to_packed_u32_felts(&self.pk.to_bytes());
Rpo256::hash_elements(&felts)
};
let digest_comm = {
let felts = bytes_to_packed_u32_felts(&self.digest);
Rpo256::hash_elements(&felts)
};
let sig_comm = {
let felts = bytes_to_packed_u32_felts(&self.sig.to_bytes());
Rpo256::hash_elements(&felts)
};
let commitment = Rpo256::merge(&[Rpo256::merge(&[pk_comm, digest_comm]), sig_comm]);
PrecompileCommitment::new(tag, commitment)
}
}
impl Serializable for EcdsaRequest {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.pk.write_into(target);
self.digest.write_into(target);
self.sig.write_into(target);
}
}
impl Deserializable for EcdsaRequest {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let pk = PublicKey::read_from(source)?;
let digest = source.read_array()?;
let sig = Signature::read_from(source)?;
Ok(Self { pk, digest, sig })
}
}
impl From<EcdsaRequest> for PrecompileRequest {
fn from(request: EcdsaRequest) -> Self {
request.as_precompile_request()
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum DataType {
PublicKey,
Signature,
Digest,
}
impl core::fmt::Display for DataType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DataType::PublicKey => write!(f, "public key"),
DataType::Signature => write!(f, "signature"),
DataType::Digest => write!(f, "digest"),
}
}
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum EcdsaError {
#[error("failed to read {data_type} from memory")]
ReadError {
data_type: DataType,
#[source]
source: crate::handlers::MemoryReadError,
},
#[error("failed to deserialize {data_type}")]
DeserializeError {
data_type: DataType,
#[source]
source: DeserializationError,
},
}