diff --git a/crates/precompile/src/blake2.rs b/crates/precompile/src/blake2/crypto.rs similarity index 85% rename from crates/precompile/src/blake2.rs rename to crates/precompile/src/blake2/crypto.rs index 68ad60d64b..929ebdb3f6 100644 --- a/crates/precompile/src/blake2.rs +++ b/crates/precompile/src/blake2/crypto.rs @@ -1,68 +1,7 @@ -//! Blake2 precompile. More details in [`run`] - -use crate::{ - crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, -}; - -const F_ROUND: u64 = 1; -const INPUT_LENGTH: usize = 213; - -/// Blake2 precompile -pub const FUN: Precompile = Precompile::new(PrecompileId::Blake2F, crate::u64_to_address(9), run); - -/// reference: -/// input format: -/// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] -pub fn run(input: &[u8], gas_limit: u64) -> PrecompileResult { - if input.len() != INPUT_LENGTH { - return Err(PrecompileError::Blake2WrongLength); - } - - // Parse number of rounds (4 bytes) - let rounds = u32::from_be_bytes(input[..4].try_into().unwrap()); - let gas_used = rounds as u64 * F_ROUND; - if gas_used > gas_limit { - return Err(PrecompileError::OutOfGas); - } - - // Parse final block flag - let f = match input[212] { - 0 => false, - 1 => true, - _ => return Err(PrecompileError::Blake2WrongFinalIndicatorFlag), - }; - - // Parse state vector h (8 × u64) - let mut h = [0u64; 8]; - input[4..68] - .chunks_exact(8) - .enumerate() - .for_each(|(i, chunk)| { - h[i] = u64::from_le_bytes(chunk.try_into().unwrap()); - }); - - // Parse message block m (16 × u64) - let mut m = [0u64; 16]; - input[68..196] - .chunks_exact(8) - .enumerate() - .for_each(|(i, chunk)| { - m[i] = u64::from_le_bytes(chunk.try_into().unwrap()); - }); - - // Parse offset counters - let t_0 = u64::from_le_bytes(input[196..204].try_into().unwrap()); - let t_1 = u64::from_le_bytes(input[204..212].try_into().unwrap()); - - crypto().blake2_compress(rounds, &mut h, &m, &[t_0, t_1], f); - - let mut out = [0u8; 64]; - for (i, h) in (0..64).step_by(8).zip(h.iter()) { - out[i..i + 8].copy_from_slice(&h.to_le_bytes()); - } - - Ok(PrecompileOutput::new(gas_used, out.into())) -} +//! Pure cryptographic implementation for BLAKE2b compression. +//! +//! This module contains the BLAKE2b compression function and its optimized +//! AVX2 variant, isolated from the precompile runner logic. /// Blake2 algorithm pub mod algo { @@ -627,23 +566,3 @@ mod avx2 { *c = _mm256_permute4x64_epi64(*c, _MM_SHUFFLE!(2, 1, 0, 3)); } } - -#[cfg(test)] -mod tests { - use super::*; - use primitives::hex; - use std::time::Instant; - - #[test] - fn perfblake2() { - let input = [hex!("0000040048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b616162636465666768696a6b6c6d6e6f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001") - ,hex!("0000020048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001") - ,hex!("0000004048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001")]; - - let time = Instant::now(); - for i in 0..3000 { - let _ = run(&input[i % 3], u64::MAX).unwrap(); - } - println!("duration: {:?}", time.elapsed()); - } -} diff --git a/crates/precompile/src/blake2/mod.rs b/crates/precompile/src/blake2/mod.rs new file mode 100644 index 0000000000..58facb0786 --- /dev/null +++ b/crates/precompile/src/blake2/mod.rs @@ -0,0 +1,91 @@ +//! Blake2 precompile. More details in [`run`] + +pub mod crypto; + +/// Re-export for backward compatibility. +pub use crypto::algo; + +use crate::{ + crypto as crypto_provider, Precompile, PrecompileError, PrecompileId, PrecompileOutput, + PrecompileResult, +}; + +const F_ROUND: u64 = 1; +const INPUT_LENGTH: usize = 213; + +/// Blake2 precompile +pub const FUN: Precompile = Precompile::new(PrecompileId::Blake2F, crate::u64_to_address(9), run); + +/// reference: +/// input format: +/// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] +pub fn run(input: &[u8], gas_limit: u64) -> PrecompileResult { + if input.len() != INPUT_LENGTH { + return Err(PrecompileError::Blake2WrongLength); + } + + // Parse number of rounds (4 bytes) + let rounds = u32::from_be_bytes(input[..4].try_into().unwrap()); + let gas_used = rounds as u64 * F_ROUND; + if gas_used > gas_limit { + return Err(PrecompileError::OutOfGas); + } + + // Parse final block flag + let f = match input[212] { + 0 => false, + 1 => true, + _ => return Err(PrecompileError::Blake2WrongFinalIndicatorFlag), + }; + + // Parse state vector h (8 × u64) + let mut h = [0u64; 8]; + input[4..68] + .chunks_exact(8) + .enumerate() + .for_each(|(i, chunk)| { + h[i] = u64::from_le_bytes(chunk.try_into().unwrap()); + }); + + // Parse message block m (16 × u64) + let mut m = [0u64; 16]; + input[68..196] + .chunks_exact(8) + .enumerate() + .for_each(|(i, chunk)| { + m[i] = u64::from_le_bytes(chunk.try_into().unwrap()); + }); + + // Parse offset counters + let t_0 = u64::from_le_bytes(input[196..204].try_into().unwrap()); + let t_1 = u64::from_le_bytes(input[204..212].try_into().unwrap()); + + crypto_provider().blake2_compress(rounds, &mut h, &m, &[t_0, t_1], f); + + let mut out = [0u8; 64]; + for (i, h) in (0..64).step_by(8).zip(h.iter()) { + out[i..i + 8].copy_from_slice(&h.to_le_bytes()); + } + + Ok(PrecompileOutput::new(gas_used, out.into())) +} + +#[cfg(test)] +mod tests { + use super::*; + use primitives::hex; + use std::time::Instant; + + #[test] + fn perfblake2() { + let input = [hex!("0000040048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b616162636465666768696a6b6c6d6e6f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001") + ,hex!("0000020048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001") + ,hex!("0000004048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001")]; + + let time = Instant::now(); + for i in 0..3000 { + let _ = run(&input[i % 3], u64::MAX).unwrap(); + } + println!("duration: {:?}", time.elapsed()); + } +} diff --git a/crates/precompile/src/modexp/crypto.rs b/crates/precompile/src/modexp/crypto.rs new file mode 100644 index 0000000000..632eeb6c27 --- /dev/null +++ b/crates/precompile/src/modexp/crypto.rs @@ -0,0 +1,113 @@ +//! Pure cryptographic implementation for modular exponentiation. +//! +//! This module isolates the modexp computation from the precompile runner, +//! containing only the mathematical implementation without EVM-specific concerns. + +use std::vec::Vec; + +#[cfg(feature = "gmp")] +/// GMP-based modular exponentiation implementation +pub(crate) fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec { + use core::ffi::c_void; + use core::mem::MaybeUninit; + use gmp_mpfr_sys::gmp; + + struct Mpz(gmp::mpz_t); + + impl Mpz { + fn new() -> Self { + unsafe { + let mut inner = MaybeUninit::::uninit(); + gmp::mpz_init(inner.as_mut_ptr()); + Self(inner.assume_init()) + } + } + + fn as_ptr(&self) -> *const gmp::mpz_t { + &self.0 + } + + fn as_mut_ptr(&mut self) -> *mut gmp::mpz_t { + &mut self.0 + } + + fn set_from_be_bytes(&mut self, bytes: &[u8]) { + unsafe { + if bytes.is_empty() { + gmp::mpz_set_ui(self.as_mut_ptr(), 0); + return; + } + + gmp::mpz_import( + self.as_mut_ptr(), + bytes.len(), + 1, + 1, + 1, + 0, + bytes.as_ptr() as *const c_void, + ); + } + } + + fn to_be_bytes(&self) -> Vec { + unsafe { + if gmp::mpz_sgn(self.as_ptr()) == 0 { + return Vec::new(); + } + + let bits = gmp::mpz_sizeinbase(self.as_ptr(), 2); + let mut output = vec![0u8; bits.div_ceil(8)]; + let mut count: usize = 0; + gmp::mpz_export( + output.as_mut_ptr() as *mut c_void, + &mut count, + 1, + 1, + 1, + 0, + self.as_ptr(), + ); + output.truncate(count); + output + } + } + } + + impl Drop for Mpz { + fn drop(&mut self) { + unsafe { + gmp::mpz_clear(self.as_mut_ptr()); + } + } + } + + let mut base_int = Mpz::new(); + let mut exp_int = Mpz::new(); + let mut mod_int = Mpz::new(); + let mut result = Mpz::new(); + + base_int.set_from_be_bytes(base); + exp_int.set_from_be_bytes(exponent); + mod_int.set_from_be_bytes(modulus); + + unsafe { + if gmp::mpz_sgn(mod_int.as_ptr()) == 0 { + return Vec::new(); + } + + gmp::mpz_powm( + result.as_mut_ptr(), + base_int.as_ptr(), + exp_int.as_ptr(), + mod_int.as_ptr(), + ); + } + + result.to_be_bytes() +} + +#[cfg(not(feature = "gmp"))] +pub(crate) fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec { + aurora_engine_modexp::modexp(base, exponent, modulus) +} diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp/mod.rs similarity index 95% rename from crates/precompile/src/modexp.rs rename to crates/precompile/src/modexp/mod.rs index 0fc4beab77..70f84a0f85 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp/mod.rs @@ -1,13 +1,17 @@ //! Modexp precompile added in [`EIP-198`](https://eips.ethereum.org/EIPS/eip-198) //! and reprices in berlin hardfork with [`EIP-2565`](https://eips.ethereum.org/EIPS/eip-2565). + +pub mod crypto; + +pub(crate) use crypto::modexp; + use crate::{ - crypto, + crypto as crypto_provider, utilities::{left_pad, left_pad_vec_be, right_pad_vec, right_pad_with_offset}, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, }; use core::cmp::{max, min}; use primitives::{eip7823, Bytes, U256}; -use std::vec::Vec; /// `modexp` precompile with BYZANTIUM gas rules. pub const BYZANTIUM: Precompile = Precompile::new( @@ -24,113 +28,6 @@ pub const BERLIN: Precompile = pub const OSAKA: Precompile = Precompile::new(PrecompileId::ModExp, crate::u64_to_address(5), osaka_run); -#[cfg(feature = "gmp")] -/// GMP-based modular exponentiation implementation -pub(crate) fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec { - use core::ffi::c_void; - use core::mem::MaybeUninit; - use gmp_mpfr_sys::gmp; - - struct Mpz(gmp::mpz_t); - - impl Mpz { - fn new() -> Self { - unsafe { - let mut inner = MaybeUninit::::uninit(); - gmp::mpz_init(inner.as_mut_ptr()); - Self(inner.assume_init()) - } - } - - fn as_ptr(&self) -> *const gmp::mpz_t { - &self.0 - } - - fn as_mut_ptr(&mut self) -> *mut gmp::mpz_t { - &mut self.0 - } - - fn set_from_be_bytes(&mut self, bytes: &[u8]) { - unsafe { - if bytes.is_empty() { - gmp::mpz_set_ui(self.as_mut_ptr(), 0); - return; - } - - gmp::mpz_import( - self.as_mut_ptr(), - bytes.len(), - 1, - 1, - 1, - 0, - bytes.as_ptr() as *const c_void, - ); - } - } - - fn to_be_bytes(&self) -> Vec { - unsafe { - if gmp::mpz_sgn(self.as_ptr()) == 0 { - return Vec::new(); - } - - let bits = gmp::mpz_sizeinbase(self.as_ptr(), 2); - let mut output = vec![0u8; bits.div_ceil(8)]; - let mut count: usize = 0; - gmp::mpz_export( - output.as_mut_ptr() as *mut c_void, - &mut count, - 1, - 1, - 1, - 0, - self.as_ptr(), - ); - output.truncate(count); - output - } - } - } - - impl Drop for Mpz { - fn drop(&mut self) { - unsafe { - gmp::mpz_clear(self.as_mut_ptr()); - } - } - } - - let mut base_int = Mpz::new(); - let mut exp_int = Mpz::new(); - let mut mod_int = Mpz::new(); - let mut result = Mpz::new(); - - base_int.set_from_be_bytes(base); - exp_int.set_from_be_bytes(exponent); - mod_int.set_from_be_bytes(modulus); - - unsafe { - if gmp::mpz_sgn(mod_int.as_ptr()) == 0 { - return Vec::new(); - } - - gmp::mpz_powm( - result.as_mut_ptr(), - base_int.as_ptr(), - exp_int.as_ptr(), - mod_int.as_ptr(), - ); - } - - result.to_be_bytes() -} - -#[cfg(not(feature = "gmp"))] -pub(crate) fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec { - aurora_engine_modexp::modexp(base, exponent, modulus) -} - /// See: /// See: pub fn byzantium_run(input: &[u8], gas_limit: u64) -> PrecompileResult { @@ -245,7 +142,7 @@ where debug_assert_eq!(modulus.len(), mod_len); // Call the modexp. - let output = crypto().modexp(base, exponent, modulus)?; + let output = crypto_provider().modexp(base, exponent, modulus)?; // Ensure the output is exactly modulus length, as required by the spec. Ok(PrecompileOutput::new( gas_cost, diff --git a/crates/precompile/src/secp256k1.rs b/crates/precompile/src/secp256k1.rs index 58dbec8e50..e43a30a62b 100644 --- a/crates/precompile/src/secp256k1.rs +++ b/crates/precompile/src/secp256k1.rs @@ -14,11 +14,14 @@ //! [32 bytes for recovered address] #[cfg(feature = "secp256k1")] pub mod bitcoin_secp256k1; +pub mod crypto; pub mod k256; +pub(crate) use crypto::ecrecover_bytes; + use crate::{ - crypto, utilities::right_pad, Precompile, PrecompileError, PrecompileId, PrecompileOutput, - PrecompileResult, + crypto as crypto_provider, utilities::right_pad, Precompile, PrecompileError, PrecompileId, + PrecompileOutput, PrecompileResult, }; use primitives::{alloy_primitives::B512, Bytes, B256}; @@ -48,23 +51,8 @@ pub fn ec_recover_run(input: &[u8], gas_limit: u64) -> PrecompileResult { let recid = input[63] - 27; let sig = <&B512>::try_from(&input[64..128]).unwrap(); - let res = crypto().secp256k1_ecrecover(&sig.0, recid, &msg.0).ok(); + let res = crypto_provider().secp256k1_ecrecover(&sig.0, recid, &msg.0).ok(); let out = res.map(|o| o.to_vec().into()).unwrap_or_default(); Ok(PrecompileOutput::new(ECRECOVER_BASE, out)) } -pub(crate) fn ecrecover_bytes(sig: &[u8; 64], recid: u8, msg: &[u8; 32]) -> Option<[u8; 32]> { - match ecrecover(sig.into(), recid, msg.into()) { - Ok(address) => Some(address.0), - Err(_) => None, - } -} - -// Select the correct implementation based on the enabled features. -cfg_if::cfg_if! { - if #[cfg(feature = "secp256k1")] { - pub use bitcoin_secp256k1::ecrecover; - } else { - pub use k256::ecrecover; - } -} diff --git a/crates/precompile/src/secp256k1/crypto.rs b/crates/precompile/src/secp256k1/crypto.rs new file mode 100644 index 0000000000..d2d51da46e --- /dev/null +++ b/crates/precompile/src/secp256k1/crypto.rs @@ -0,0 +1,24 @@ +//! Pure cryptographic implementation for secp256k1 ecrecover. +//! +//! This module isolates the cryptographic logic from the precompile runner, +//! containing only the signature recovery implementation without EVM-specific concerns. + +// Select the correct implementation based on the enabled features. +cfg_if::cfg_if! { + if #[cfg(feature = "secp256k1")] { + pub use super::bitcoin_secp256k1::ecrecover; + } else { + pub use super::k256::ecrecover; + } +} + +/// Recover an Ethereum address from a secp256k1 ECDSA signature. +/// +/// Returns `Some(address)` as a 32-byte array (12 zero bytes + 20 address bytes) +/// if recovery succeeds, or `None` on failure. +pub(crate) fn ecrecover_bytes(sig: &[u8; 64], recid: u8, msg: &[u8; 32]) -> Option<[u8; 32]> { + match ecrecover(sig.into(), recid, msg.into()) { + Ok(address) => Some(address.0), + Err(_) => None, + } +} diff --git a/crates/precompile/src/secp256r1/crypto.rs b/crates/precompile/src/secp256r1/crypto.rs new file mode 100644 index 0000000000..f8bddf53d4 --- /dev/null +++ b/crates/precompile/src/secp256r1/crypto.rs @@ -0,0 +1,41 @@ +//! Pure cryptographic implementation for secp256r1 (P-256) signature verification. +//! +//! This module isolates the cryptographic logic from the precompile runner, +//! containing only the signature verification implementation without EVM-specific concerns. + +/// Verify a secp256r1 (P-256) ECDSA signature over a prehashed message. +/// +/// Returns `Some(())` if the signature is valid, `None` otherwise. +pub(crate) fn verify_signature(msg: &[u8; 32], sig: &[u8; 64], pk: &[u8; 64]) -> Option<()> { + cfg_if::cfg_if! { + if #[cfg(feature = "p256-aws-lc-rs")] { + use aws_lc_rs::{digest, signature::{self, UnparsedPublicKey}}; + + // Construct a Digest from the raw prehashed message bytes. + let digest = digest::Digest::import_less_safe(msg, &digest::SHA256).ok()?; + + // Build uncompressed public key: 0x04 || x || y + let mut pubkey_bytes = [0u8; 65]; + pubkey_bytes[0] = 0x04; + pubkey_bytes[1..].copy_from_slice(pk); + + let public_key = UnparsedPublicKey::new(&signature::ECDSA_P256_SHA256_FIXED, &pubkey_bytes); + + public_key.verify_digest(&digest, sig).ok() + } else { + use p256::{ + ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}, + EncodedPoint, + }; + + // Can fail only if the input is not exact length. + let signature = Signature::from_slice(sig).ok()?; + // Decode the public key bytes (x,y coordinates) using EncodedPoint + let encoded_point = EncodedPoint::from_untagged_bytes(&(*pk).into()); + // Create VerifyingKey from the encoded point + let public_key = VerifyingKey::from_encoded_point(&encoded_point).ok()?; + + public_key.verify_prehash(msg, &signature).ok() + } + } +} diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1/mod.rs similarity index 86% rename from crates/precompile/src/secp256r1.rs rename to crates/precompile/src/secp256r1/mod.rs index 58624b0434..f322ad64de 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1/mod.rs @@ -6,9 +6,13 @@ //! The main purpose of this precompile is to verify ECDSA signatures that use the secp256r1, or //! P256 elliptic curve. The [`P256VERIFY`] const represents the implementation of this precompile, //! with the address that it is currently deployed at. +pub mod crypto; + +pub(crate) use crypto::verify_signature; + use crate::{ - crypto, u64_to_address, Precompile, PrecompileError, PrecompileId, PrecompileOutput, - PrecompileResult, + crypto as crypto_provider, u64_to_address, Precompile, PrecompileError, PrecompileId, + PrecompileOutput, PrecompileResult, }; use primitives::{alloy_primitives::B512, Bytes, B256}; @@ -92,41 +96,7 @@ pub fn verify_impl(input: &[u8]) -> bool { // x, y: public key let pk = <&B512>::try_from(&input[96..160]).unwrap(); - crypto().secp256r1_verify_signature(&msg.0, &sig.0, &pk.0) -} - -pub(crate) fn verify_signature(msg: &[u8; 32], sig: &[u8; 64], pk: &[u8; 64]) -> Option<()> { - cfg_if::cfg_if! { - if #[cfg(feature = "p256-aws-lc-rs")] { - use aws_lc_rs::{digest, signature::{self, UnparsedPublicKey}}; - - // Construct a Digest from the raw prehashed message bytes. - let digest = digest::Digest::import_less_safe(msg, &digest::SHA256).ok()?; - - // Build uncompressed public key: 0x04 || x || y - let mut pubkey_bytes = [0u8; 65]; - pubkey_bytes[0] = 0x04; - pubkey_bytes[1..].copy_from_slice(pk); - - let public_key = UnparsedPublicKey::new(&signature::ECDSA_P256_SHA256_FIXED, &pubkey_bytes); - - public_key.verify_digest(&digest, sig).ok() - } else { - use p256::{ - ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}, - EncodedPoint, - }; - - // Can fail only if the input is not exact length. - let signature = Signature::from_slice(sig).ok()?; - // Decode the public key bytes (x,y coordinates) using EncodedPoint - let encoded_point = EncodedPoint::from_untagged_bytes(&(*pk).into()); - // Create VerifyingKey from the encoded point - let public_key = VerifyingKey::from_encoded_point(&encoded_point).ok()?; - - public_key.verify_prehash(msg, &signature).ok() - } - } + crypto_provider().secp256r1_verify_signature(&msg.0, &sig.0, &pk.0) } #[cfg(test)]