/* * Copyright (C) 2006-2026 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ /*! ECDSA trait impls for the RustCrypto `signature` crate. Provides per-curve wrapper types (`P256SigningKey`, `P256VerifyingKey`, `P256Signature`, etc.) over the inherent [`crate::ecc::ECC`] wrapper. Each curve pairs with its canonical hash algorithm (P-256 with SHA-256, P-384 with SHA-384, P-521 with SHA-512) and produces fixed-size `r‖s` signatures matching the conventions used by the RustCrypto `ecdsa` crate. Signing and verifying use the high-level `wc_SignatureGenerate` / `wc_SignatureVerify` wolfCrypt entry points, which hash the raw message internally and emit/consume DER-encoded ECDSA signatures; the wrapper converts between DER and fixed `r‖s` via `wc_ecc_sig_to_rs` and `wc_ecc_rs_raw_to_sig`. */ #![cfg(all(feature = "signature", ecc, ecc_sign, ecc_verify, ecc_import, ecc_export, ecc_curve_ids, random))] use core::ffi::c_void; use core::mem::size_of; use signature::{Error, Keypair, SignatureEncoding, SignerMut, Verifier}; use crate::ecc::ECC; use crate::random::RNG; use crate::sys; /// Build a fixed `r‖s` signature buffer from DER bytes produced by wolfCrypt. fn der_to_rs( der: &[u8], ) -> Result<[u8; SIG_SIZE], Error> { debug_assert_eq!(SIG_SIZE, 2 * FIELD_SIZE); let mut r_buf = [0u8; FIELD_SIZE]; let mut s_buf = [0u8; FIELD_SIZE]; let mut r_len = FIELD_SIZE as u32; let mut s_len = FIELD_SIZE as u32; let rc = unsafe { sys::wc_ecc_sig_to_rs( der.as_ptr(), der.len() as u32, r_buf.as_mut_ptr(), &mut r_len, s_buf.as_mut_ptr(), &mut s_len, ) }; if rc != 0 { return Err(Error::new()); } let r_len = r_len as usize; let s_len = s_len as usize; if r_len > FIELD_SIZE || s_len > FIELD_SIZE { return Err(Error::new()); } let mut out = [0u8; SIG_SIZE]; out[FIELD_SIZE - r_len..FIELD_SIZE].copy_from_slice(&r_buf[..r_len]); out[SIG_SIZE - s_len..SIG_SIZE].copy_from_slice(&s_buf[..s_len]); Ok(out) } /// Build a DER signature from fixed `r‖s` bytes. fn rs_to_der( rs: &[u8], der_out: &mut [u8], ) -> Result { if rs.len() != 2 * FIELD_SIZE { return Err(Error::new()); } let (r, s) = rs.split_at(FIELD_SIZE); let mut der_len = der_out.len() as u32; let rc = unsafe { sys::wc_ecc_rs_raw_to_sig( r.as_ptr(), FIELD_SIZE as u32, s.as_ptr(), FIELD_SIZE as u32, der_out.as_mut_ptr(), &mut der_len, ) }; if rc != 0 { return Err(Error::new()); } Ok(der_len as usize) } macro_rules! define_ecdsa_curve { ( $(#[$meta:meta])* ($signing_key:ident, $verifying_key:ident, $signature:ident), field_size = $field_size:literal, sig_size = $sig_size:literal, x963_size = $x963_size:literal, der_max = $der_max:literal, curve_id = $curve_id:expr, hash_type = $hash_type:expr, hash_cfg = $hash_cfg:meta $(,)? ) => { /// Fixed-size ECDSA signature in `r‖s` form. $(#[$meta])* #[cfg($hash_cfg)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct $signature([u8; $sig_size]); #[cfg($hash_cfg)] impl $signature { /// Size in bytes of the fixed `r‖s` encoding. pub const BYTE_SIZE: usize = $sig_size; /// Construct a signature from raw `r‖s` bytes. pub const fn from_bytes(bytes: [u8; $sig_size]) -> Self { Self(bytes) } /// Return the raw `r‖s` bytes. pub const fn to_bytes(&self) -> [u8; $sig_size] { self.0 } } #[cfg($hash_cfg)] impl AsRef<[u8]> for $signature { fn as_ref(&self) -> &[u8] { &self.0 } } #[cfg($hash_cfg)] impl TryFrom<&[u8]> for $signature { type Error = Error; fn try_from(bytes: &[u8]) -> Result { let arr: [u8; $sig_size] = bytes.try_into().map_err(|_| Error::new())?; Ok(Self(arr)) } } #[cfg($hash_cfg)] impl From<$signature> for [u8; $sig_size] { fn from(sig: $signature) -> Self { sig.0 } } #[cfg($hash_cfg)] impl SignatureEncoding for $signature { type Repr = [u8; $sig_size]; } /// ECDSA signing key (private key + owned RNG + cached public key). $(#[$meta])* #[cfg($hash_cfg)] pub struct $signing_key { inner: ECC, rng: RNG, pub_bytes: [u8; $x963_size], } #[cfg($hash_cfg)] impl $signing_key { /// Byte length of the uncompressed X9.63 public key encoding. pub const PUB_KEY_SIZE: usize = $x963_size; /// Private-scalar byte length (`d`, curve field size). pub const SCALAR_SIZE: usize = $field_size; /// Generate a fresh signing key using the provided RNG. pub fn generate(mut rng: RNG) -> Result { let ecc = ECC::generate_ex( $field_size as i32, &mut rng, $curve_id, None, None, )?; Self::from_ecc(ecc, rng) } /// Import a signing key from unsigned big-endian public /// coordinates `qx`, `qy` and private scalar `d`, each of exactly /// the curve's field size in bytes. pub fn import_unsigned( qx: &[u8; $field_size], qy: &[u8; $field_size], d: &[u8; $field_size], rng: RNG, ) -> Result { let ecc = ECC::import_unsigned(qx, qy, d, $curve_id, None, None)?; Self::from_ecc(ecc, rng) } /// Import a signing key from an uncompressed X9.63 public key /// (leading `0x04` byte + `x‖y`) and a matching unsigned /// big-endian private scalar `d`. pub fn import_x963( public_x963: &[u8; $x963_size], d: &[u8; $field_size], rng: RNG, ) -> Result { let ecc = ECC::import_private_key_ex( d, public_x963, $curve_id, None, None, )?; Self::from_ecc(ecc, rng) } /// Borrow the inner [`ECC`] key for operations not covered by the /// signature traits. pub fn as_ecc(&self) -> &ECC { &self.inner } /// Consume the signing key and return its `ECC` and `RNG` parts. pub fn into_parts(self) -> (ECC, RNG) { (self.inner, self.rng) } /// Helper that caches the X9.63 public key bytes from an already /// populated [`ECC`] and pairs it with the given `rng`. fn from_ecc(mut ecc: ECC, rng: RNG) -> Result { let mut pub_bytes = [0u8; $x963_size]; let written = ecc.export_x963(&mut pub_bytes)?; if written != $x963_size { return Err(sys::wolfCrypt_ErrorCodes_BAD_FUNC_ARG); } Ok(Self { inner: ecc, rng, pub_bytes }) } } #[cfg($hash_cfg)] impl Keypair for $signing_key { type VerifyingKey = $verifying_key; fn verifying_key(&self) -> $verifying_key { $verifying_key { pub_bytes: self.pub_bytes } } } #[cfg($hash_cfg)] impl SignerMut<$signature> for $signing_key { fn try_sign(&mut self, msg: &[u8]) -> Result<$signature, Error> { let mut der = [0u8; $der_max]; let mut der_len: u32 = der.len() as u32; let msg_len: u32 = msg.len().try_into().map_err(|_| Error::new())?; let rc = unsafe { sys::wc_SignatureGenerate( $hash_type, sys::wc_SignatureType_WC_SIGNATURE_TYPE_ECC, msg.as_ptr(), msg_len, der.as_mut_ptr(), &mut der_len, &mut self.inner.wc_ecc_key as *mut _ as *mut c_void, size_of::() as u32, &mut self.rng.wc_rng, ) }; if rc != 0 { return Err(Error::new()); } let rs = der_to_rs::<$sig_size, $field_size>(&der[..der_len as usize])?; Ok($signature(rs)) } } /// ECDSA verifying key. Owns the uncompressed X9.63 public key bytes /// and instantiates a short-lived [`ECC`] on each verification. $(#[$meta])* #[cfg($hash_cfg)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct $verifying_key { pub_bytes: [u8; $x963_size], } #[cfg($hash_cfg)] impl $verifying_key { /// Byte length of the uncompressed X9.63 public key encoding. pub const BYTE_SIZE: usize = $x963_size; /// Construct a verifying key from its uncompressed X9.63 bytes. /// /// The buffer must start with `0x04` followed by `x‖y` (each /// `FIELD_SIZE` bytes). pub const fn from_bytes(bytes: [u8; $x963_size]) -> Self { Self { pub_bytes: bytes } } /// Return the uncompressed X9.63 public key bytes. pub const fn to_bytes(&self) -> [u8; $x963_size] { self.pub_bytes } } #[cfg($hash_cfg)] impl AsRef<[u8]> for $verifying_key { fn as_ref(&self) -> &[u8] { &self.pub_bytes } } #[cfg($hash_cfg)] impl TryFrom<&[u8]> for $verifying_key { type Error = Error; fn try_from(bytes: &[u8]) -> Result { let arr: [u8; $x963_size] = bytes.try_into().map_err(|_| Error::new())?; Ok(Self { pub_bytes: arr }) } } #[cfg($hash_cfg)] impl Verifier<$signature> for $verifying_key { fn verify(&self, msg: &[u8], sig: &$signature) -> Result<(), Error> { let mut der = [0u8; $der_max]; let der_len = rs_to_der::<$field_size>(&sig.0, &mut der)?; let mut key = ECC::import_x963_ex(&self.pub_bytes, $curve_id, None, None) .map_err(|_| Error::new())?; let msg_len: u32 = msg.len().try_into().map_err(|_| Error::new())?; let rc = unsafe { sys::wc_SignatureVerify( $hash_type, sys::wc_SignatureType_WC_SIGNATURE_TYPE_ECC, msg.as_ptr(), msg_len, der.as_ptr(), der_len as u32, &mut key.wc_ecc_key as *mut _ as *mut c_void, size_of::() as u32, ) }; if rc != 0 { return Err(Error::new()); } Ok(()) } } }; } define_ecdsa_curve! { /// NIST P-256 (secp256r1) paired with SHA-256. (P256SigningKey, P256VerifyingKey, P256Signature), field_size = 32, sig_size = 64, x963_size = 65, der_max = 72, curve_id = sys::ecc_curve_ids_ECC_SECP256R1, hash_type = sys::wc_HashType_WC_HASH_TYPE_SHA256, hash_cfg = sha256, } define_ecdsa_curve! { /// NIST P-384 (secp384r1) paired with SHA-384. (P384SigningKey, P384VerifyingKey, P384Signature), field_size = 48, sig_size = 96, x963_size = 97, der_max = 104, curve_id = sys::ecc_curve_ids_ECC_SECP384R1, hash_type = sys::wc_HashType_WC_HASH_TYPE_SHA384, hash_cfg = sha384, } define_ecdsa_curve! { /// NIST P-521 (secp521r1) paired with SHA-512. (P521SigningKey, P521VerifyingKey, P521Signature), field_size = 66, sig_size = 132, x963_size = 133, der_max = 141, curve_id = sys::ecc_curve_ids_ECC_SECP521R1, hash_type = sys::wc_HashType_WC_HASH_TYPE_SHA512, hash_cfg = sha512, }