Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions wrapper/rust/wolfssl-wolfcrypt/src/rsa_pkcs1v15.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,23 @@ RSA PKCS#1 v1.5 signing/verifying fits cleanly into `no_std` without `alloc`:
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.

[`SigningKey<H, N>`] requires the `random` cfg because signing needs an RNG.
[`VerifyingKey<H, N>`] and [`Signature<N>`] are available without it, so
verify-only builds do not have to pull in a random number generator.
*/

#![cfg(all(feature = "signature", rsa, random))]
#![cfg(all(feature = "signature", rsa))]

use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::size_of;

use signature::{Error, Keypair, SignatureEncoding, SignerMut, Verifier};
use signature::{Error, SignatureEncoding, Verifier};
#[cfg(random)]
use signature::{Keypair, SignerMut};

#[cfg(random)]
use crate::random::RNG;
use crate::rsa::RSA;
use crate::sys;
Expand Down Expand Up @@ -140,12 +147,17 @@ fn check_modulus_size(rsa: &RSA, expected: usize) -> Result<(), i32> {
///
/// `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).
///
/// Requires the `random` cfg: PKCS#1 v1.5 signing needs an RNG for
/// blinding.
#[cfg(random)]
pub struct SigningKey<H: Hash, const N: usize> {
inner: RSA,
rng: RNG,
_hash: PhantomData<H>,
}

#[cfg(random)]
impl<H: Hash, const N: usize> SigningKey<H, N> {
/// Generate a fresh `N * 8`-bit RSA key with public exponent 65537.
#[cfg(rsa_keygen)]
Expand Down Expand Up @@ -173,6 +185,7 @@ impl<H: Hash, const N: usize> SigningKey<H, N> {
}
}

#[cfg(random)]
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];
Expand Down Expand Up @@ -327,6 +340,7 @@ impl<H: Hash, const N: usize> Verifier<Signature<N>> for VerifyingKey<H, N> {
}
}

#[cfg(random)]
impl<H: Hash, const N: usize> Keypair for SigningKey<H, N> {
type VerifyingKey = VerifyingKey<H, N>;
fn verifying_key(&self) -> VerifyingKey<H, N> {
Expand Down
37 changes: 32 additions & 5 deletions wrapper/rust/wolfssl-wolfcrypt/tests/test_rsa_pkcs1v15.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#![cfg(all(feature = "signature", rsa, random))]
#![cfg(all(feature = "signature", rsa))]

mod common;

use signature::{Keypair, SignerMut, Verifier};
use signature::Verifier;
#[cfg(random)]
use signature::{Keypair, SignerMut};
#[cfg(random)]
use wolfssl_wolfcrypt::random::RNG;

#[test]
#[cfg(all(sha256, rsa_keygen))]
#[cfg(all(sha256, rsa_keygen, random))]
fn test_rsa2048_sha256_sign_verify() {
use wolfssl_wolfcrypt::rsa_pkcs1v15::{Sha256, Signature, SigningKey, VerifyingKey};

Expand Down Expand Up @@ -44,7 +47,7 @@ fn test_rsa2048_sha256_sign_verify() {
}

#[test]
#[cfg(all(sha384, rsa_keygen))]
#[cfg(all(sha384, rsa_keygen, random))]
fn test_rsa3072_sha384_sign_verify() {
use wolfssl_wolfcrypt::rsa_pkcs1v15::{Sha384, Signature, SigningKey, VerifyingKey};

Expand All @@ -66,7 +69,7 @@ fn test_rsa3072_sha384_sign_verify() {
}

#[test]
#[cfg(all(sha256, rsa_keygen))]
#[cfg(all(sha256, rsa_keygen, random))]
fn test_modulus_size_mismatch_rejected() {
use wolfssl_wolfcrypt::rsa::RSA;
use wolfssl_wolfcrypt::rsa_pkcs1v15::{Sha256, SigningKey};
Expand All @@ -79,3 +82,27 @@ fn test_modulus_size_mismatch_rejected() {
let result: Result<SigningKey<Sha256, 384>, _> = SigningKey::from_rsa(rsa2048, rng);
assert!(result.is_err(), "modulus size mismatch must be rejected");
}

/// VerifyingKey must be usable in builds without the `random` cfg, so this
/// test deliberately avoids any RNG.
#[test]
#[cfg(all(sha256, feature = "alloc"))]
fn test_verifying_key_without_rng() {
extern crate std;
use std::fs;
use wolfssl_wolfcrypt::rsa_pkcs1v15::{Sha256, Signature, VerifyingKey};

common::setup();

let der = fs::read("../../../certs/client-keyPub.der").expect("read public key");
let vk = VerifyingKey::<Sha256, 256>::from_public_der(&der).expect("from_public_der");

// Round-trip through the raw components.
let vk_copy = VerifyingKey::<Sha256, 256>::from_components(vk.modulus(), vk.exponent())
.expect("from_components");
assert_eq!(vk, vk_copy);

// A garbage signature must not verify.
let bogus = Signature::<256>::from_bytes([0xA5u8; 256]);
assert!(vk.verify(b"message", &bogus).is_err());
}
Loading