Rust wrapper: add signature implementations

This commit is contained in:
Josh Holtrop
2026-04-17 15:51:39 -04:00
parent 7f33de0882
commit 3ca90b1904
15 changed files with 1344 additions and 3 deletions
+7
View File
@@ -310,6 +310,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
[[package]]
name = "syn"
version = "2.0.106"
@@ -420,6 +426,7 @@ dependencies = [
"digest",
"rand_core 0.10.0",
"regex",
"signature",
"zeroize",
]
@@ -16,18 +16,21 @@ rand_core = ["dep:rand_core"]
aead = ["dep:aead"]
cipher = ["dep:cipher"]
digest = ["dep:digest"]
signature = ["dep:signature"]
[dependencies]
rand_core = { version = "0.10", optional = true, default-features = false }
aead = { version = "0.5", optional = true, default-features = false }
cipher = { version = "0.5", optional = true, default-features = false }
digest = { version = "0.11", optional = true, default-features = false, features = ["block-api"] }
signature = { version = "2.2", optional = true, default-features = false }
zeroize = { version = "1.3", default-features = false, features = ["derive"] }
[dev-dependencies]
aead = { version = "0.5", features = ["alloc", "dev"] }
cipher = "0.5"
digest = { version = "0.11", features = ["dev"] }
signature = "2.2"
[build-dependencies]
bindgen = "0.72.1"
+1 -1
View File
@@ -1,4 +1,4 @@
FEATURES := rand_core,aead,cipher,digest
FEATURES := rand_core,aead,cipher,digest,signature
CARGO_FEATURE_FLAGS := --features $(FEATURES)
.PHONY: all
+16
View File
@@ -433,6 +433,22 @@ fn scan_cfg() -> Result<()> {
check_cfg(&binding, "wc_RNG_DRBG_Reseed", "random_hashdrbg");
check_cfg(&binding, "wc_InitRng", "random");
// When WOLFSSL_NO_MALLOC is set without WOLFSSL_STATIC_MEMORY, the
// WC_RNG struct contains an inline `drbg_data` field and wolfCrypt sets
// `rng->drbg = &rng->drbg_data` — a self-referential pointer. Rust
// moves values by memcpy, which would silently invalidate that pointer.
// Detect this configuration and refuse to build.
if binding.contains("drbg_data") {
eprintln!(
"error: wolfSSL appears to be built with WOLFSSL_NO_MALLOC \
(without WOLFSSL_STATIC_MEMORY). This embeds a self-referential \
pointer inside WC_RNG (drbg -> drbg_data) that is incompatible \
with Rust move semantics. Please rebuild wolfSSL without \
WOLFSSL_NO_MALLOC, or enable WOLFSSL_STATIC_MEMORY."
);
std::process::exit(1);
}
/* rsa */
check_cfg(&binding, "wc_InitRsaKey", "rsa");
check_cfg(&binding, "wc_RsaDirect", "rsa_direct");
+1 -1
View File
@@ -296,7 +296,7 @@ impl Drop for ECCPoint {
/// `import_x963_ex()`, `import_private_key()`, `import_private_key_ex()`,
/// `import_raw()`, or `import_raw_ex()`.
pub struct ECC {
wc_ecc_key: sys::ecc_key,
pub(crate) wc_ecc_key: sys::ecc_key,
}
#[cfg(ecc_curve_ids)]
+372
View File
@@ -0,0 +1,372 @@
/*
* 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,
}
@@ -1443,3 +1443,123 @@ impl Drop for Ed25519 {
self.zeroize();
}
}
/// RustCrypto `signature` crate trait implementations.
///
/// Provides a fixed-size [`Signature`] and a [`VerifyingKey`] type so that
/// [`Ed25519`] can be used wherever the `signature` crate's
/// [`signature::SignerMut`], [`signature::Keypair`], and
/// [`signature::Verifier`] traits are accepted.
#[cfg(feature = "signature")]
mod signature_impl {
use super::Ed25519;
use signature::Error;
/// Ed25519 signature in its standard 64-byte encoded form.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Signature([u8; Ed25519::SIG_SIZE]);
impl Signature {
/// Construct a signature from its raw bytes.
pub const fn from_bytes(bytes: [u8; Ed25519::SIG_SIZE]) -> Self {
Self(bytes)
}
/// Return the raw signature bytes.
pub const fn to_bytes(&self) -> [u8; Ed25519::SIG_SIZE] {
self.0
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Signature {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
let arr: [u8; Ed25519::SIG_SIZE] = bytes.try_into().map_err(|_| Error::new())?;
Ok(Self(arr))
}
}
impl From<Signature> for [u8; Ed25519::SIG_SIZE] {
fn from(sig: Signature) -> Self {
sig.0
}
}
impl signature::SignatureEncoding for Signature {
type Repr = [u8; Ed25519::SIG_SIZE];
}
/// Ed25519 verifying (public) key.
///
/// Owns a copy of the 32-byte compressed public key and instantiates a
/// short-lived wolfCrypt `ed25519_key` on each verification.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VerifyingKey([u8; Ed25519::PUB_KEY_SIZE]);
impl VerifyingKey {
/// Construct a verifying key from its raw public key bytes.
pub const fn from_bytes(bytes: [u8; Ed25519::PUB_KEY_SIZE]) -> Self {
Self(bytes)
}
/// Return the raw public key bytes.
pub const fn to_bytes(&self) -> [u8; Ed25519::PUB_KEY_SIZE] {
self.0
}
}
impl AsRef<[u8]> for VerifyingKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for VerifyingKey {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
let arr: [u8; Ed25519::PUB_KEY_SIZE] =
bytes.try_into().map_err(|_| Error::new())?;
Ok(Self(arr))
}
}
#[cfg(all(ed25519_sign, ed25519_export))]
impl signature::Keypair for Ed25519 {
type VerifyingKey = VerifyingKey;
fn verifying_key(&self) -> Self::VerifyingKey {
let mut pub_key = [0u8; Ed25519::PUB_KEY_SIZE];
self.export_public(&mut pub_key).expect("ed25519 export_public failed");
VerifyingKey(pub_key)
}
}
#[cfg(ed25519_sign)]
impl signature::SignerMut<Signature> for Ed25519 {
fn try_sign(&mut self, msg: &[u8]) -> Result<Signature, Error> {
let mut sig = [0u8; Ed25519::SIG_SIZE];
self.sign_msg(msg, &mut sig).map_err(|_| Error::new())?;
Ok(Signature(sig))
}
}
#[cfg(all(ed25519_import, ed25519_verify))]
impl signature::Verifier<Signature> for VerifyingKey {
fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
let mut key = Ed25519::new().map_err(|_| Error::new())?;
key.import_public(&self.0).map_err(|_| Error::new())?;
let valid = key
.verify_msg(&signature.0, msg)
.map_err(|_| Error::new())?;
if valid { Ok(()) } else { Err(Error::new()) }
}
}
}
#[cfg(feature = "signature")]
pub use signature_impl::{Signature, VerifyingKey};
+124
View File
@@ -1368,3 +1368,127 @@ impl Drop for Ed448 {
self.zeroize();
}
}
/// RustCrypto `signature` crate trait implementations.
///
/// Provides a fixed-size [`Signature`] and a [`VerifyingKey`] type so that
/// [`Ed448`] can be used wherever the `signature` crate's
/// [`signature::SignerMut`], [`signature::Keypair`], and
/// [`signature::Verifier`] traits are accepted.
///
/// These impls use the plain Ed448 (pure) signature variant with no context;
/// the context-, hashed-, and streaming-signature variants remain accessible
/// via the inherent methods on [`Ed448`].
#[cfg(feature = "signature")]
mod signature_impl {
use super::Ed448;
use signature::Error;
/// Ed448 signature in its standard 114-byte encoded form.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Signature([u8; Ed448::SIG_SIZE]);
impl Signature {
/// Construct a signature from its raw bytes.
pub const fn from_bytes(bytes: [u8; Ed448::SIG_SIZE]) -> Self {
Self(bytes)
}
/// Return the raw signature bytes.
pub const fn to_bytes(&self) -> [u8; Ed448::SIG_SIZE] {
self.0
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for Signature {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
let arr: [u8; Ed448::SIG_SIZE] = bytes.try_into().map_err(|_| Error::new())?;
Ok(Self(arr))
}
}
impl From<Signature> for [u8; Ed448::SIG_SIZE] {
fn from(sig: Signature) -> Self {
sig.0
}
}
impl signature::SignatureEncoding for Signature {
type Repr = [u8; Ed448::SIG_SIZE];
}
/// Ed448 verifying (public) key.
///
/// Owns a copy of the 57-byte compressed public key and instantiates a
/// short-lived wolfCrypt `ed448_key` on each verification.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VerifyingKey([u8; Ed448::PUB_KEY_SIZE]);
impl VerifyingKey {
/// Construct a verifying key from its raw public key bytes.
pub const fn from_bytes(bytes: [u8; Ed448::PUB_KEY_SIZE]) -> Self {
Self(bytes)
}
/// Return the raw public key bytes.
pub const fn to_bytes(&self) -> [u8; Ed448::PUB_KEY_SIZE] {
self.0
}
}
impl AsRef<[u8]> for VerifyingKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl TryFrom<&[u8]> for VerifyingKey {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
let arr: [u8; Ed448::PUB_KEY_SIZE] =
bytes.try_into().map_err(|_| Error::new())?;
Ok(Self(arr))
}
}
#[cfg(all(ed448_sign, ed448_export))]
impl signature::Keypair for Ed448 {
type VerifyingKey = VerifyingKey;
fn verifying_key(&self) -> Self::VerifyingKey {
let mut pub_key = [0u8; Ed448::PUB_KEY_SIZE];
self.export_public(&mut pub_key).expect("ed448 export_public failed");
VerifyingKey(pub_key)
}
}
#[cfg(ed448_sign)]
impl signature::SignerMut<Signature> for Ed448 {
fn try_sign(&mut self, msg: &[u8]) -> Result<Signature, Error> {
let mut sig = [0u8; Ed448::SIG_SIZE];
self.sign_msg(msg, None, &mut sig).map_err(|_| Error::new())?;
Ok(Signature(sig))
}
}
#[cfg(all(ed448_import, ed448_verify))]
impl signature::Verifier<Signature> for VerifyingKey {
fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
let mut key = Ed448::new().map_err(|_| Error::new())?;
key.import_public(&self.0).map_err(|_| Error::new())?;
let valid = key
.verify_msg(&signature.0, msg, None)
.map_err(|_| Error::new())?;
if valid { Ok(()) } else { Err(Error::new()) }
}
}
}
#[cfg(feature = "signature")]
pub use signature_impl::{Signature, VerifyingKey};
@@ -48,6 +48,8 @@ pub mod curve25519;
pub mod dh;
pub mod dilithium;
pub mod ecc;
#[cfg(feature = "signature")]
pub mod ecdsa;
pub mod ed25519;
pub mod ed448;
pub mod fips;
@@ -59,6 +61,8 @@ pub mod mlkem;
pub mod prf;
pub mod random;
pub mod rsa;
#[cfg(feature = "signature")]
pub mod rsa_pkcs1v15;
pub mod sha;
#[cfg(feature = "digest")]
mod sha_digest;
+53 -1
View File
@@ -71,7 +71,7 @@ use core::mem::{MaybeUninit};
/// An instance can be created with `new_from_der()`, `new_public_from_der()`,
/// or `generate()`.
pub struct RSA {
wc_rsakey: sys::RsaKey,
pub(crate) wc_rsakey: sys::RsaKey,
}
impl RSA {
@@ -362,6 +362,58 @@ impl RSA {
Ok(rsa)
}
/// Create a new RSA public key from raw modulus (`n`) and public
/// exponent (`e`) bytes in big-endian form.
///
/// # Parameters
///
/// * `n`: Big-endian modulus bytes.
/// * `e`: Big-endian public exponent bytes.
///
/// # Returns
///
/// Returns either Ok(RSA) containing the RSA struct instance or Err(e)
/// containing the wolfSSL library error code value.
pub fn new_public_from_raw(n: &[u8], e: &[u8]) -> Result<Self, i32> {
Self::new_public_from_raw_ex(n, e, None, None)
}
/// Create a new RSA public key from raw modulus (`n`) and public
/// exponent (`e`) bytes with optional heap and device ID.
pub fn new_public_from_raw_ex(
n: &[u8], e: &[u8],
heap: Option<*mut core::ffi::c_void>, dev_id: Option<i32>,
) -> Result<Self, i32> {
let n_size = crate::buffer_len_to_u32(n.len())?;
let e_size = crate::buffer_len_to_u32(e.len())?;
let mut wc_rsakey: MaybeUninit<sys::RsaKey> = MaybeUninit::uninit();
let heap = match heap {
Some(heap) => heap,
None => core::ptr::null_mut(),
};
let dev_id = match dev_id {
Some(dev_id) => dev_id,
None => sys::INVALID_DEVID,
};
let rc = unsafe { sys::wc_InitRsaKey_ex(wc_rsakey.as_mut_ptr(), heap, dev_id) };
if rc != 0 {
return Err(rc);
}
let mut wc_rsakey = unsafe { wc_rsakey.assume_init() };
let rc = unsafe {
sys::wc_RsaPublicKeyDecodeRaw(
n.as_ptr(), n_size,
e.as_ptr(), e_size,
&mut wc_rsakey,
)
};
if rc != 0 {
unsafe { sys::wc_FreeRsaKey(&mut wc_rsakey); }
return Err(rc);
}
Ok(RSA { wc_rsakey })
}
/// Generate a new RSA key using the given size and exponent.
///
/// This function generates an RSA private key of length size (in bits) and
@@ -0,0 +1,346 @@
/*
* 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
*/
/*!
RSA PKCS#1 v1.5 trait impls for the RustCrypto `signature` crate.
Provides fixed-size const-generic wrapper types over [`crate::rsa::RSA`] so
RSA PKCS#1 v1.5 signing/verifying fits cleanly into `no_std` without `alloc`:
- [`SigningKey<H, N>`] / [`VerifyingKey<H, N>`] — `H` is a [`Hash`] marker
selecting the digest algorithm, `N` is the modulus size in bytes (e.g.
`256` for RSA-2048).
- [`Signature<N>`] — fixed-size `[u8; N]` wrapper implementing
[`signature::SignatureEncoding`].
Signing and verifying delegate to `wc_SignatureGenerate` and
`wc_SignatureVerify` with `WC_SIGNATURE_TYPE_RSA_W_ENC`, which hash the raw
message and apply the PKCS#1 v1.5 DigestInfo encoding internally.
*/
#![cfg(all(feature = "signature", rsa, random))]
use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::size_of;
use signature::{Error, Keypair, SignatureEncoding, SignerMut, Verifier};
use crate::random::RNG;
use crate::rsa::RSA;
use crate::sys;
mod private {
pub trait Sealed {}
}
/// Marker trait selecting the digest algorithm used by PKCS#1 v1.5 DigestInfo
/// encoding.
pub trait Hash: private::Sealed {
/// wolfCrypt hash algorithm identifier.
const HASH_TYPE: u32;
}
/// SHA-256 digest selection for PKCS#1 v1.5.
#[cfg(sha256)]
pub enum Sha256 {}
#[cfg(sha256)]
impl private::Sealed for Sha256 {}
#[cfg(sha256)]
impl Hash for Sha256 {
const HASH_TYPE: u32 = sys::wc_HashType_WC_HASH_TYPE_SHA256;
}
/// SHA-384 digest selection for PKCS#1 v1.5.
#[cfg(sha384)]
pub enum Sha384 {}
#[cfg(sha384)]
impl private::Sealed for Sha384 {}
#[cfg(sha384)]
impl Hash for Sha384 {
const HASH_TYPE: u32 = sys::wc_HashType_WC_HASH_TYPE_SHA384;
}
/// SHA-512 digest selection for PKCS#1 v1.5.
#[cfg(sha512)]
pub enum Sha512 {}
#[cfg(sha512)]
impl private::Sealed for Sha512 {}
#[cfg(sha512)]
impl Hash for Sha512 {
const HASH_TYPE: u32 = sys::wc_HashType_WC_HASH_TYPE_SHA512;
}
/// Fixed-size RSA PKCS#1 v1.5 signature. `N` is the modulus size in bytes.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Signature<const N: usize>([u8; N]);
impl<const N: usize> Signature<N> {
/// Construct a signature from its raw bytes.
pub const fn from_bytes(bytes: [u8; N]) -> Self {
Self(bytes)
}
/// Return the raw signature bytes.
pub const fn to_bytes(&self) -> [u8; N] {
self.0
}
}
impl<const N: usize> AsRef<[u8]> for Signature<N> {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<const N: usize> TryFrom<&[u8]> for Signature<N> {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
let arr: [u8; N] = bytes.try_into().map_err(|_| Error::new())?;
Ok(Self(arr))
}
}
impl<const N: usize> From<Signature<N>> for [u8; N] {
fn from(sig: Signature<N>) -> Self {
sig.0
}
}
impl<const N: usize> SignatureEncoding for Signature<N> {
type Repr = [u8; N];
}
fn check_modulus_size(rsa: &RSA, expected: usize) -> Result<(), i32> {
let actual = rsa.get_encrypt_size()?;
if actual != expected {
return Err(sys::wolfCrypt_ErrorCodes_BAD_FUNC_ARG);
}
Ok(())
}
/// RSA PKCS#1 v1.5 signing key.
///
/// `H` selects the hash used in DigestInfo encoding; `N` is the expected
/// modulus size in bytes (e.g. `256` for RSA-2048, `384` for RSA-3072).
pub struct SigningKey<H: Hash, const N: usize> {
inner: RSA,
rng: RNG,
_hash: PhantomData<H>,
}
impl<H: Hash, const N: usize> SigningKey<H, N> {
/// Generate a fresh `N * 8`-bit RSA key with public exponent 65537.
#[cfg(rsa_keygen)]
pub fn generate(mut rng: RNG) -> Result<Self, i32> {
let bits: i32 = (N * 8).try_into().map_err(|_| sys::wolfCrypt_ErrorCodes_BAD_FUNC_ARG)?;
let rsa = RSA::generate(bits, 65537, &mut rng)?;
Ok(Self { inner: rsa, rng, _hash: PhantomData })
}
/// Adopt an existing [`RSA`] key, verifying that its modulus size in
/// bytes matches `N`.
pub fn from_rsa(rsa: RSA, rng: RNG) -> Result<Self, i32> {
check_modulus_size(&rsa, N)?;
Ok(Self { inner: rsa, rng, _hash: PhantomData })
}
/// Borrow the inner [`RSA`] key.
pub fn as_rsa(&self) -> &RSA {
&self.inner
}
/// Consume the signing key and return its `RSA` and `RNG` parts.
pub fn into_parts(self) -> (RSA, RNG) {
(self.inner, self.rng)
}
}
impl<H: Hash, const N: usize> SignerMut<Signature<N>> for SigningKey<H, N> {
fn try_sign(&mut self, msg: &[u8]) -> Result<Signature<N>, Error> {
let mut sig = [0u8; N];
let mut sig_len: u32 = N as u32;
let msg_len: u32 = msg.len().try_into().map_err(|_| Error::new())?;
let rc = unsafe {
sys::wc_SignatureGenerate(
H::HASH_TYPE,
sys::wc_SignatureType_WC_SIGNATURE_TYPE_RSA_W_ENC,
msg.as_ptr(), msg_len,
sig.as_mut_ptr(), &mut sig_len,
&mut self.inner.wc_rsakey as *mut _ as *mut c_void,
size_of::<sys::RsaKey>() as u32,
&mut self.rng.wc_rng,
)
};
if rc != 0 || sig_len as usize != N {
return Err(Error::new());
}
Ok(Signature(sig))
}
}
const MAX_E_LEN: usize = 8;
/// RSA PKCS#1 v1.5 verifying key.
///
/// Owns a copy of the public key as raw `(n, e)` bytes and instantiates a
/// short-lived [`RSA`] on each verification. `H` selects the hash algorithm
/// used in DigestInfo encoding; `N` is the modulus size in bytes.
pub struct VerifyingKey<H: Hash, const N: usize> {
n: [u8; N],
e: [u8; MAX_E_LEN],
e_len: u8,
_hash: PhantomData<H>,
}
// Manual impls avoid requiring `H: Clone`/`Copy`/etc. — `H` is a marker
// (uninhabited enum) that only appears inside `PhantomData`.
impl<H: Hash, const N: usize> Clone for VerifyingKey<H, N> {
fn clone(&self) -> Self { *self }
}
impl<H: Hash, const N: usize> Copy for VerifyingKey<H, N> {}
impl<H: Hash, const N: usize> core::fmt::Debug for VerifyingKey<H, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("VerifyingKey")
.field("n", &&self.n[..])
.field("e", &self.exponent())
.finish()
}
}
impl<H: Hash, const N: usize> PartialEq for VerifyingKey<H, N> {
fn eq(&self, other: &Self) -> bool {
self.n == other.n && self.exponent() == other.exponent()
}
}
impl<H: Hash, const N: usize> Eq for VerifyingKey<H, N> {}
impl<H: Hash, const N: usize> VerifyingKey<H, N> {
/// Construct a verifying key from raw big-endian modulus (`n`) and
/// public exponent (`e`) bytes.
pub fn from_components(n: &[u8], e: &[u8]) -> Result<Self, Error> {
if n.len() != N || e.is_empty() || e.len() > MAX_E_LEN {
return Err(Error::new());
}
let mut n_arr = [0u8; N];
n_arr.copy_from_slice(n);
let mut e_arr = [0u8; MAX_E_LEN];
e_arr[..e.len()].copy_from_slice(e);
Ok(Self {
n: n_arr,
e: e_arr,
e_len: e.len() as u8,
_hash: PhantomData,
})
}
/// Adopt an existing [`RSA`] public key, verifying its modulus size in
/// bytes matches `N`.
pub fn from_rsa(rsa: RSA) -> Result<Self, i32> {
check_modulus_size(&rsa, N)?;
let mut n = [0u8; N];
let mut e = [0u8; MAX_E_LEN];
let mut n_len: u32 = n.len() as u32;
let mut e_len: u32 = e.len() as u32;
let rc = unsafe {
sys::wc_RsaFlattenPublicKey(
&rsa.wc_rsakey,
e.as_mut_ptr(), &mut e_len,
n.as_mut_ptr(), &mut n_len,
)
};
if rc != 0 {
return Err(rc);
}
if (n_len as usize) != N || e_len == 0 || (e_len as usize) > MAX_E_LEN {
return Err(sys::wolfCrypt_ErrorCodes_BAD_FUNC_ARG);
}
Ok(Self {
n,
e,
e_len: e_len as u8,
_hash: PhantomData,
})
}
/// Construct a verifying key from a DER-encoded `SubjectPublicKeyInfo`
/// / PKCS#1 public key.
pub fn from_public_der(der: &[u8]) -> Result<Self, i32> {
let rsa = RSA::new_public_from_der(der)?;
Self::from_rsa(rsa)
}
/// Return the raw modulus bytes.
pub const fn modulus(&self) -> &[u8; N] {
&self.n
}
/// Return the raw public exponent bytes.
pub fn exponent(&self) -> &[u8] {
&self.e[..self.e_len as usize]
}
}
impl<H: Hash, const N: usize> Verifier<Signature<N>> for VerifyingKey<H, N> {
fn verify(&self, msg: &[u8], signature: &Signature<N>) -> Result<(), Error> {
let msg_len: u32 = msg.len().try_into().map_err(|_| Error::new())?;
let mut rsa = RSA::new_public_from_raw(&self.n, self.exponent())
.map_err(|_| Error::new())?;
let rc = unsafe {
sys::wc_SignatureVerify(
H::HASH_TYPE,
sys::wc_SignatureType_WC_SIGNATURE_TYPE_RSA_W_ENC,
msg.as_ptr(), msg_len,
signature.0.as_ptr(), N as u32,
&mut rsa.wc_rsakey as *mut _ as *mut c_void,
size_of::<sys::RsaKey>() as u32,
)
};
if rc != 0 {
return Err(Error::new());
}
Ok(())
}
}
impl<H: Hash, const N: usize> Keypair for SigningKey<H, N> {
type VerifyingKey = VerifyingKey<H, N>;
fn verifying_key(&self) -> VerifyingKey<H, N> {
let mut n = [0u8; N];
let mut e = [0u8; MAX_E_LEN];
let mut n_len: u32 = n.len() as u32;
let mut e_len: u32 = e.len() as u32;
let rc = unsafe {
sys::wc_RsaFlattenPublicKey(
&self.inner.wc_rsakey,
e.as_mut_ptr(), &mut e_len,
n.as_mut_ptr(), &mut n_len,
)
};
if rc != 0 {
panic!("wc_RsaFlattenPublicKey failed: {rc}");
}
VerifyingKey {
n,
e,
e_len: e_len as u8,
_hash: PhantomData,
}
}
}
@@ -0,0 +1,142 @@
#![cfg(all(feature = "signature", ecc, ecc_sign, ecc_verify, ecc_curve_ids, random))]
mod common;
use signature::{Keypair, SignerMut, Verifier};
use wolfssl_wolfcrypt::random::RNG;
#[test]
#[cfg(sha256)]
fn test_p256_sign_verify() {
use wolfssl_wolfcrypt::ecdsa::{P256Signature, P256SigningKey, P256VerifyingKey};
common::setup();
let rng = RNG::new().expect("RNG");
let mut sk = P256SigningKey::generate(rng).expect("generate P256");
let msg = b"ecdsa p256 signature trait test";
let sig: P256Signature = sk.sign(msg);
// Encoding round-trip.
let bytes = sig.to_bytes();
assert_eq!(bytes.len(), 64);
let sig2 = P256Signature::try_from(bytes.as_ref()).expect("parse sig");
assert_eq!(sig, sig2);
// Wrong length must fail.
assert!(P256Signature::try_from(&bytes[..63]).is_err());
// Keypair provides a matching verifying key.
let vk: P256VerifyingKey = sk.verifying_key();
vk.verify(msg, &sig).expect("verify");
// Tampered message fails.
let mut tampered = *msg;
tampered[0] ^= 0x01;
assert!(vk.verify(&tampered, &sig).is_err());
// VerifyingKey bytes round-trip.
let vk_bytes = vk.to_bytes();
assert_eq!(vk_bytes.len(), 65);
assert_eq!(vk_bytes[0], 0x04); // uncompressed X9.63 tag
let vk2 = P256VerifyingKey::try_from(vk_bytes.as_ref()).expect("parse vk");
assert_eq!(vk, vk2);
vk2.verify(msg, &sig).expect("verify via rebuilt vk");
}
#[test]
#[cfg(sha256)]
fn test_p256_import_unsigned_and_x963() {
use wolfssl_wolfcrypt::ecc::ECC;
use wolfssl_wolfcrypt::ecdsa::{P256SigningKey, P256VerifyingKey};
common::setup();
// Start from a freshly generated key so we have known-good (qx, qy, d).
let mut rng = RNG::new().expect("RNG");
let mut src = ECC::generate_ex(32, &mut rng, ECC::SECP256R1, None, None)
.expect("generate ECC");
let mut qx = [0u8; 32];
let mut qy = [0u8; 32];
let mut d_buf = [0u8; 32];
let mut qx_len = 0u32;
let mut qy_len = 0u32;
let mut d_len = 0u32;
src.export_ex(&mut qx, &mut qx_len, &mut qy, &mut qy_len, &mut d_buf, &mut d_len, false)
.expect("export_ex");
assert_eq!(qx_len as usize, 32);
assert_eq!(qy_len as usize, 32);
assert_eq!(d_len as usize, 32);
let mut x963 = [0u8; 65];
let x963_written = src.export_x963(&mut x963).expect("export_x963");
assert_eq!(x963_written, 65);
let msg = b"ecdsa p256 import path";
// Path 1: raw unsigned components.
let rng = RNG::new().expect("RNG");
let mut sk_a = P256SigningKey::import_unsigned(&qx, &qy, &d_buf, rng)
.expect("import_unsigned");
let sig_a = sk_a.sign(msg);
sk_a.verifying_key().verify(msg, &sig_a).expect("verify a");
// Path 2: X9.63 public + private scalar.
let rng = RNG::new().expect("RNG");
let mut sk_b = P256SigningKey::import_x963(&x963, &d_buf, rng)
.expect("import_x963");
let sig_b = sk_b.sign(msg);
sk_b.verifying_key().verify(msg, &sig_b).expect("verify b");
// Both imported keys produce the same public key bytes.
let vk_a: P256VerifyingKey = sk_a.verifying_key();
let vk_b: P256VerifyingKey = sk_b.verifying_key();
assert_eq!(vk_a, vk_b);
// Cross-verify: vk_a verifies a signature produced by sk_b.
vk_a.verify(msg, &sig_b).expect("cross-verify a/b");
}
#[test]
#[cfg(sha384)]
fn test_p384_sign_verify() {
use wolfssl_wolfcrypt::ecdsa::{P384Signature, P384SigningKey, P384VerifyingKey};
common::setup();
let rng = RNG::new().expect("RNG");
let mut sk = P384SigningKey::generate(rng).expect("generate P384");
let msg = b"ecdsa p384 signature trait test";
let sig: P384Signature = sk.sign(msg);
assert_eq!(sig.to_bytes().len(), 96);
let vk: P384VerifyingKey = sk.verifying_key();
vk.verify(msg, &sig).expect("verify p384");
let mut tampered = *msg;
tampered[5] ^= 0x80;
assert!(vk.verify(&tampered, &sig).is_err());
}
#[test]
#[cfg(sha512)]
fn test_p521_sign_verify() {
use wolfssl_wolfcrypt::ecdsa::{P521Signature, P521SigningKey, P521VerifyingKey};
common::setup();
let rng = RNG::new().expect("RNG");
let mut sk = P521SigningKey::generate(rng).expect("generate P521");
let msg = b"ecdsa p521 signature trait test";
let sig: P521Signature = sk.sign(msg);
assert_eq!(sig.to_bytes().len(), 132);
let vk: P521VerifyingKey = sk.verifying_key();
vk.verify(msg, &sig).expect("verify p521");
let mut tampered = *msg;
tampered[10] ^= 0x55;
assert!(vk.verify(&tampered, &sig).is_err());
}
@@ -243,6 +243,43 @@ fn test_import_export() {
ed.import_public_ex(&public, false).expect("Error with import_public_ex()");
}
#[test]
#[cfg(all(feature = "signature", ed25519_import, ed25519_export, ed25519_sign, ed25519_verify))]
fn test_signature_traits() {
use signature::{Keypair, SignerMut, Verifier};
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
let mut ed = Ed25519::generate(&mut rng).expect("Error with generate()");
let message = b"message to sign via RustCrypto signature trait";
let sig: Signature = ed.sign(message);
// Round-trip the signature bytes through the SignatureEncoding machinery.
let bytes = sig.to_bytes();
assert_eq!(bytes.len(), Ed25519::SIG_SIZE);
let sig_round_trip = Signature::try_from(bytes.as_ref()).expect("Signature::try_from bytes");
assert_eq!(sig, sig_round_trip);
// Reject signatures of the wrong length.
assert!(Signature::try_from(&bytes[..bytes.len() - 1]).is_err());
// VerifyingKey obtained via the Keypair trait verifies this signature.
let vk: VerifyingKey = ed.verifying_key();
vk.verify(message, &sig).expect("Verifier::verify failed");
// A tampered message must fail verification.
let mut tampered = *message;
tampered[0] ^= 0x01;
assert!(vk.verify(&tampered, &sig).is_err());
// VerifyingKey bytes round-trip.
let vk_bytes = vk.to_bytes();
let vk2 = VerifyingKey::try_from(vk_bytes.as_ref()).expect("VerifyingKey::try_from bytes");
assert_eq!(vk, vk2);
}
#[test]
fn test_sizes() {
let mut rng = RNG::new().expect("Error creating RNG");
@@ -247,6 +247,43 @@ fn test_import_export() {
ed.import_public_ex(&public, false).expect("Error with import_public_ex()");
}
#[test]
#[cfg(all(feature = "signature", ed448_import, ed448_export, ed448_sign, ed448_verify))]
fn test_signature_traits() {
use signature::{Keypair, SignerMut, Verifier};
common::setup();
let mut rng = RNG::new().expect("Error creating RNG");
let mut ed = Ed448::generate(&mut rng).expect("Error with generate()");
let message = b"message to sign via RustCrypto signature trait";
let sig: Signature = ed.sign(message);
// Round-trip the signature bytes through the SignatureEncoding machinery.
let bytes = sig.to_bytes();
assert_eq!(bytes.len(), Ed448::SIG_SIZE);
let sig_round_trip = Signature::try_from(bytes.as_ref()).expect("Signature::try_from bytes");
assert_eq!(sig, sig_round_trip);
// Reject signatures of the wrong length.
assert!(Signature::try_from(&bytes[..bytes.len() - 1]).is_err());
// VerifyingKey obtained via the Keypair trait verifies this signature.
let vk: VerifyingKey = ed.verifying_key();
vk.verify(message, &sig).expect("Verifier::verify failed");
// A tampered message must fail verification.
let mut tampered = *message;
tampered[0] ^= 0x01;
assert!(vk.verify(&tampered, &sig).is_err());
// VerifyingKey bytes round-trip.
let vk_bytes = vk.to_bytes();
let vk2 = VerifyingKey::try_from(vk_bytes.as_ref()).expect("VerifyingKey::try_from bytes");
assert_eq!(vk, vk2);
}
#[test]
fn test_sizes() {
let mut rng = RNG::new().expect("Error creating RNG");
@@ -0,0 +1,81 @@
#![cfg(all(feature = "signature", rsa, random))]
mod common;
use signature::{Keypair, SignerMut, Verifier};
use wolfssl_wolfcrypt::random::RNG;
#[test]
#[cfg(all(sha256, rsa_keygen))]
fn test_rsa2048_sha256_sign_verify() {
use wolfssl_wolfcrypt::rsa_pkcs1v15::{Sha256, Signature, SigningKey, VerifyingKey};
common::setup();
let rng = RNG::new().expect("RNG");
let mut sk: SigningKey<Sha256, 256> = SigningKey::generate(rng).expect("generate 2048");
let msg = b"rsa pkcs1v15 sha256 signature trait test";
let sig: Signature<256> = sk.sign(msg);
// Encoding round-trip.
let bytes = sig.to_bytes();
assert_eq!(bytes.len(), 256);
let sig2 = Signature::<256>::try_from(bytes.as_ref()).expect("parse sig");
assert_eq!(sig, sig2);
// Wrong length must fail.
assert!(Signature::<256>::try_from(&bytes[..255]).is_err());
// Keypair gives a matching verifying key.
let vk: VerifyingKey<Sha256, 256> = sk.verifying_key();
vk.verify(msg, &sig).expect("verify");
// Tampered message fails.
let mut tampered = *msg;
tampered[0] ^= 0x01;
assert!(vk.verify(&tampered, &sig).is_err());
// VerifyingKey rebuilt from raw components still verifies.
let vk_copy = VerifyingKey::<Sha256, 256>::from_components(vk.modulus(), vk.exponent())
.expect("from_components");
assert_eq!(vk, vk_copy);
vk_copy.verify(msg, &sig).expect("verify via rebuilt vk");
}
#[test]
#[cfg(all(sha384, rsa_keygen))]
fn test_rsa3072_sha384_sign_verify() {
use wolfssl_wolfcrypt::rsa_pkcs1v15::{Sha384, Signature, SigningKey, VerifyingKey};
common::setup();
let rng = RNG::new().expect("RNG");
let mut sk: SigningKey<Sha384, 384> = SigningKey::generate(rng).expect("generate 3072");
let msg = b"rsa pkcs1v15 sha384 signature trait test";
let sig: Signature<384> = sk.sign(msg);
assert_eq!(sig.to_bytes().len(), 384);
let vk: VerifyingKey<Sha384, 384> = sk.verifying_key();
vk.verify(msg, &sig).expect("verify");
let mut tampered = *msg;
tampered[2] ^= 0x10;
assert!(vk.verify(&tampered, &sig).is_err());
}
#[test]
#[cfg(all(sha256, rsa_keygen))]
fn test_modulus_size_mismatch_rejected() {
use wolfssl_wolfcrypt::rsa::RSA;
use wolfssl_wolfcrypt::rsa_pkcs1v15::{Sha256, SigningKey};
common::setup();
let mut rng = RNG::new().expect("RNG");
let rsa2048 = RSA::generate(2048, 65537, &mut rng).expect("generate");
// Attempt to adopt a 2048-bit key as if it were 3072.
let result: Result<SigningKey<Sha256, 384>, _> = SigningKey::from_rsa(rsa2048, rng);
assert!(result.is_err(), "modulus size mismatch must be rejected");
}