diff --git a/rustls-wolfcrypt-provider/src/lib.rs b/rustls-wolfcrypt-provider/src/lib.rs index dfeb6f6..3c5aa8b 100644 --- a/rustls-wolfcrypt-provider/src/lib.rs +++ b/rustls-wolfcrypt-provider/src/lib.rs @@ -27,8 +27,7 @@ pub mod aead { pub mod sign { pub mod ecdsa; pub mod eddsa; - pub mod rsapkcs1; - pub mod rsapss; + pub mod rsa; } use crate::aead::{aes128gcm, aes256gcm, chacha20}; @@ -93,22 +92,8 @@ impl rustls::crypto::KeyProvider for Provider { ) -> Result, rustls::Error> { // Define supported algorithms as closures let algorithms: SigningAlgorithms = vec![ - Box::new(|key| { - sign::ecdsa::EcdsaSigningKeyP256Sha256Sign::try_from(key).map(|x| Arc::new(x) as _) - }), - Box::new(|key| { - sign::ecdsa::EcdsaSigningKeyP384Sha384Sign::try_from(key).map(|x| Arc::new(x) as _) - }), - Box::new(|key| { - sign::ecdsa::EcdsaSigningKeyP521Sha512Sign::try_from(key).map(|x| Arc::new(x) as _) - }), - Box::new(|key| sign::rsapss::RsaPssPrivateKey::try_from(key).map(|x| Arc::new(x) as _)), - Box::new(|key| { - sign::rsapkcs1::RsaPkcs1PrivateKey::try_from(key).map(|x| Arc::new(x) as _) - }), - Box::new(|key| { - sign::rsapkcs1::RsaPkcs1PrivateKey::try_from(key).map(|x| Arc::new(x) as _) - }), + Box::new(|key| sign::ecdsa::EcdsaSigningKey::try_from(key).map(|x| Arc::new(x) as _)), + Box::new(|key| sign::rsa::RsaPrivateKey::try_from(key).map(|x| Arc::new(x) as _)), Box::new(|key| sign::eddsa::Ed25519PrivateKey::try_from(key).map(|x| Arc::new(x) as _)), ]; diff --git a/rustls-wolfcrypt-provider/src/sign/ecdsa.rs b/rustls-wolfcrypt-provider/src/sign/ecdsa.rs index ae54bc2..1019a45 100644 --- a/rustls-wolfcrypt-provider/src/sign/ecdsa.rs +++ b/rustls-wolfcrypt-provider/src/sign/ecdsa.rs @@ -1,7 +1,10 @@ +use crate::alloc::string::ToString; use crate::error::*; use crate::types::*; use alloc::boxed::Box; +use alloc::format; use alloc::sync::Arc; +use alloc::vec; use alloc::vec::Vec; use core::mem; use core::ptr; @@ -12,209 +15,99 @@ use rustls::{SignatureAlgorithm, SignatureScheme}; use wolfcrypt_rs::*; +/// A unified ECDSA signing key that supports P-256, P-384, P-521. +/// Internally, we store the raw private key bytes plus +/// which scheme we should use (determined by WolfSSL after decode). #[derive(Clone, Debug)] -pub struct EcdsaSigningKeyP256Sha256Sign { +pub struct EcdsaSigningKey { + /// Raw private key bytes exported from WolfSSL (`wc_ecc_export_private_only`) + /// in big-endian format. key: Arc>, + /// The signature scheme to use (e.g. ECDSA_NISTP256_SHA256). scheme: SignatureScheme, } -impl TryFrom<&PrivateKeyDer<'_>> for EcdsaSigningKeyP256Sha256Sign { +impl TryFrom<&PrivateKeyDer<'_>> for EcdsaSigningKey { type Error = rustls::Error; fn try_from(value: &PrivateKeyDer<'_>) -> Result { - match value { - PrivateKeyDer::Pkcs8(der) => { - let mut ecc_c_type: ecc_key = unsafe { mem::zeroed() }; - let ecc_key_object = ECCKeyObject::new(&mut ecc_c_type); - let pkcs8: &[u8] = der.secret_pkcs8_der(); - let pkcs8_sz: word32 = pkcs8.len() as word32; - let mut priv_key_bytes: [u8; 32] = [0; 32]; - let mut priv_key_bytes_len: word32 = priv_key_bytes.len() as word32; - let mut ret; - - // This function initializes an ecc_key object for - // future use with message signing. - ecc_key_object.init(); - - let mut idx: u32 = 0; - - // This function reads in an ECC private key from the input buffer, input, - // parses the private key, and uses it to generate an ecc_key object, - // which it stores in key. - ret = unsafe { - wc_EccPrivateKeyDecode( - pkcs8.as_ptr() as *mut u8, - &mut idx, - ecc_key_object.as_ptr(), - pkcs8_sz, - ) - }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - ret = unsafe { - wc_ecc_export_private_only( - ecc_key_object.as_ptr(), - priv_key_bytes.as_mut_ptr(), - &mut priv_key_bytes_len, - ) - }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - Ok(Self { - key: Arc::new(priv_key_bytes.to_vec()), - scheme: SignatureScheme::ECDSA_NISTP256_SHA256, - }) + let der_formatted = match value { + PrivateKeyDer::Pkcs8(der) => der.secret_pkcs8_der(), + PrivateKeyDer::Sec1(der) => der.secret_sec1_der(), + PrivateKeyDer::Pkcs1(_) => { + return Err(rustls::Error::General( + "Unsupported ECDSA key format (PKCS#1)".into(), + )) } - _ => Err(rustls::Error::General( - "Unsupported private key format".into(), - )), - } - } -} - -impl SigningKey for EcdsaSigningKeyP256Sha256Sign { - fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { - if offered.contains(&self.scheme) { - Some(Box::new(self.clone())) - } else { - None - } - } - - fn algorithm(&self) -> SignatureAlgorithm { - SignatureAlgorithm::ECDSA - } -} - -impl Signer for EcdsaSigningKeyP256Sha256Sign { - fn sign(&self, message: &[u8]) -> Result, rustls::Error> { - let mut ret; - let mut rng: WC_RNG = unsafe { mem::zeroed() }; - let rng_object: WCRngObject = WCRngObject::new(&mut rng); - let mut digest: [u8; WC_SHA256_DIGEST_SIZE as usize] = [0; WC_SHA256_DIGEST_SIZE as usize]; - let message_length: word32 = message.len() as word32; - let digest_length: word32 = digest.len() as word32; - let mut sig: [u8; ECC_MAX_SIG_SIZE as usize] = [0; ECC_MAX_SIG_SIZE as usize]; - let mut sig_sz: word32 = sig.len() as word32; - let priv_key_bytes = &self.key; - let mut priv_key: ecc_key = unsafe { mem::zeroed() }; - let priv_key_object = ECCKeyObject::new(&mut priv_key); - - // We hash the message, since it's not, using Sha256 (ECDSA_NISTP256_SHA256) - ret = unsafe { wc_Sha256Hash(message.as_ptr(), message_length, digest.as_mut_ptr()) }; - check_if_zero(ret).unwrap(); + _ => { + return Err(rustls::Error::General( + "Unsupported ECDSA key format (not PKCS#8)".into(), + )) + } + }; - rng_object.init(); + let mut ecc_c_type: ecc_key = unsafe { mem::zeroed() }; + let ecc_key_object = ECCKeyObject::new(&mut ecc_c_type); - priv_key_object.init(); + ecc_key_object.init(); - ret = unsafe { - wc_ecc_import_private_key_ex( - priv_key_bytes.as_ptr(), - priv_key_bytes.len() as word32, - ptr::null_mut(), - 0, - priv_key_object.as_ptr(), - ecc_curve_id_ECC_SECP256R1, + let mut idx: u32 = 0; + let ret = unsafe { + wc_EccPrivateKeyDecode( + der_formatted.as_ptr() as *mut u8, + &mut idx, + ecc_key_object.as_ptr(), + der_formatted.len() as word32, ) }; - check_if_zero(ret).unwrap(); + check_if_zero(ret) + .map_err(|_| rustls::Error::General("wc_EccPrivateKeyDecode failed".into()))?; + + let key_size = unsafe { wc_ecc_size(ecc_key_object.as_ptr()) }; + if key_size == 0 { + return Err(rustls::Error::General( + "wc_ecc_size returned 0; invalid key?".into(), + )); + } - ret = unsafe { wc_ecc_set_curve(priv_key_object.as_ptr(), 32, ecc_curve_id_ECC_SECP256R1) }; - check_if_zero(ret).unwrap(); + let mut priv_key_bytes = vec![0u8; key_size as usize]; + let mut priv_key_bytes_len = priv_key_bytes.len() as word32; - // This function signs a message digest - // using an ecc_key object to guarantee authenticity. - ret = unsafe { - wc_ecc_sign_hash( - digest.as_mut_ptr(), - digest_length, - sig.as_mut_ptr(), - &mut sig_sz, - &mut rng, - priv_key_object.as_ptr(), + let ret = unsafe { + wc_ecc_export_private_only( + ecc_key_object.as_ptr(), + priv_key_bytes.as_mut_ptr(), + &mut priv_key_bytes_len, ) }; - check_if_zero(ret).unwrap(); - - let mut sig_vec = sig.to_vec(); + check_if_zero(ret) + .map_err(|_| rustls::Error::General("wc_ecc_export_private_only failed".into()))?; - sig_vec.truncate(sig_sz as usize); + priv_key_bytes.truncate(priv_key_bytes_len as usize); - Ok(sig_vec) - } + let scheme = + curve_id_to_scheme(key_size).map_err(|e| rustls::Error::General(e.to_string()))?; - fn scheme(&self) -> SignatureScheme { - self.scheme + Ok(Self { + key: Arc::new(priv_key_bytes), + scheme, + }) } } -#[derive(Clone, Debug)] -pub struct EcdsaSigningKeyP384Sha384Sign { - key: Arc>, - scheme: SignatureScheme, -} - -impl TryFrom<&PrivateKeyDer<'_>> for EcdsaSigningKeyP384Sha384Sign { - type Error = rustls::Error; - - fn try_from(value: &PrivateKeyDer<'_>) -> Result { - match value { - PrivateKeyDer::Pkcs8(der) => { - let mut ecc_c_type: ecc_key = unsafe { mem::zeroed() }; - let ecc_key_object = ECCKeyObject::new(&mut ecc_c_type); - let pkcs8: &[u8] = der.secret_pkcs8_der(); - let pkcs8_sz: word32 = pkcs8.len() as word32; - let mut priv_key_bytes: [u8; 48] = [0; 48]; - let mut priv_key_bytes_len: word32 = priv_key_bytes.len() as word32; - let mut ret; - - // This function initializes an ecc_key object for - // future use with message signing. - ecc_key_object.init(); - - let mut idx: u32 = 0; - - // This function reads in an ECC private key from the input buffer, input, - // parses the private key, and uses it to generate an ecc_key object, - // which it stores in key. - ret = unsafe { - wc_EccPrivateKeyDecode( - pkcs8.as_ptr() as *mut u8, - &mut idx, - ecc_key_object.as_ptr(), - pkcs8_sz, - ) - }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - ret = unsafe { - wc_ecc_export_private_only( - ecc_key_object.as_ptr(), - priv_key_bytes.as_mut_ptr(), - &mut priv_key_bytes_len, - ) - }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - Ok(Self { - key: Arc::new(priv_key_bytes.to_vec()), - scheme: SignatureScheme::ECDSA_NISTP384_SHA384, - }) - } - _ => Err(rustls::Error::General( - "Unsupported private key format".into(), - )), - } +/// Converts a key size to a `SignatureScheme` (e.g. 32 -> ECDSA_NISTP256_SHA256). +fn curve_id_to_scheme(key_size: i32) -> Result { + match key_size { + 32 => Ok(SignatureScheme::ECDSA_NISTP256_SHA256), + 48 => Ok(SignatureScheme::ECDSA_NISTP384_SHA384), + 66 => Ok(SignatureScheme::ECDSA_NISTP521_SHA512), + _ => Err("Unsupported ECC key size"), } } -impl SigningKey for EcdsaSigningKeyP384Sha384Sign { +impl SigningKey for EcdsaSigningKey { fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { + // If the server (or peer) offered the scheme we have, we can sign with it if offered.contains(&self.scheme) { Some(Box::new(self.clone())) } else { @@ -227,59 +120,56 @@ impl SigningKey for EcdsaSigningKeyP384Sha384Sign { } } -impl Signer for EcdsaSigningKeyP384Sha384Sign { +impl Signer for EcdsaSigningKey { fn sign(&self, message: &[u8]) -> Result, rustls::Error> { - let mut ret; + let digest = hash_message_for_scheme(self.scheme, message) + .map_err(|_| rustls::Error::General("hash failed".into()))?; + let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object: WCRngObject = WCRngObject::new(&mut rng); - let mut digest: [u8; WC_SHA384_DIGEST_SIZE as usize] = [0; WC_SHA384_DIGEST_SIZE as usize]; - let message_length: word32 = message.len() as word32; - let digest_length: word32 = digest.len() as word32; - let mut sig: [u8; ECC_MAX_SIG_SIZE as usize] = [0; ECC_MAX_SIG_SIZE as usize]; - let mut sig_sz: word32 = sig.len() as word32; - let priv_key_bytes = &self.key; - let mut priv_key: ecc_key = unsafe { mem::zeroed() }; - let priv_key_object = ECCKeyObject::new(&mut priv_key); - - // We hash the message, since it's not, using Sha384 (ECDSA_NISTP384_SHA384) - ret = unsafe { wc_Sha384Hash(message.as_ptr(), message_length, digest.as_mut_ptr()) }; - check_if_zero(ret).unwrap(); - rng_object.init(); - priv_key_object.init(); + let mut ecc_c_type: ecc_key = unsafe { mem::zeroed() }; + let ecc_key_object = ECCKeyObject::new(&mut ecc_c_type); + ecc_key_object.init(); - ret = unsafe { + let curve_id = scheme_to_curve_id(self.scheme) + .map_err(|e| rustls::Error::General(format!("scheme_to_curve_id unsupported: {e}")))?; + + let ret = unsafe { wc_ecc_import_private_key_ex( - priv_key_bytes.as_ptr(), - priv_key_bytes.len() as word32, + self.key.as_ptr(), + self.key.len() as word32, ptr::null_mut(), 0, - priv_key_object.as_ptr(), - ecc_curve_id_ECC_SECP384R1, + ecc_key_object.as_ptr(), + curve_id, ) }; - check_if_zero(ret).unwrap(); + check_if_zero(ret) + .map_err(|_| rustls::Error::General("wc_ecc_import_private_key_ex failed".into()))?; + + let ret = + unsafe { wc_ecc_set_curve(ecc_key_object.as_ptr(), self.key.len() as i32, curve_id) }; + check_if_zero(ret).map_err(|_| rustls::Error::General("wc_ecc_set_curve failed".into()))?; - ret = unsafe { wc_ecc_set_curve(priv_key_object.as_ptr(), 48, ecc_curve_id_ECC_SECP384R1) }; - check_if_zero(ret).unwrap(); + let mut sig = [0u8; ECC_MAX_SIG_SIZE as usize]; + let mut sig_sz: word32 = sig.len() as word32; - // This function signs a message digest - // using an ecc_key object to guarantee authenticity. - ret = unsafe { + let ret = unsafe { wc_ecc_sign_hash( - digest.as_mut_ptr(), - digest_length, + digest.as_ptr() as *mut u8, + digest.len() as word32, sig.as_mut_ptr(), &mut sig_sz, - &mut rng, - priv_key_object.as_ptr(), + rng_object.as_ptr(), + ecc_key_object.as_ptr(), ) }; - check_if_zero(ret).unwrap(); + check_if_zero(ret).map_err(|_| rustls::Error::General("wc_ecc_sign_hash failed".into()))?; + // truncate to actual sig size let mut sig_vec = sig.to_vec(); - sig_vec.truncate(sig_sz as usize); Ok(sig_vec) @@ -290,140 +180,65 @@ impl Signer for EcdsaSigningKeyP384Sha384Sign { } } -#[derive(Clone, Debug)] -pub struct EcdsaSigningKeyP521Sha512Sign { - key: Arc>, +/// Hash the input `message` according to the scheme’s hash algorithm. +/// Returns the raw digest bytes. +fn hash_message_for_scheme( scheme: SignatureScheme, -} - -impl TryFrom<&PrivateKeyDer<'_>> for EcdsaSigningKeyP521Sha512Sign { - type Error = rustls::Error; - - fn try_from(value: &PrivateKeyDer<'_>) -> Result { - match value { - PrivateKeyDer::Pkcs8(der) => { - let mut ecc_c_type: ecc_key = unsafe { mem::zeroed() }; - let ecc_key_object = ECCKeyObject::new(&mut ecc_c_type); - let pkcs8: &[u8] = der.secret_pkcs8_der(); - let pkcs8_sz: word32 = pkcs8.len() as word32; - let mut priv_key_bytes: [u8; 66] = [0; 66]; - let mut priv_key_bytes_len: word32 = priv_key_bytes.len() as word32; - let mut ret; - - // This function initializes an ecc_key object for - // future use with message signing. - ecc_key_object.init(); - - let mut idx: u32 = 0; - - // This function reads in an ECC private key from the input buffer, input, - // parses the private key, and uses it to generate an ecc_key object, - // which it stores in key. - ret = unsafe { - wc_EccPrivateKeyDecode( - pkcs8.as_ptr() as *mut u8, - &mut idx, - ecc_key_object.as_ptr(), - pkcs8_sz, - ) - }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - ret = unsafe { - wc_ecc_export_private_only( - ecc_key_object.as_ptr(), - priv_key_bytes.as_mut_ptr(), - &mut priv_key_bytes_len, - ) - }; - check_if_zero(ret).unwrap(); - - Ok(Self { - key: Arc::new(priv_key_bytes.to_vec()), - scheme: SignatureScheme::ECDSA_NISTP521_SHA512, - }) + message: &[u8], +) -> Result, &'static str> { + match scheme { + SignatureScheme::ECDSA_NISTP256_SHA256 => { + let mut digest = vec![0u8; WC_SHA256_DIGEST_SIZE as usize]; + let ret = unsafe { + wc_Sha256Hash( + message.as_ptr(), + message.len() as word32, + digest.as_mut_ptr(), + ) + }; + if ret != 0 { + return Err("wc_Sha256Hash failed"); } - _ => Err(rustls::Error::General( - "Unsupported private key format".into(), - )), + Ok(digest) } - } -} - -impl SigningKey for EcdsaSigningKeyP521Sha512Sign { - fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { - if offered.contains(&self.scheme) { - Some(Box::new(self.clone())) - } else { - None + SignatureScheme::ECDSA_NISTP384_SHA384 => { + let mut digest = vec![0u8; WC_SHA384_DIGEST_SIZE as usize]; + let ret = unsafe { + wc_Sha384Hash( + message.as_ptr(), + message.len() as word32, + digest.as_mut_ptr(), + ) + }; + if ret != 0 { + return Err("wc_Sha384Hash failed"); + } + Ok(digest) } - } - - fn algorithm(&self) -> SignatureAlgorithm { - SignatureAlgorithm::ECDSA + SignatureScheme::ECDSA_NISTP521_SHA512 => { + let mut digest = vec![0u8; WC_SHA512_DIGEST_SIZE as usize]; + let ret = unsafe { + wc_Sha512Hash( + message.as_ptr(), + message.len() as word32, + digest.as_mut_ptr(), + ) + }; + if ret != 0 { + return Err("wc_Sha512Hash failed"); + } + Ok(digest) + } + _ => Err("Unsupported scheme for ECDSA signing"), } } -impl Signer for EcdsaSigningKeyP521Sha512Sign { - fn sign(&self, message: &[u8]) -> Result, rustls::Error> { - let mut ret; - let mut rng: WC_RNG = unsafe { mem::zeroed() }; - let rng_object: WCRngObject = WCRngObject::new(&mut rng); - let mut digest: [u8; WC_SHA512_DIGEST_SIZE as usize] = [0; WC_SHA512_DIGEST_SIZE as usize]; - let message_length: word32 = message.len() as word32; - let digest_length: word32 = digest.len() as word32; - let mut sig: [u8; ECC_MAX_SIG_SIZE as usize] = [0; ECC_MAX_SIG_SIZE as usize]; - let mut sig_sz: word32 = sig.len() as word32; - let priv_key_bytes = &self.key; - let mut priv_key: ecc_key = unsafe { mem::zeroed() }; - let priv_key_object = ECCKeyObject::new(&mut priv_key); - - // We hash the message, since it's not, using Sha384 (ECDSA_NISTP384_SHA384) - ret = unsafe { wc_Sha512Hash(message.as_ptr(), message_length, digest.as_mut_ptr()) }; - check_if_zero(ret).unwrap(); - - rng_object.init(); - - priv_key_object.init(); - - ret = unsafe { - wc_ecc_import_private_key_ex( - priv_key_bytes.as_ptr(), - priv_key_bytes.len() as word32, - ptr::null_mut(), - 0, - priv_key_object.as_ptr(), - ecc_curve_id_ECC_SECP521R1, - ) - }; - check_if_zero(ret).unwrap(); - - ret = unsafe { wc_ecc_set_curve(priv_key_object.as_ptr(), 66, ecc_curve_id_ECC_SECP521R1) }; - check_if_zero(ret).unwrap(); - - // This function signs a message digest - // using an ecc_key object to guarantee authenticity. - ret = unsafe { - wc_ecc_sign_hash( - digest.as_mut_ptr(), - digest_length, - sig.as_mut_ptr(), - &mut sig_sz, - &mut rng, - priv_key_object.as_ptr(), - ) - }; - check_if_zero(ret).unwrap(); - - let mut sig_vec = sig.to_vec(); - - sig_vec.truncate(sig_sz as usize); - - Ok(sig_vec) - } - - fn scheme(&self) -> SignatureScheme { - self.scheme +/// Converts a rustls `SignatureScheme` to the WolfSSL curve id (ecc_curve_id_ECC_...). +fn scheme_to_curve_id(scheme: SignatureScheme) -> Result { + match scheme { + SignatureScheme::ECDSA_NISTP256_SHA256 => Ok(ecc_curve_id_ECC_SECP256R1), + SignatureScheme::ECDSA_NISTP384_SHA384 => Ok(ecc_curve_id_ECC_SECP384R1), + SignatureScheme::ECDSA_NISTP521_SHA512 => Ok(ecc_curve_id_ECC_SECP521R1), + _ => Err("Not an ECDSA_NISTPxxx_SHAxxx scheme"), } } diff --git a/rustls-wolfcrypt-provider/src/sign/rsa.rs b/rustls-wolfcrypt-provider/src/sign/rsa.rs new file mode 100644 index 0000000..06327d0 --- /dev/null +++ b/rustls-wolfcrypt-provider/src/sign/rsa.rs @@ -0,0 +1,294 @@ +use crate::error::*; +use crate::types::*; +use alloc::boxed::Box; +use alloc::sync::Arc; +use alloc::vec; +use alloc::vec::Vec; +use core::mem; +use foreign_types::ForeignType; +use rustls::pki_types::PrivateKeyDer; +use rustls::sign::{Signer, SigningKey}; +use rustls::{SignatureAlgorithm, SignatureScheme}; + +use core::ptr; +use wolfcrypt_rs::*; + +const ALL_RSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, +]; + +const MAX_RSA_SIG_SIZE: usize = 512; +const HASH_TYPE_SHA256: u32 = wc_HashType_WC_HASH_TYPE_SHA256; +const HASH_TYPE_SHA384: u32 = wc_HashType_WC_HASH_TYPE_SHA384; +const HASH_TYPE_SHA512: u32 = wc_HashType_WC_HASH_TYPE_SHA512; + +const MGF1_SHA256: u32 = WC_MGF1SHA256; +const MGF1_SHA384: u32 = WC_MGF1SHA384; +const MGF1_SHA512: u32 = WC_MGF1SHA512; + +#[derive(Clone, Debug)] +pub struct RsaPrivateKey { + key: Arc, + algo: SignatureAlgorithm, +} + +impl RsaPrivateKey { + pub fn get_key(&self) -> Arc { + Arc::clone(&self.key) + } +} + +impl TryFrom<&PrivateKeyDer<'_>> for RsaPrivateKey { + type Error = rustls::Error; + + fn try_from(value: &PrivateKeyDer<'_>) -> Result { + match value { + PrivateKeyDer::Pkcs8(der) => { + let pkcs8: &[u8] = der.secret_pkcs8_der(); + let pkcs8_sz: word32 = pkcs8.len() as word32; + let mut ret; + let rsa_key_box = Box::new(unsafe { mem::zeroed::() }); + let rsa_key_ptr = Box::into_raw(rsa_key_box); + let rsa_key_object = unsafe { RsaKeyObject::from_ptr(rsa_key_ptr) }; + + ret = unsafe { wc_InitRsaKey(rsa_key_object.as_ptr(), ptr::null_mut()) }; + check_if_zero(ret).unwrap(); + + let mut idx: u32 = 0; + + ret = unsafe { + wc_RsaPrivateKeyDecode( + pkcs8.as_ptr() as *mut u8, + &mut idx, + rsa_key_object.as_ptr(), + pkcs8_sz, + ) + }; + check_if_zero(ret) + .map_err(|_| rustls::Error::General("FFI function failed".into()))?; + + Ok(Self { + key: Arc::new(rsa_key_object), + algo: SignatureAlgorithm::RSA, + }) + } + PrivateKeyDer::Pkcs1(der) => { + let pkcs1: &[u8] = der.secret_pkcs1_der(); + let pkcs1_sz: word32 = pkcs1.len() as word32; + let mut ret; + let rsa_key_box = Box::new(unsafe { mem::zeroed::() }); + let rsa_key_ptr = Box::into_raw(rsa_key_box); + let rsa_key_object = unsafe { RsaKeyObject::from_ptr(rsa_key_ptr) }; + + ret = unsafe { wc_InitRsaKey(rsa_key_object.as_ptr(), ptr::null_mut()) }; + check_if_zero(ret).unwrap(); + + let mut idx: u32 = 0; + + ret = unsafe { + wc_RsaPrivateKeyDecode( + pkcs1.as_ptr() as *mut u8, + &mut idx, + rsa_key_object.as_ptr(), + pkcs1_sz, + ) + }; + check_if_zero(ret) + .map_err(|_| rustls::Error::General("FFI function failed".into()))?; + + Ok(Self { + key: Arc::new(rsa_key_object), + algo: SignatureAlgorithm::RSA, + }) + } + _ => Err(rustls::Error::General( + "Unsupported private key format".into(), + )), + } + } +} + +impl SigningKey for RsaPrivateKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { + // Iterate through all RSA schemes and check if any is in the offered list + ALL_RSA_SCHEMES.iter().find_map(|&scheme| { + if offered.contains(&scheme) { + Some(Box::new(RsaSigner { + key: self.get_key(), + scheme, + }) as Box) + } else { + None + } + }) + } + + fn algorithm(&self) -> SignatureAlgorithm { + self.algo + } +} + +#[derive(Clone, Debug)] +pub struct RsaSigner { + key: Arc, + scheme: SignatureScheme, +} + +impl RsaSigner { + pub fn new(key: Arc, scheme: SignatureScheme) -> Self { + Self { key, scheme } + } + + fn get_key(&self) -> Arc { + Arc::clone(&self.key) + } +} + +impl Signer for RsaSigner { + fn sign(&self, message: &[u8]) -> Result, rustls::Error> { + let rsa_key_arc = self.get_key(); + let rsa_key_object = rsa_key_arc.as_ref(); + + // Prepare a random generator + let mut rng: WC_RNG = unsafe { mem::zeroed() }; + let rng_object = WCRngObject::new(&mut rng); + rng_object.init(); + + // Allocate enough space for the signature + let mut sig_buf = [0u8; MAX_RSA_SIG_SIZE]; + + match self.scheme { + // ------------------------------------------------ + // RSA-PSS branch: + // ------------------------------------------------ + SignatureScheme::RSA_PSS_SHA256 + | SignatureScheme::RSA_PSS_SHA384 + | SignatureScheme::RSA_PSS_SHA512 => { + // We'll do explicit hashing plus wc_RsaPSS_Sign. + + // 1) Determine hash algorithm & MGF + let (hash_ty, mgf_ty, digest_len) = match self.scheme { + SignatureScheme::RSA_PSS_SHA256 => { + (HASH_TYPE_SHA256, MGF1_SHA256, WC_SHA256_DIGEST_SIZE) + } + SignatureScheme::RSA_PSS_SHA384 => { + (HASH_TYPE_SHA384, MGF1_SHA384, WC_SHA384_DIGEST_SIZE) + } + SignatureScheme::RSA_PSS_SHA512 => { + (HASH_TYPE_SHA512, MGF1_SHA512, WC_SHA512_DIGEST_SIZE) + } + _ => unreachable!(), + }; + + // 2) Hash the message ourselves + let mut digest = vec![0u8; digest_len as usize]; + let ret = unsafe { + match hash_ty { + HASH_TYPE_SHA256 => wc_Sha256Hash( + message.as_ptr(), + message.len() as u32, + digest.as_mut_ptr(), + ), + HASH_TYPE_SHA384 => wc_Sha384Hash( + message.as_ptr(), + message.len() as u32, + digest.as_mut_ptr(), + ), + HASH_TYPE_SHA512 => wc_Sha512Hash( + message.as_ptr(), + message.len() as u32, + digest.as_mut_ptr(), + ), + _ => -1, + } + }; + check_if_zero(ret) + .map_err(|_| rustls::Error::General("Failed to hash for PSS".into()))?; + + // 3) Sign with wc_RsaPSS_Sign + let ret = unsafe { + wc_RsaPSS_Sign( + digest.as_ptr(), + digest_len, + sig_buf.as_mut_ptr(), + sig_buf.len() as u32, + hash_ty, + mgf_ty.try_into().unwrap(), + rsa_key_object.as_ptr(), + rng_object.as_ptr(), + ) + }; + check_if_greater_than_zero(ret) + .map_err(|_| rustls::Error::General("wc_RsaPSS_Sign failed".into()))?; + + let sig_len = ret as usize; + let mut sig_vec = sig_buf.to_vec(); + sig_vec.truncate(sig_len); + Ok(sig_vec) + } + + // ------------------------------------------------ + // RSA-PKCS#1 branch: + // ------------------------------------------------ + SignatureScheme::RSA_PKCS1_SHA256 + | SignatureScheme::RSA_PKCS1_SHA384 + | SignatureScheme::RSA_PKCS1_SHA512 => { + // We'll let wc_SignatureGenerate do the hashing & PKCS#1. + let hash_ty = match self.scheme { + SignatureScheme::RSA_PKCS1_SHA256 => HASH_TYPE_SHA256, + SignatureScheme::RSA_PKCS1_SHA384 => HASH_TYPE_SHA384, + SignatureScheme::RSA_PKCS1_SHA512 => HASH_TYPE_SHA512, + _ => unreachable!(), + }; + + let mut sig_len: u32 = sig_buf.len() as u32; + + // wc_SignatureGenerate will produce a PKCS#1 signature, including hashing. + let deref_rsa_key_c_type = unsafe { *(rsa_key_object.as_ptr()) }; + let ret = unsafe { + wc_SignatureGenerate( + hash_ty, + wc_SignatureType_WC_SIGNATURE_TYPE_RSA_W_ENC, + message.as_ptr(), + message.len() as u32, + sig_buf.as_mut_ptr(), + &mut sig_len, + rsa_key_object.as_ptr() as *const core::ffi::c_void, + mem::size_of_val(&deref_rsa_key_c_type).try_into().unwrap(), + rng_object.as_ptr(), + ) + }; + check_if_zero(ret) + .map_err(|_| rustls::Error::General("wc_SignatureGenerate failed".into()))?; + + // Check how big the actual signature is + let actual_sig_size = unsafe { + wc_SignatureGetSize( + wc_SignatureType_WC_SIGNATURE_TYPE_RSA_W_ENC, + rsa_key_object.as_ptr() as *const core::ffi::c_void, + mem::size_of_val(&deref_rsa_key_c_type).try_into().unwrap(), + ) + }; + + let mut sig_vec = sig_buf.to_vec(); + // Truncate to the size returned by wc_SignatureGetSize or the updated `sig_len`. + let min_len = core::cmp::min(actual_sig_size as usize, sig_len as usize); + sig_vec.truncate(min_len); + + Ok(sig_vec) + } + + // If someone tries a scheme that isn't RSA... + _ => Err(rustls::Error::General("Unsupported RSA scheme".into())), + } + } + + fn scheme(&self) -> SignatureScheme { + self.scheme + } +} diff --git a/rustls-wolfcrypt-provider/src/sign/rsapkcs1.rs b/rustls-wolfcrypt-provider/src/sign/rsapkcs1.rs deleted file mode 100644 index ce8746c..0000000 --- a/rustls-wolfcrypt-provider/src/sign/rsapkcs1.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::error::*; -use crate::types::*; -use alloc::boxed::Box; -use alloc::sync::Arc; -use alloc::vec::Vec; -use core::ffi::c_void; -use core::mem; -use core::ptr; -use foreign_types::ForeignType; -use rustls::pki_types::PrivateKeyDer; -use rustls::sign::{Signer, SigningKey}; -use rustls::{SignatureAlgorithm, SignatureScheme}; -use wolfcrypt_rs::*; - -const ALL_RSA_SCHEMES: &[SignatureScheme] = &[ - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::RSA_PKCS1_SHA512, -]; - -#[derive(Clone, Debug)] -pub struct RsaPkcs1PrivateKey { - key: Arc, - algo: SignatureAlgorithm, -} - -impl RsaPkcs1PrivateKey { - pub fn get_key(&self) -> Arc { - Arc::clone(&self.key) - } -} - -const RSA_PKCS1_SIG_SIZE: u32 = 512; - -impl TryFrom<&PrivateKeyDer<'_>> for RsaPkcs1PrivateKey { - type Error = rustls::Error; - - fn try_from(value: &PrivateKeyDer<'_>) -> Result { - match value { - PrivateKeyDer::Pkcs1(der) => { - let pkcs1: &[u8] = der.secret_pkcs1_der(); - let pkcs1_sz: word32 = pkcs1.len() as word32; - let mut ret; - let rsa_key_box = Box::new(unsafe { mem::zeroed::() }); - let rsa_key_ptr = Box::into_raw(rsa_key_box); - let rsa_key_object = unsafe { RsaKeyObject::from_ptr(rsa_key_ptr) }; - - ret = unsafe { wc_InitRsaKey(rsa_key_object.as_ptr(), ptr::null_mut()) }; - check_if_zero(ret).unwrap(); - - let mut idx: u32 = 0; - - ret = unsafe { - wc_RsaPrivateKeyDecode( - pkcs1.as_ptr() as *mut u8, - &mut idx, - rsa_key_object.as_ptr(), - pkcs1_sz, - ) - }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - Ok(Self { - key: Arc::new(rsa_key_object), - algo: SignatureAlgorithm::RSA, - }) - } - _ => Err(rustls::Error::General( - "Unsupported private key format".into(), - )), - } - } -} - -impl SigningKey for RsaPkcs1PrivateKey { - fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { - // Iterate through all RSA schemes and check if any is in the offered list - ALL_RSA_SCHEMES.iter().find_map(|&scheme| { - if offered.contains(&scheme) { - Some(Box::new(RsaPkcs1Signer { - key: self.get_key(), - scheme, - }) as Box) - } else { - None - } - }) - } - - fn algorithm(&self) -> SignatureAlgorithm { - self.algo - } -} - -#[derive(Clone, Debug)] -pub struct RsaPkcs1Signer { - key: Arc, - scheme: SignatureScheme, -} - -impl RsaPkcs1Signer { - pub fn get_key(&self) -> Arc { - Arc::clone(&self.key) - } -} - -impl Signer for RsaPkcs1Signer { - fn sign(&self, message: &[u8]) -> Result, rustls::Error> { - let mut rng: WC_RNG = unsafe { mem::zeroed() }; - let rng_object: WCRngObject = WCRngObject::new(&mut rng); - let mut sig: [u8; RSA_PKCS1_SIG_SIZE as usize] = [0; RSA_PKCS1_SIG_SIZE as usize]; - let mut sig_len: word32 = sig.len() as word32; - let rsa_key_arc = self.get_key(); - let rsa_key_object = rsa_key_arc.as_ref(); - - // Define Rust-style aliases for binding constants - const HASH_TYPE_SHA256: u32 = wc_HashType_WC_HASH_TYPE_SHA256; - const HASH_TYPE_SHA384: u32 = wc_HashType_WC_HASH_TYPE_SHA384; - const HASH_TYPE_SHA512: u32 = wc_HashType_WC_HASH_TYPE_SHA512; - - // Determine the hashing algorithm, digest size, and MGF type based on the scheme - let hash_type: u32 = match self.scheme { - SignatureScheme::RSA_PKCS1_SHA256 => HASH_TYPE_SHA256, - SignatureScheme::RSA_PKCS1_SHA384 => HASH_TYPE_SHA384, - SignatureScheme::RSA_PKCS1_SHA512 => HASH_TYPE_SHA512, - _ => { - return Err(rustls::Error::General( - "Unsupported signature scheme".into(), - )); - } - }; - - rng_object.init(); - - let derefenced_rsa_key_c_type = unsafe { *(rsa_key_object.as_ptr()) }; - - // Sign the digest using the appropriate scheme - let ret = unsafe { - wc_SignatureGenerate( - hash_type, - wc_SignatureType_WC_SIGNATURE_TYPE_RSA_W_ENC, - message.as_ptr(), - message.len() as word32, - sig.as_mut_ptr(), - &mut sig_len, - rsa_key_object.as_ptr() as *const c_void, - mem::size_of_val(&derefenced_rsa_key_c_type) - .try_into() - .unwrap(), - rng_object.as_ptr(), - ) - }; - check_if_zero(ret).map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - let sz = unsafe { - wc_SignatureGetSize( - wc_SignatureType_WC_SIGNATURE_TYPE_RSA_W_ENC, - rsa_key_object.as_ptr() as *const c_void, - mem::size_of_val(&derefenced_rsa_key_c_type) - .try_into() - .unwrap(), - ) - }; - - // Convert the signature to a Vec and truncate to the actual size - let mut sig_vec = sig.to_vec(); - sig_vec.truncate(sz as usize); - - Ok(sig_vec) - } - - fn scheme(&self) -> SignatureScheme { - self.scheme - } -} diff --git a/rustls-wolfcrypt-provider/src/sign/rsapss.rs b/rustls-wolfcrypt-provider/src/sign/rsapss.rs deleted file mode 100644 index 3ae7b4a..0000000 --- a/rustls-wolfcrypt-provider/src/sign/rsapss.rs +++ /dev/null @@ -1,211 +0,0 @@ -use crate::error::*; -use crate::types::*; -use alloc::boxed::Box; -use alloc::sync::Arc; -use alloc::vec; -use alloc::vec::Vec; -use core::mem; -use foreign_types::ForeignType; -use rustls::pki_types::PrivateKeyDer; -use rustls::sign::{Signer, SigningKey}; -use rustls::{SignatureAlgorithm, SignatureScheme}; - -use core::ptr; -use wolfcrypt_rs::*; - -const ALL_RSA_SCHEMES: &[SignatureScheme] = &[ - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512, -]; - -#[derive(Clone, Debug)] -pub struct RsaPssPrivateKey { - key: Arc, - algo: SignatureAlgorithm, -} - -impl RsaPssPrivateKey { - pub fn get_key(&self) -> Arc { - Arc::clone(&self.key) - } -} - -const RSA_PSS_SIG_SIZE: u32 = 512; - -impl TryFrom<&PrivateKeyDer<'_>> for RsaPssPrivateKey { - type Error = rustls::Error; - - fn try_from(value: &PrivateKeyDer<'_>) -> Result { - match value { - PrivateKeyDer::Pkcs8(der) => { - let pkcs8: &[u8] = der.secret_pkcs8_der(); - let pkcs8_sz: word32 = pkcs8.len() as word32; - let mut ret; - let rsa_key_box = Box::new(unsafe { mem::zeroed::() }); - let rsa_key_ptr = Box::into_raw(rsa_key_box); - let rsa_key_object = unsafe { RsaKeyObject::from_ptr(rsa_key_ptr) }; - - ret = unsafe { wc_InitRsaKey(rsa_key_object.as_ptr(), ptr::null_mut()) }; - check_if_zero(ret).unwrap(); - - let mut idx: u32 = 0; - - ret = unsafe { - wc_RsaPrivateKeyDecode( - pkcs8.as_ptr() as *mut u8, - &mut idx, - rsa_key_object.as_ptr(), - pkcs8_sz, - ) - }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - Ok(Self { - key: Arc::new(rsa_key_object), - algo: SignatureAlgorithm::RSA, - }) - } - _ => Err(rustls::Error::General( - "Unsupported private key format".into(), - )), - } - } -} - -impl SigningKey for RsaPssPrivateKey { - fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { - // Iterate through all RSA schemes and check if any is in the offered list - ALL_RSA_SCHEMES.iter().find_map(|&scheme| { - if offered.contains(&scheme) { - Some(Box::new(RsaPssSigner { - key: self.get_key(), - scheme, - }) as Box) - } else { - None - } - }) - } - - fn algorithm(&self) -> SignatureAlgorithm { - self.algo - } -} - -#[derive(Clone, Debug)] -pub struct RsaPssSigner { - key: Arc, - scheme: SignatureScheme, -} - -impl RsaPssSigner { - pub fn get_key(&self) -> Arc { - Arc::clone(&self.key) - } -} - -impl Signer for RsaPssSigner { - fn sign(&self, message: &[u8]) -> Result, rustls::Error> { - let mut rng: WC_RNG = unsafe { mem::zeroed() }; - let rng_object: WCRngObject = WCRngObject::new(&mut rng); - let mut sig: [u8; RSA_PSS_SIG_SIZE as usize] = [0; RSA_PSS_SIG_SIZE as usize]; - let rsa_key_arc = self.get_key(); - let rsa_key_object = rsa_key_arc.as_ref(); - let mut digest: Vec; - let digest_length: word32; - let hash_type; - let mgf_type; - - // Define Rust-style aliases for binding constants - const HASH_TYPE_SHA256: u32 = wc_HashType_WC_HASH_TYPE_SHA256; - const HASH_TYPE_SHA384: u32 = wc_HashType_WC_HASH_TYPE_SHA384; - const HASH_TYPE_SHA512: u32 = wc_HashType_WC_HASH_TYPE_SHA512; - - const MGF1_SHA256: u32 = WC_MGF1SHA256; - const MGF1_SHA384: u32 = WC_MGF1SHA384; - const MGF1_SHA512: u32 = WC_MGF1SHA512; - - // Determine the hashing algorithm, digest size, and MGF type based on the scheme - match self.scheme { - SignatureScheme::RSA_PSS_SHA256 => { - digest = vec![0; WC_SHA256_DIGEST_SIZE as usize]; - digest_length = WC_SHA256_DIGEST_SIZE as word32; - hash_type = HASH_TYPE_SHA256; - mgf_type = MGF1_SHA256; - } - SignatureScheme::RSA_PSS_SHA384 => { - digest = vec![0; WC_SHA384_DIGEST_SIZE as usize]; - digest_length = WC_SHA384_DIGEST_SIZE as word32; - hash_type = HASH_TYPE_SHA384; - mgf_type = MGF1_SHA384; - } - SignatureScheme::RSA_PSS_SHA512 => { - digest = vec![0; WC_SHA512_DIGEST_SIZE as usize]; - digest_length = WC_SHA512_DIGEST_SIZE as word32; - hash_type = HASH_TYPE_SHA512; - mgf_type = MGF1_SHA512; - } - _ => { - return Err(rustls::Error::General( - "Unsupported signature scheme".into(), - )); - } - } - - // Initialize RNG - rng_object.init(); - - // Hash the message using the selected hashing algorithm - let ret = unsafe { - match hash_type { - HASH_TYPE_SHA256 => wc_Sha256Hash( - message.as_ptr(), - message.len() as word32, - digest.as_mut_ptr(), - ), - HASH_TYPE_SHA384 => wc_Sha384Hash( - message.as_ptr(), - message.len() as word32, - digest.as_mut_ptr(), - ), - HASH_TYPE_SHA512 => wc_Sha512Hash( - message.as_ptr(), - message.len() as word32, - digest.as_mut_ptr(), - ), - _ => -1, // Should not reach here - } - }; - check_if_zero(ret).unwrap(); - - // Sign the digest using the appropriate scheme - let ret = unsafe { - wc_RsaPSS_Sign( - digest.as_ptr(), - digest_length, - sig.as_mut_ptr(), - sig.len() as word32, - hash_type, - mgf_type.try_into().unwrap(), - rsa_key_object.as_ptr(), - rng_object.as_ptr(), - ) - }; - check_if_greater_than_zero(ret) - .map_err(|_| rustls::Error::General("FFI function failed".into()))?; - - let sz = ret; - - // Convert the signature to a Vec and truncate to the actual size - let mut sig_vec = sig.to_vec(); - sig_vec.truncate(sz as usize); - - Ok(sig_vec) - } - - fn scheme(&self) -> SignatureScheme { - self.scheme - } -} diff --git a/rustls-wolfcrypt-provider/src/verify.rs b/rustls-wolfcrypt-provider/src/verify.rs index 8e37fbc..db787b1 100644 --- a/rustls-wolfcrypt-provider/src/verify.rs +++ b/rustls-wolfcrypt-provider/src/verify.rs @@ -38,7 +38,7 @@ static RSA_PSS_SHA512: &dyn SignatureVerificationAlgorithm = &rsapss::RsaPssSha5 static RSA_PKCS1_SHA256: &dyn SignatureVerificationAlgorithm = &rsapkcs1::RsaPkcs1Sha256Verify; static RSA_PKCS1_SHA384: &dyn SignatureVerificationAlgorithm = &rsapkcs1::RsaPkcs1Sha384Verify; static RSA_PKCS1_SHA512: &dyn SignatureVerificationAlgorithm = &rsapkcs1::RsaPkcs1Sha512Verify; -static ECDSA_P256_SHA256: &dyn SignatureVerificationAlgorithm = &ecdsa::EcdsaNistp256Sha256; -static ECDSA_P384_SHA384: &dyn SignatureVerificationAlgorithm = &ecdsa::EcdsaNistp384Sha384; -static ECDSA_P521_SHA512: &dyn SignatureVerificationAlgorithm = &ecdsa::EcdsaNistp521Sha512; +static ECDSA_P256_SHA256: &dyn SignatureVerificationAlgorithm = &ecdsa::EcdsaVerifier::P256_SHA256; +static ECDSA_P384_SHA384: &dyn SignatureVerificationAlgorithm = &ecdsa::EcdsaVerifier::P384_SHA384; +static ECDSA_P521_SHA512: &dyn SignatureVerificationAlgorithm = &ecdsa::EcdsaVerifier::P521_SHA512; static ED25519: &dyn SignatureVerificationAlgorithm = &eddsa::Ed25519; diff --git a/rustls-wolfcrypt-provider/src/verify/ecdsa.rs b/rustls-wolfcrypt-provider/src/verify/ecdsa.rs index 7d76cbf..cb22019 100644 --- a/rustls-wolfcrypt-provider/src/verify/ecdsa.rs +++ b/rustls-wolfcrypt-provider/src/verify/ecdsa.rs @@ -2,103 +2,56 @@ use crate::{ error::{check_if_one, check_if_zero, WCError}, types::*, }; +use alloc::vec; use core::mem; use core::ptr; use foreign_types::ForeignType; use rustls::pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm}; +use rustls::SignatureScheme; use webpki::alg_id; use wolfcrypt_rs::*; +/// A unified ECDSA verifier for P-256, P-384, and P-521. +/// We store the `SignatureScheme` and switch logic accordingly. #[derive(Debug)] -pub struct EcdsaNistp256Sha256; - -impl SignatureVerificationAlgorithm for EcdsaNistp256Sha256 { - fn public_key_alg_id(&self) -> AlgorithmIdentifier { - alg_id::ECDSA_P256 - } - - fn signature_alg_id(&self) -> AlgorithmIdentifier { - alg_id::ECDSA_SHA256 - } - - fn verify_signature( - &self, - public_key: &[u8], - message: &[u8], - signature: &[u8], - ) -> Result<(), InvalidSignature> { - unsafe { - let mut ecc_c_type: ecc_key = mem::zeroed(); - let ecc_key_object = ECCKeyObject::from_ptr(&mut ecc_c_type); - let mut digest: [u8; WC_SHA256_DIGEST_SIZE as usize] = - [0; WC_SHA256_DIGEST_SIZE as usize]; - let mut ret; - let mut stat: i32 = 0; - - ecc_key_object.init(); - - /* - * Skipping first byte because rustls uses this format: - * https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.2. - * */ - ret = wc_ecc_import_unsigned( - ecc_key_object.as_ptr(), - public_key[1..33].as_ptr(), /* Public "x" Coordinate */ - public_key[33..].as_ptr(), /* Public "y" Coordinate */ - ptr::null_mut(), /* Private "d" (optional) */ - ecc_curve_id_ECC_SECP256R1, /* ECC Curve Id */ - ); - check_if_zero(ret).unwrap(); - - // This function returns the size of the digest (output) for a hash_type. - // The returns size is used to make sure the output buffer - // provided to wc_Hash is large enough. - let digest_sz = wc_HashGetDigestSize(wc_HashType_WC_HASH_TYPE_SHA256); - - // This function performs a hash on the provided data buffer and - // returns it in the hash buffer provided. - // In this case we hash with Sha256 (RSA_PSS_SHA256). - // We hash the message since it's not hashed. - ret = wc_Hash( - wc_HashType_WC_HASH_TYPE_SHA256, - message.as_ptr(), - message.len() as word32, - digest.as_mut_ptr(), - digest_sz as word32, - ); - check_if_zero(ret).unwrap(); - - ret = wc_ecc_verify_hash( - signature.as_ptr(), - signature.len() as word32, - digest.as_ptr(), - digest_sz as word32, - &mut stat, - ecc_key_object.as_ptr(), - ); - if stat != 1 { - panic!("ret = {}, stat = {}", ret, stat); - } - - if let Err(WCError::Failure) = check_if_one(stat) { - Err(InvalidSignature) - } else { - Ok(()) - } - } - } +pub struct EcdsaVerifier { + scheme: SignatureScheme, } -#[derive(Debug)] -pub struct EcdsaNistp384Sha384; +impl EcdsaVerifier { + /// Constructor for P-256 / ECDSA_NISTP256_SHA256 + pub const P256_SHA256: Self = Self { + scheme: SignatureScheme::ECDSA_NISTP256_SHA256, + }; + + /// Constructor for P-384 / ECDSA_NISTP384_SHA384 + pub const P384_SHA384: Self = Self { + scheme: SignatureScheme::ECDSA_NISTP384_SHA384, + }; + + /// Constructor for P-521 / ECDSA_NISTP521_SHA512 + pub const P521_SHA512: Self = Self { + scheme: SignatureScheme::ECDSA_NISTP521_SHA512, + }; +} -impl SignatureVerificationAlgorithm for EcdsaNistp384Sha384 { +impl SignatureVerificationAlgorithm for EcdsaVerifier { fn public_key_alg_id(&self) -> AlgorithmIdentifier { - alg_id::ECDSA_P384 + match self.scheme { + SignatureScheme::ECDSA_NISTP256_SHA256 => alg_id::ECDSA_P256, + SignatureScheme::ECDSA_NISTP384_SHA384 => alg_id::ECDSA_P384, + SignatureScheme::ECDSA_NISTP521_SHA512 => alg_id::ECDSA_P521, + _ => unreachable!("Unsupported scheme for ECDSA public_key_alg_id"), + } } fn signature_alg_id(&self) -> AlgorithmIdentifier { - alg_id::ECDSA_SHA384 + match self.scheme { + SignatureScheme::ECDSA_NISTP256_SHA256 => alg_id::ECDSA_SHA256, + SignatureScheme::ECDSA_NISTP384_SHA384 => alg_id::ECDSA_SHA384, + SignatureScheme::ECDSA_NISTP521_SHA512 => alg_id::ECDSA_SHA512, + _ => unreachable!("Unsupported scheme for ECDSA signature_alg_id"), + } } fn verify_signature( @@ -108,119 +61,61 @@ impl SignatureVerificationAlgorithm for EcdsaNistp384Sha384 { signature: &[u8], ) -> Result<(), InvalidSignature> { unsafe { + // Initialize WolfSSL ECC key let mut ecc_c_type: ecc_key = mem::zeroed(); let ecc_key_object = ECCKeyObject::from_ptr(&mut ecc_c_type); - let mut digest: [u8; WC_SHA384_DIGEST_SIZE as usize] = - [0; WC_SHA384_DIGEST_SIZE as usize]; - let mut ret; - let mut stat: i32 = 0; - ecc_key_object.init(); - /* - * Skipping first byte because rustls uses this format: - * https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.2. - * */ - ret = wc_ecc_import_unsigned( - ecc_key_object.as_ptr(), - public_key[1..49].as_ptr(), /* Public "x" Coordinate */ - public_key[49..].as_ptr(), /* Public "y" Coordinate */ - ptr::null_mut(), /* Private "d" (optional) */ - ecc_curve_id_ECC_SECP384R1, /* ECC Curve Id */ - ); - check_if_zero(ret).unwrap(); - - // This function returns the size of the digest (output) for a hash_type. - // The returns size is used to make sure the output buffer - // provided to wc_Hash is large enough. - let digest_sz = wc_HashGetDigestSize(wc_HashType_WC_HASH_TYPE_SHA384); - - // This function performs a hash on the provided data buffer and - // returns it in the hash buffer provided. - // In this case we hash with Sha384. - // We hash the message since it's not hashed. - ret = wc_Hash( - wc_HashType_WC_HASH_TYPE_SHA384, - message.as_ptr(), - message.len() as word32, - digest.as_mut_ptr(), - digest_sz as word32, - ); - check_if_zero(ret).unwrap(); - - ret = wc_ecc_verify_hash( - signature.as_ptr(), - signature.len() as word32, - digest.as_ptr(), - digest_sz as word32, - &mut stat, - ecc_key_object.as_ptr(), - ); - if stat != 1 { - panic!("ret = {}, stat = {}", ret, stat); - } - - if let Err(WCError::Failure) = check_if_one(stat) { - Err(InvalidSignature) - } else { - Ok(()) - } - } - } -} - -#[derive(Debug)] -pub struct EcdsaNistp521Sha512; - -impl SignatureVerificationAlgorithm for EcdsaNistp521Sha512 { - fn public_key_alg_id(&self) -> AlgorithmIdentifier { - alg_id::ECDSA_P521 - } - - fn signature_alg_id(&self) -> AlgorithmIdentifier { - alg_id::ECDSA_SHA512 - } - - fn verify_signature( - &self, - public_key: &[u8], - message: &[u8], - signature: &[u8], - ) -> Result<(), InvalidSignature> { - unsafe { - let mut ecc_c_type: ecc_key = mem::zeroed(); - let ecc_key_object = ECCKeyObject::from_ptr(&mut ecc_c_type); - let mut digest: [u8; WC_SHA512_DIGEST_SIZE as usize] = - [0; WC_SHA512_DIGEST_SIZE as usize]; let mut ret; let mut stat: i32 = 0; - ecc_key_object.init(); + // Determine curve, how many bytes to skip from public_key, and which hash to use + let (curve_id, skip_len, wc_hash_type) = match self.scheme { + SignatureScheme::ECDSA_NISTP256_SHA256 => ( + ecc_curve_id_ECC_SECP256R1, + 32, + wc_HashType_WC_HASH_TYPE_SHA256, + ), + SignatureScheme::ECDSA_NISTP384_SHA384 => ( + ecc_curve_id_ECC_SECP384R1, + 48, + wc_HashType_WC_HASH_TYPE_SHA384, + ), + SignatureScheme::ECDSA_NISTP521_SHA512 => ( + ecc_curve_id_ECC_SECP521R1, + 66, + wc_HashType_WC_HASH_TYPE_SHA512, + ), + _ => return Err(InvalidSignature), + }; /* * Skipping first byte because rustls uses this format: - * https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.2. - * */ + * https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.2 + * + * For P-256, skip_len=32 means public_key[1..33] is "x" and [33..65] is "y". + * For P-384, skip_len=48 means public_key[1..49] is "x" and [49..97] is "y". + * For P-521, skip_len=66 means public_key[1..67] is "x" and [67..133] is "y". + */ ret = wc_ecc_import_unsigned( ecc_key_object.as_ptr(), - public_key[1..67].as_ptr(), /* Public "x" Coordinate */ - public_key[67..].as_ptr(), /* Public "y" Coordinate */ - ptr::null_mut(), /* Private "d" (optional) */ - ecc_curve_id_ECC_SECP521R1, /* ECC Curve Id */ + public_key[1..(1 + skip_len)].as_ptr(), // Public "x" coordinate + public_key[(1 + skip_len)..].as_ptr(), // Public "y" coordinate + ptr::null_mut(), // Private "d" (optional) + curve_id, ); check_if_zero(ret).unwrap(); // This function returns the size of the digest (output) for a hash_type. - // The returns size is used to make sure the output buffer - // provided to wc_Hash is large enough. - let digest_sz = wc_HashGetDigestSize(wc_HashType_WC_HASH_TYPE_SHA512); + // The returned size is used to make sure the output buffer is large enough. + let digest_sz = wc_HashGetDigestSize(wc_hash_type); // This function performs a hash on the provided data buffer and // returns it in the hash buffer provided. - // In this case we hash with Sha512. - // We hash the message since it's not hashed. + // We hash the message since it's not pre-hashed. + let mut digest = vec![0u8; digest_sz as usize]; ret = wc_Hash( - wc_HashType_WC_HASH_TYPE_SHA512, + wc_hash_type, message.as_ptr(), message.len() as word32, digest.as_mut_ptr(), @@ -228,6 +123,7 @@ impl SignatureVerificationAlgorithm for EcdsaNistp521Sha512 { ); check_if_zero(ret).unwrap(); + // Finally, verify the signature against the digest ret = wc_ecc_verify_hash( signature.as_ptr(), signature.len() as word32, @@ -236,6 +132,8 @@ impl SignatureVerificationAlgorithm for EcdsaNistp521Sha512 { &mut stat, ecc_key_object.as_ptr(), ); + + // If stat != 1, signature is invalid if stat != 1 { panic!("ret = {}, stat = {}", ret, stat); } diff --git a/rustls-wolfcrypt-provider/tests/e2e.rs b/rustls-wolfcrypt-provider/tests/e2e.rs index 19b3c38..430beee 100644 --- a/rustls-wolfcrypt-provider/tests/e2e.rs +++ b/rustls-wolfcrypt-provider/tests/e2e.rs @@ -83,7 +83,9 @@ fn start_wolfssl_server(current_dir_string: String, tls_version: &str) -> Child #[cfg(test)] mod tests { use rustls::crypto::CryptoProvider; - use rustls_pki_types::{PrivateKeyDer, PrivatePkcs1KeyDer, PrivatePkcs8KeyDer}; + use rustls_pki_types::{ + PrivateKeyDer, PrivatePkcs1KeyDer, PrivatePkcs8KeyDer, PrivateSec1KeyDer, + }; use super::*; @@ -478,14 +480,28 @@ mod tests { }; check_if_greater_than_zero(ret).unwrap(); - der_ecc_key.resize(ret as usize, 0); // Trim to actual size - let rustls_pkcs8_der = PrivatePkcs8KeyDer::from(der_ecc_key.as_slice()); - let rustls_private_key = PrivateKeyDer::from(rustls_pkcs8_der); + // Trim to actual key size + der_ecc_key.resize(ret as usize, 0); + + // Convert to PKCS#8 format and verify + let rustls_private_key_pkcs8 = + PrivateKeyDer::from(PrivatePkcs8KeyDer::from(der_ecc_key.as_slice())); sign_and_verify( &wolfcrypt_default_provider, scheme, - rustls_private_key.clone_key(), + rustls_private_key_pkcs8.clone_key(), + pub_key_bytes.as_slice(), + ); + + // Convert to SEC1 format and verify + let rustls_private_key_sec1 = + PrivateKeyDer::from(PrivateSec1KeyDer::from(der_ecc_key.as_slice())); + + sign_and_verify( + &wolfcrypt_default_provider, + scheme, + rustls_private_key_sec1.clone_key(), pub_key_bytes.as_slice(), ); } @@ -575,13 +591,19 @@ mod tests { .collect(); test_cases.par_iter().for_each(|&(scheme, key_size)| { - generate_and_test_pss_key(&wolfcrypt_default_provider, scheme, key_size).expect( + generate_and_test_rsa_pkcs8_key(&wolfcrypt_default_provider, scheme, key_size).expect( + &format!("Failed for scheme {:?} with key size {}", scheme, key_size), + ); + }); + + test_cases.par_iter().for_each(|&(scheme, key_size)| { + generate_and_test_rsa_pkcs1_key(&wolfcrypt_default_provider, scheme, key_size).expect( &format!("Failed for scheme {:?} with key size {}", scheme, key_size), ); }); } - fn generate_and_test_pss_key( + fn generate_and_test_rsa_pkcs8_key( provider: &CryptoProvider, scheme: SignatureScheme, key_size: usize, @@ -658,13 +680,19 @@ mod tests { .collect(); test_cases.par_iter().for_each(|&(scheme, key_size)| { - generate_and_test_pkcs1_key(&wolfcrypt_default_provider, scheme, key_size).expect( + generate_and_test_rsa_pkcs1_key(&wolfcrypt_default_provider, scheme, key_size).expect( + &format!("Failed for scheme {:?} with key size {}", scheme, key_size), + ); + }); + + test_cases.par_iter().for_each(|&(scheme, key_size)| { + generate_and_test_rsa_pkcs8_key(&wolfcrypt_default_provider, scheme, key_size).expect( &format!("Failed for scheme {:?} with key size {}", scheme, key_size), ); }); } - fn generate_and_test_pkcs1_key( + fn generate_and_test_rsa_pkcs1_key( provider: &CryptoProvider, scheme: SignatureScheme, key_size: usize,