mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-06 09:00:48 +02:00
373 lines
13 KiB
Rust
373 lines
13 KiB
Rust
/*
|
|
* 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<const SIG_SIZE: usize, const FIELD_SIZE: usize>(
|
|
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<const FIELD_SIZE: usize>(
|
|
rs: &[u8],
|
|
der_out: &mut [u8],
|
|
) -> Result<usize, Error> {
|
|
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<Self, Error> {
|
|
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<Self, i32> {
|
|
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<Self, i32> {
|
|
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<Self, i32> {
|
|
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<Self, i32> {
|
|
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::<sys::ecc_key>() 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<Self, Error> {
|
|
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::<sys::ecc_key>() 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,
|
|
}
|