diff --git a/rustls-wolfcrypt-provider/src/error.rs b/rustls-wolfcrypt-provider/src/error.rs index f2b262c..94baaf6 100644 --- a/rustls-wolfcrypt-provider/src/error.rs +++ b/rustls-wolfcrypt-provider/src/error.rs @@ -1,28 +1,79 @@ -/// Custom error type for cryptographic operations, only indicating failure. -/// Errors are generalized to avoid the leaking of information. +use core::fmt; + +/// Custom error type for cryptographic operations. +/// Groups common wolfCrypt error types into categories. #[derive(Debug)] pub enum WCError { + /// Operation completed successfully (ret = 0) + Success, + /// Generic failure (-1) Failure, + /// Memory-related errors (MEMORY_E, MP_MEM, etc.) + Memory, + /// Invalid arguments or state (BAD_FUNC_ARG, BAD_STATE_E, etc.) + InvalidArgument, + /// Buffer-related errors (BUFFER_E, RSA_BUFFER_E, etc.) + Buffer, + /// Authentication failures (MAC_CMP_FAILED_E, AES_GCM_AUTH_E, etc.) + Authentication, + /// Random number generation errors (RNG_FAILURE_E, MISSING_RNG_E, etc.) + RandomError, + /// ASN parsing errors (ASN_PARSE_E and related) + ASNParse, + /// Key-related errors (RSA_KEY_PAIR_E, ECC_PRIV_KEY_E, etc.) + KeyError, + /// Feature not available (NOT_COMPILED_IN, CRYPTOCB_UNAVAILABLE, etc.) + NotAvailable, +} + +impl fmt::Display for WCError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + WCError::Success => write!(f, "Operation successful"), + WCError::Failure => write!(f, "Operation failed"), + WCError::Memory => write!(f, "Memory allocation error"), + WCError::InvalidArgument => write!(f, "Invalid argument or state"), + WCError::Buffer => write!(f, "Buffer error"), + WCError::Authentication => write!(f, "Authentication failed"), + WCError::RandomError => write!(f, "Random number generation error"), + WCError::ASNParse => write!(f, "ASN parsing error"), + WCError::KeyError => write!(f, "Key-related error"), + WCError::NotAvailable => write!(f, "Feature not available"), + } + } } /// A result type for cryptographic operations. pub type WCResult = Result<(), WCError>; +/// Internal function to map wolfCrypt error codes to WCError variants +fn check_error(ret: i32) -> WCResult { + match ret { + 0 => Ok(()), + -1 => Err(WCError::Failure), + -125 => Err(WCError::Memory), + -173 => Err(WCError::InvalidArgument), + -132 => Err(WCError::Buffer), + -181..=-180 | -213 => Err(WCError::Authentication), + -199 | -236 => Err(WCError::RandomError), + -162..=-140 => Err(WCError::ASNParse), + -262 | -216 => Err(WCError::KeyError), + -174 | -271 => Err(WCError::NotAvailable), + _ => Err(WCError::Failure), + } +} + /// Checks if the FFI return value is `0` (indicating success). /// Returns `Ok(())` if success, otherwise returns `Err(CryptoError::Failure)` /// if the value is not `0`. /// /// # Arguments -/// * `stat` - The return value from the FFI call (i32). +/// * `ret` - The return value from the FFI call (i32). /// /// # Returns /// `CryptoResult` indicating either success or failure. pub fn check_if_zero(ret: i32) -> WCResult { - if ret == 0 { - Ok(()) - } else { - Err(WCError::Failure) - } + check_error(ret) } /// Checks if the FFI return value is `1` (indicating success). @@ -35,10 +86,10 @@ pub fn check_if_zero(ret: i32) -> WCResult { /// # Returns /// `CryptoResult` indicating either success or failure. pub fn check_if_one(stat: i32) -> WCResult { - if stat == 1 { - Ok(()) - } else { - Err(WCError::Failure) + match stat { + 1 => Ok(()), + 0 => Err(WCError::Failure), + _ => check_error(stat), } } @@ -52,9 +103,72 @@ pub fn check_if_one(stat: i32) -> WCResult { /// # Returns /// `CryptoResult` indicating either success or failure. pub fn check_if_greater_than_zero(ret: i32) -> WCResult { - if ret > 0 { - Ok(()) - } else { - Err(WCError::Failure) + match ret { + x if x > 0 => Ok(()), + 0 => Err(WCError::Failure), + _ => check_error(ret), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_display() { + assert_eq!(WCError::Success.to_string(), "Operation successful"); + assert_eq!(WCError::Failure.to_string(), "Operation failed"); + assert_eq!(WCError::Memory.to_string(), "Memory allocation error"); + assert_eq!( + WCError::InvalidArgument.to_string(), + "Invalid argument or state" + ); + assert_eq!(WCError::Buffer.to_string(), "Buffer error"); + assert_eq!(WCError::Authentication.to_string(), "Authentication failed"); + assert_eq!( + WCError::RandomError.to_string(), + "Random number generation error" + ); + assert_eq!(WCError::ASNParse.to_string(), "ASN parsing error"); + assert_eq!(WCError::KeyError.to_string(), "Key-related error"); + assert_eq!(WCError::NotAvailable.to_string(), "Feature not available"); + } + + #[test] + fn test_check_error() { + assert!(check_error(0).is_ok()); + assert!(matches!(check_error(-1), Err(WCError::Failure))); + assert!(matches!(check_error(-125), Err(WCError::Memory))); + assert!(matches!(check_error(-173), Err(WCError::InvalidArgument))); + assert!(matches!(check_error(-132), Err(WCError::Buffer))); + assert!(matches!(check_error(-180), Err(WCError::Authentication))); + assert!(matches!(check_error(-199), Err(WCError::RandomError))); + assert!(matches!(check_error(-140), Err(WCError::ASNParse))); + assert!(matches!(check_error(-262), Err(WCError::KeyError))); + assert!(matches!(check_error(-174), Err(WCError::NotAvailable))); + } + + #[test] + fn test_check_if_zero() { + assert!(check_if_zero(0).is_ok()); + assert!(matches!(check_if_zero(-1), Err(WCError::Failure))); + assert!(matches!(check_if_zero(-125), Err(WCError::Memory))); + } + + #[test] + fn test_check_if_one() { + assert!(check_if_one(1).is_ok()); + assert!(check_if_one(0).is_err()); + assert!(matches!(check_if_one(-1), Err(WCError::Failure))); + } + + #[test] + fn test_check_if_greater_than_zero() { + assert!(check_if_greater_than_zero(1).is_ok()); + assert!(check_if_greater_than_zero(0).is_err()); + assert!(matches!( + check_if_greater_than_zero(-1), + Err(WCError::Failure) + )); } } diff --git a/rustls-wolfcrypt-provider/src/kx/sec256r1.rs b/rustls-wolfcrypt-provider/src/kx/sec256r1.rs index 3376137..9de7a8b 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec256r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec256r1.rs @@ -1,21 +1,19 @@ use crate::{error::check_if_zero, types::*}; use alloc::boxed::Box; -use alloc::vec; -use alloc::vec::Vec; use core::mem; use core::ptr; use foreign_types::ForeignType; use wolfcrypt_rs::*; pub struct KeyExchangeSecP256r1 { - priv_key_bytes: Vec, - pub_key_bytes: Vec, + priv_key_bytes: Box<[u8]>, + pub_key_bytes: Box<[u8]>, } pub struct ECCPubKey { - qx: Vec, + qx: [u8; 32], qx_len: word32, - qy: Vec, + qy: [u8; 32], qy_len: word32, } @@ -27,21 +25,18 @@ impl KeyExchangeSecP256r1 { let rng_object: WCRngObject = WCRngObject::new(&mut rng); let mut ret; let mut pub_key_raw = ECCPubKey { - qx: [0; 32].to_vec(), + qx: [0; 32], qx_len: 32, - qy: [0; 32].to_vec(), + qy: [0; 32], qy_len: 32, }; - // We initiliaze the ecc key object. key_object.init(); - - // We initiliaze the rng object. rng_object.init(); let key_size = unsafe { wc_ecc_get_curve_size_from_id(ecc_curve_id_ECC_SECP256R1) }; - let mut priv_key_raw: Vec = vec![0; key_size as usize]; + let mut priv_key_raw = [0u8; 32]; let mut priv_key_raw_len: word32 = priv_key_raw.len() as word32; ret = unsafe { @@ -73,33 +68,31 @@ impl KeyExchangeSecP256r1 { ) }; check_if_zero(ret).unwrap(); + // One byte prefix (0x04) + 32 bytes X coord + 32 bytes Y coord + let mut pub_key_bytes = [0x04; 65]; - let mut pub_key_bytes = Vec::new(); + // Copy X coordinate into bytes 1-32 + pub_key_bytes[1..33].copy_from_slice(&pub_key_raw.qx); - pub_key_bytes.push(0x04); - pub_key_bytes.extend(pub_key_raw.qx.clone()); - pub_key_bytes.extend(pub_key_raw.qy.clone()); - pub_key_bytes.as_slice(); + // Copy Y coordinate into bytes 33-64 + pub_key_bytes[33..65].copy_from_slice(&pub_key_raw.qy); KeyExchangeSecP256r1 { - priv_key_bytes: priv_key_raw.to_vec(), - pub_key_bytes: pub_key_bytes.to_vec(), + priv_key_bytes: Box::new(priv_key_raw), + pub_key_bytes: Box::new(pub_key_bytes), } } - pub fn derive_shared_secret(&self, peer_pub_key: Vec) -> Vec { + pub fn derive_shared_secret(&self, peer_pub_key: &[u8]) -> Box<[u8]> { let mut priv_key: ecc_key = unsafe { mem::zeroed() }; let priv_key_object = ECCKeyObject::new(&mut priv_key); let mut pub_key: ecc_key = unsafe { mem::zeroed() }; let pub_key_object = ECCKeyObject::new(&mut pub_key); - let mut ret; let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object = WCRngObject::new(&mut rng); + let mut ret: i32; - // We initialize the private key before we import it. priv_key_object.init(); - - // We initiliaze the public key before we import it. pub_key_object.init(); ret = unsafe { @@ -114,10 +107,6 @@ impl KeyExchangeSecP256r1 { }; check_if_zero(ret).unwrap(); - /* - * Skipping first byte because rustls uses this format: - * https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.2. - * */ ret = unsafe { wc_ecc_import_unsigned( pub_key_object.as_ptr(), @@ -129,7 +118,6 @@ impl KeyExchangeSecP256r1 { }; check_if_zero(ret).unwrap(); - // We initialize the rng object. rng_object.init(); ret = unsafe { wc_ecc_set_rng(pub_key_object.as_ptr(), rng_object.as_ptr()) }; @@ -138,9 +126,7 @@ impl KeyExchangeSecP256r1 { ret = unsafe { wc_ecc_set_rng(priv_key_object.as_ptr(), rng_object.as_ptr()) }; check_if_zero(ret).unwrap(); - let key_size = unsafe { wc_ecc_get_curve_size_from_id(ecc_curve_id_ECC_SECP256R1) }; - - let mut out: Vec = vec![0; key_size as usize]; + let mut out = [0u8; 32]; let mut out_len: word32 = out.len() as word32; ret = unsafe { @@ -153,7 +139,7 @@ impl KeyExchangeSecP256r1 { }; check_if_zero(ret).unwrap(); - out.to_vec() + Box::new(out) } } @@ -162,15 +148,12 @@ impl rustls::crypto::ActiveKeyExchange for KeyExchangeSecP256r1 { self: Box, peer_pub_key: &[u8], ) -> Result { - // We derive the shared secret with our private key and - // the received public key. - let secret = self.derive_shared_secret(peer_pub_key.to_vec()); - - Ok(rustls::crypto::SharedSecret::from(secret.as_slice())) + let secret = self.derive_shared_secret(peer_pub_key); + Ok(rustls::crypto::SharedSecret::from(&*secret)) } fn pub_key(&self) -> &[u8] { - self.pub_key_bytes.as_slice() + &self.pub_key_bytes } fn group(&self) -> rustls::NamedGroup { @@ -189,8 +172,8 @@ mod tests { let bob = Box::new(KeyExchangeSecP256r1::use_secp256r1()); assert_eq!( - alice.derive_shared_secret(bob.pub_key().try_into().unwrap()), - bob.derive_shared_secret(alice.pub_key().try_into().unwrap()), + alice.derive_shared_secret(bob.pub_key()), + bob.derive_shared_secret(alice.pub_key()), ) } } diff --git a/rustls-wolfcrypt-provider/src/kx/sec384r1.rs b/rustls-wolfcrypt-provider/src/kx/sec384r1.rs index 230319b..4625aa0 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec384r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec384r1.rs @@ -1,22 +1,20 @@ use crate::error::*; use crate::types::*; use alloc::boxed::Box; -use alloc::vec; -use alloc::vec::Vec; use core::mem; use core::ptr; use foreign_types::ForeignType; use wolfcrypt_rs::*; pub struct KeyExchangeSecP384r1 { - priv_key_bytes: Vec, - pub_key_bytes: Vec, + priv_key_bytes: Box<[u8]>, + pub_key_bytes: Box<[u8]>, } pub struct ECCPubKey { - qx: Vec, + qx: [u8; 48], qx_len: word32, - qy: Vec, + qy: [u8; 48], qy_len: word32, } @@ -28,9 +26,9 @@ impl KeyExchangeSecP384r1 { let rng_object: WCRngObject = WCRngObject::new(&mut rng); let mut ret; let mut pub_key_raw = ECCPubKey { - qx: [0; 48].to_vec(), + qx: [0; 48], qx_len: 48, - qy: [0; 48].to_vec(), + qy: [0; 48], qy_len: 48, }; @@ -50,7 +48,7 @@ impl KeyExchangeSecP384r1 { }; check_if_zero(ret).unwrap(); - let mut priv_key_raw: Vec = vec![0; key_size as usize]; + let mut priv_key_raw = [0u8; 48]; let mut priv_key_raw_len: word32 = priv_key_raw.len() as word32; ret = unsafe { @@ -73,20 +71,22 @@ impl KeyExchangeSecP384r1 { }; check_if_zero(ret).unwrap(); - let mut pub_key_bytes = Vec::new(); + // One byte prefix + 48 bytes X + 48 bytes Y + let mut pub_key_bytes = [0x04; 97]; - pub_key_bytes.push(0x04); - pub_key_bytes.extend(pub_key_raw.qx.clone()); - pub_key_bytes.extend(pub_key_raw.qy.clone()); - pub_key_bytes.as_slice(); + // Copy X coordinate into bytes 1-49 + pub_key_bytes[1..49].copy_from_slice(&pub_key_raw.qx); + + // Copy Y coordinate into bytes 49-97 + pub_key_bytes[49..97].copy_from_slice(&pub_key_raw.qy); KeyExchangeSecP384r1 { - priv_key_bytes: priv_key_raw.to_vec(), - pub_key_bytes: pub_key_bytes.to_vec(), + priv_key_bytes: Box::new(priv_key_raw), + pub_key_bytes: Box::new(pub_key_bytes), } } - pub fn derive_shared_secret(&self, peer_pub_key: Vec) -> Vec { + pub fn derive_shared_secret(&self, peer_pub_key: &[u8]) -> Box<[u8]> { let mut priv_key: ecc_key = unsafe { mem::zeroed() }; let priv_key_object: ECCKeyObject = ECCKeyObject::new(&mut priv_key); let mut pub_key: ecc_key = unsafe { mem::zeroed() }; @@ -133,9 +133,7 @@ impl KeyExchangeSecP384r1 { ret = unsafe { wc_ecc_set_rng(priv_key_object.as_ptr(), rng_object.as_ptr()) }; check_if_zero(ret).unwrap(); - let key_size = unsafe { wc_ecc_get_curve_size_from_id(ecc_curve_id_ECC_SECP384R1) }; - - let mut out: Vec = vec![0; key_size as usize]; + let mut out = [0u8; 48]; let mut out_len: word32 = out.len() as word32; ret = unsafe { @@ -148,7 +146,7 @@ impl KeyExchangeSecP384r1 { }; check_if_zero(ret).unwrap(); - out.to_vec() + Box::new(out) } } @@ -159,13 +157,13 @@ impl rustls::crypto::ActiveKeyExchange for KeyExchangeSecP384r1 { ) -> Result { // We derive the shared secret with our private key and // the received public key. - let secret = self.derive_shared_secret(peer_pub_key.to_vec()); + let secret = self.derive_shared_secret(peer_pub_key); - Ok(rustls::crypto::SharedSecret::from(secret.as_slice())) + Ok(rustls::crypto::SharedSecret::from(&*secret)) } fn pub_key(&self) -> &[u8] { - self.pub_key_bytes.as_slice() + &self.pub_key_bytes } fn group(&self) -> rustls::NamedGroup { diff --git a/rustls-wolfcrypt-provider/src/kx/sec521r1.rs b/rustls-wolfcrypt-provider/src/kx/sec521r1.rs index 4258ff9..7854cc5 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec521r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec521r1.rs @@ -1,21 +1,19 @@ use crate::{error::check_if_zero, types::*}; use alloc::boxed::Box; -use alloc::vec; -use alloc::vec::Vec; use core::mem; use core::ptr; use foreign_types::ForeignType; use wolfcrypt_rs::*; pub struct KeyExchangeSecP521r1 { - priv_key_bytes: Vec, - pub_key_bytes: Vec, + priv_key_bytes: Box<[u8]>, + pub_key_bytes: Box<[u8]>, } pub struct ECCPubKey { - qx: Vec, + qx: [u8; 66], qx_len: word32, - qy: Vec, + qy: [u8; 66], qy_len: word32, } @@ -27,9 +25,9 @@ impl KeyExchangeSecP521r1 { let rng_object: WCRngObject = WCRngObject::new(&mut rng); let mut ret; let mut pub_key_raw = ECCPubKey { - qx: [0; 66].to_vec(), + qx: [0; 66], qx_len: 66, - qy: [0; 66].to_vec(), + qy: [0; 66], qy_len: 66, }; @@ -51,7 +49,7 @@ impl KeyExchangeSecP521r1 { }; check_if_zero(ret).unwrap(); - let mut priv_key_raw: Vec = vec![0; key_size as usize]; + let mut priv_key_raw = [0u8; 66]; let mut priv_key_raw_len: word32 = priv_key_raw.len() as word32; ret = unsafe { @@ -74,20 +72,21 @@ impl KeyExchangeSecP521r1 { }; check_if_zero(ret).unwrap(); - let mut pub_key_bytes = Vec::new(); + let mut pub_key_bytes = [0x04; 133]; // One byte prefix + 66 bytes X + 66 bytes Y - pub_key_bytes.push(0x04); - pub_key_bytes.extend(pub_key_raw.qx.clone()); - pub_key_bytes.extend(pub_key_raw.qy.clone()); - pub_key_bytes.as_slice(); + // Copy X coordinate into bytes 1-66 + pub_key_bytes[1..67].copy_from_slice(&pub_key_raw.qx); + + // Copy Y coordinate into bytes 67-133 + pub_key_bytes[67..133].copy_from_slice(&pub_key_raw.qy); KeyExchangeSecP521r1 { - priv_key_bytes: priv_key_raw.to_vec(), - pub_key_bytes: pub_key_bytes.to_vec(), + priv_key_bytes: Box::new(priv_key_raw), + pub_key_bytes: Box::new(pub_key_bytes), } } - pub fn derive_shared_secret(&self, peer_pub_key: Vec) -> Vec { + pub fn derive_shared_secret(&self, peer_pub_key: &[u8]) -> Box<[u8]> { let mut priv_key: ecc_key = unsafe { mem::zeroed() }; let priv_key_object: ECCKeyObject = ECCKeyObject::new(&mut priv_key); let mut pub_key: ecc_key = unsafe { mem::zeroed() }; @@ -135,9 +134,7 @@ impl KeyExchangeSecP521r1 { ret = unsafe { wc_ecc_set_rng(priv_key_object.as_ptr(), rng_object.as_ptr()) }; check_if_zero(ret).unwrap(); - let key_size = unsafe { wc_ecc_get_curve_size_from_id(ecc_curve_id_ECC_SECP521R1) }; - - let mut out: Vec = vec![0; key_size as usize]; + let mut out = [0u8; 66]; let mut out_len: word32 = out.len() as word32; ret = unsafe { @@ -150,7 +147,7 @@ impl KeyExchangeSecP521r1 { }; check_if_zero(ret).unwrap(); - out.to_vec() + Box::new(out) } } @@ -161,13 +158,13 @@ impl rustls::crypto::ActiveKeyExchange for KeyExchangeSecP521r1 { ) -> Result { // We derive the shared secret with our private key and // the received public key. - let secret = self.derive_shared_secret(peer_pub_key.to_vec()); + let secret = self.derive_shared_secret(peer_pub_key); - Ok(rustls::crypto::SharedSecret::from(secret.as_slice())) + Ok(rustls::crypto::SharedSecret::from(&*secret)) } fn pub_key(&self) -> &[u8] { - self.pub_key_bytes.as_slice() + &self.pub_key_bytes } fn group(&self) -> rustls::NamedGroup { diff --git a/rustls-wolfcrypt-provider/src/kx/x25519.rs b/rustls-wolfcrypt-provider/src/kx/x25519.rs index 919a45c..8a7c363 100644 --- a/rustls-wolfcrypt-provider/src/kx/x25519.rs +++ b/rustls-wolfcrypt-provider/src/kx/x25519.rs @@ -1,13 +1,12 @@ use crate::{error::check_if_zero, types::*}; use alloc::boxed::Box; -use alloc::vec::Vec; use core::mem; use foreign_types::ForeignType; use wolfcrypt_rs::*; pub struct KeyExchangeX25519 { - pub_key_bytes: Vec, - priv_key_bytes: Vec, + pub_key_bytes: Box<[u8]>, + priv_key_bytes: Box<[u8]>, } impl KeyExchangeX25519 { @@ -48,12 +47,12 @@ impl KeyExchangeX25519 { check_if_zero(ret).unwrap(); KeyExchangeX25519 { - pub_key_bytes: pub_key_raw.to_vec(), - priv_key_bytes: priv_key_raw.to_vec(), + pub_key_bytes: Box::new(pub_key_raw), + priv_key_bytes: Box::new(priv_key_raw), } } - pub fn derive_shared_secret(&self, peer_pub_key: Vec) -> Vec { + pub fn derive_shared_secret(&self, peer_pub_key: &[u8]) -> Box<[u8]> { let mut ret; let endian: u32 = EC25519_LITTLE_ENDIAN; let mut pub_key_provided: curve25519_key = unsafe { mem::zeroed() }; @@ -113,7 +112,7 @@ impl KeyExchangeX25519 { }; check_if_zero(ret).unwrap(); - out.to_vec() + Box::new(out) } } @@ -124,9 +123,9 @@ impl rustls::crypto::ActiveKeyExchange for KeyExchangeX25519 { ) -> Result { // We derive the shared secret with our private key and // the received public key. - let secret = self.derive_shared_secret(peer_pub_key.to_vec()); + let secret = self.derive_shared_secret(peer_pub_key); - Ok(rustls::crypto::SharedSecret::from(secret.as_slice())) + Ok(rustls::crypto::SharedSecret::from(&*secret)) } fn pub_key(&self) -> &[u8] { diff --git a/rustls-wolfcrypt-provider/src/types/mod.rs b/rustls-wolfcrypt-provider/src/types/mod.rs index 5efe513..ac7a272 100644 --- a/rustls-wolfcrypt-provider/src/types/mod.rs +++ b/rustls-wolfcrypt-provider/src/types/mod.rs @@ -1,6 +1,7 @@ use crate::error::*; use core::ptr::NonNull; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; +use log::error; use wolfcrypt_rs::*; @@ -29,8 +30,8 @@ macro_rules! define_foreign_type { } impl $struct_name { - // Given a $c_type (FFI C binding), it creates an object around it - // using the ForeignType's function from_ptr function. + /// Given a $c_type (FFI C binding), it creates an object around it + /// using the ForeignType's function from_ptr function. pub fn new(c_type: &mut $c_type) -> $struct_name { unsafe { let new_object: $struct_name = $struct_name::from_ptr(c_type); @@ -38,25 +39,32 @@ macro_rules! define_foreign_type { } } - // Given an $init_function, it calls it with the object's ptr as argument. + /// Given an $init_function, it calls it with the object's ptr as argument. pub fn init(&self) { unsafe { check_if_zero($init_function(self.as_ptr())).unwrap() } } } }; - // For types that also need Drop implementations ($struct_name:ident, $ref_name:ident, $c_type:ty, drop($drop_fn:ident), $init_function:ident) => { define_foreign_type!($struct_name, $ref_name, $c_type, $init_function); + /// Implements Drop trait for cryptographic types that require cleanup. + /// This safely frees memory and other resources when the type goes out of scope. + /// Any cleanup errors are logged but cannot be returned since this is Drop. + /// The unsafe block is needed for FFI calls to the underlying C functions. impl Drop for $struct_name { fn drop(&mut self) { let ret = unsafe { $drop_fn(self.as_ptr()) }; - if ret != 0 { - panic!( - "Error while freeing resource in Drop for {}", - stringify!($struct_name) - ); + match check_if_zero(ret) { + Err(err) => { + error!( + "Error while freeing resource in Drop for {}: {}", + stringify!($struct_name), + err + ); + } + Ok(()) => {} } } } @@ -88,15 +96,27 @@ macro_rules! define_foreign_type_with_copy { } }; - // For types that also need Drop implementations ($struct_name:ident, $ref_name:ident, $c_type:ty, drop($drop_fn:ident)) => { define_foreign_type_with_copy!($struct_name, $ref_name, $c_type); + /// Implements Drop trait for cryptographic types that require cleanup. + /// This safely frees memory and other resources when the type goes out of scope. + /// Any cleanup errors are logged but cannot be returned since this is Drop. + /// The unsafe block is needed for FFI calls to the underlying C functions. impl Drop for $struct_name { fn drop(&mut self) { unsafe { let ret = $drop_fn(self.as_ptr()); - check_if_zero(ret).unwrap() + match check_if_zero(ret) { + Err(err) => { + error!( + "Error while freeing resource in Drop for {}: {}", + stringify!($struct_name), + err + ); + } + Ok(()) => {} + } } } } diff --git a/wolfcrypt-rs/build.rs b/wolfcrypt-rs/build.rs index cbb7a65..5443c72 100644 --- a/wolfcrypt-rs/build.rs +++ b/wolfcrypt-rs/build.rs @@ -2,19 +2,54 @@ extern crate bindgen; use std::env; use std::fs; +use std::io::{self, Result}; use std::path::Path; use std::path::PathBuf; use std::process::Command; +/// Version-related constants for WolfSSL +const WOLFSSL_DIR: &str = "wolfssl-5.7.6-stable"; +const WOLFSSL_ZIP: &str = "wolfssl-5.7.6-stable.zip"; +const WOLFSSL_URL: &str = "https://github.com/wolfSSL/wolfssl/archive/refs/tags/v5.7.6-stable.zip"; + +/// Entry point for the build script. +/// Handles the main build process and exits with an error code if anything fails. fn main() { - // We check if the release was already fetched, if not, - // we fetch it and setup it. - if fs::metadata("wolfssl-5.7.6-stable").is_err() { - setup_wolfssl(); + if let Err(e) = run_build() { + eprintln!("Build failed: {}", e); + std::process::exit(1); } +} + +/// Orchestrates the entire build process. +/// +/// This function: +/// 1. Checks if WolfSSL needs to be set up +/// 2. Sets up WolfSSL if necessary +/// 3. Generates Rust bindings for the WolfSSL library +/// +/// Returns `Ok(())` if successful, or an error if any step fails. +fn run_build() -> Result<()> { + if fs::metadata(WOLFSSL_DIR).is_err() { + setup_wolfssl()?; + } + + generate_bindings()?; + Ok(()) +} - let wolfssl_lib_dir = Path::new(&"/opt/wolfssl-rs/lib/"); - let wolfssl_include_dir = Path::new(&"/opt/wolfssl-rs/include/"); +/// Generates Rust bindings for the WolfSSL library using bindgen. +/// +/// This function: +/// 1. Sets up the library and include paths +/// 2. Configures the build environment +/// 3. Generates Rust bindings using bindgen +/// 4. Writes the bindings to a file +/// +/// Returns `Ok(())` if successful, or an error if binding generation fails. +fn generate_bindings() -> Result<()> { + let wolfssl_lib_dir = Path::new("/opt/wolfssl-rs/lib/"); + let wolfssl_include_dir = Path::new("/opt/wolfssl-rs/include/"); println!( "cargo:rustc-link-search={}", @@ -27,130 +62,163 @@ fn main() { .clang_arg(format!("-I{}/", wolfssl_include_dir.to_str().unwrap())) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() - .expect("Unable to generate bindings"); + .map_err(|_| io::Error::new(io::ErrorKind::Other, "Failed to generate bindings"))?; let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); + .map_err(|e| { + io::Error::new( + io::ErrorKind::Other, + format!("Couldn't write bindings: {}", e), + ) + }) +} + +/// Coordinates the complete setup process for WolfSSL. +/// +/// This function executes all necessary steps in sequence: +/// 1. Downloads the WolfSSL source +/// 2. Extracts the archive +/// 3. Removes the downloaded archive +/// 4. Builds WolfSSL from source +/// 5. Returns to the original directory +/// +/// Returns `Ok(())` if all steps complete successfully, or an error if any step fails. +fn setup_wolfssl() -> Result<()> { + download_wolfssl()?; + unzip_wolfssl()?; + remove_zip()?; + build_wolfssl()?; + change_back_to_root()?; + Ok(()) } -fn setup_wolfssl() { - // Step 1: Download the ZIP file using curl +/// Downloads the WolfSSL source code archive using curl. +/// +/// Uses curl to download the specified version of WolfSSL from the official repository. +/// The download URL and filename are defined in the constants. +/// +/// Returns `Ok(())` if the download succeeds, or an error if the download fails. +fn download_wolfssl() -> Result<()> { let output = Command::new("curl") .arg("-L") .arg("-o") - .arg("wolfssl-5.7.6-stable.zip") - .arg("https://github.com/wolfSSL/wolfssl/archive/refs/tags/v5.7.6-stable.zip") - .output() - .expect("Failed to execute curl command"); - - if output.status.success() { - println!("Download completed successfully."); - - // Step 2: Unzip the downloaded file - let output = Command::new("unzip") - .arg("wolfssl-5.7.6-stable.zip") - .output() - .expect("Failed to execute unzip command"); - - if output.status.success() { - println!("Unzipping completed successfully."); - - // Step 3: Remove the ZIP file - if let Err(e) = fs::remove_file("wolfssl-5.7.6-stable.zip") { - eprintln!("Error removing ZIP file: {}", e); - } else { - println!("Removed ZIP file successfully."); - } - - // Step 4: Change the current working directory to the unzipped folder - if let Err(e) = env::set_current_dir("wolfssl-5.7.6-stable") { - eprintln!("Error changing directory: {}", e); - } else { - println!("Changed directory to wolfssl-5.7.6-stable."); - - // Step 5: Execute ./autogen.sh - let output = Command::new("./autogen.sh") - .output() - .expect("Failed to execute ./autogen.sh"); - - if output.status.success() { - println!("./autogen.sh completed successfully."); - - // Step 6: Execute ./configure - let output = Command::new("./configure") - .arg("--enable-all") - .arg("--enable-all-crypto") - .arg("--enable-debug") - .arg("--disable-shared") - .arg("--prefix=/opt/wolfssl-rs/") - .output() - .expect("Failed to execute ./configure"); - - if output.status.success() { - println!("./configure completed successfully."); - - // Step 7: Execute make - let output = Command::new("make") - .output() - .expect("Failed to execute make"); - - if output.status.success() { - println!("make completed successfully."); - - // Step 8: Execute sudo make install - let output = Command::new("sudo") - .arg("make") - .arg("install") - .output() - .expect("Failed to execute sudo make install"); - - if output.status.success() { - println!("sudo make install completed successfully."); - } else { - eprintln!( - "Error executing sudo make install: {}", - String::from_utf8_lossy(&output.stderr) - ); - } - } else { - eprintln!( - "Error executing make: {}", - String::from_utf8_lossy(&output.stderr) - ); - } - } else { - eprintln!( - "Error executing ./configure: {}", - String::from_utf8_lossy(&output.stderr) - ); - } - } else { - eprintln!( - "Error executing ./autogen.sh: {}", - String::from_utf8_lossy(&output.stderr) - ); - } - } - } else { - eprintln!( - "Error unzipping file: {}", + .arg(WOLFSSL_ZIP) + .arg(WOLFSSL_URL) + .output()?; + + if !output.status.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Failed to download: {}", + String::from_utf8_lossy(&output.stderr) + ), + )); + } + println!("Download completed successfully."); + Ok(()) +} + +/// Extracts the downloaded WolfSSL archive. +/// +/// Uses the unzip command to extract the contents of the downloaded ZIP file. +/// The archive name is defined in the constants. +/// +/// Returns `Ok(())` if extraction succeeds, or an error if it fails. +fn unzip_wolfssl() -> Result<()> { + let output = Command::new("unzip").arg(WOLFSSL_ZIP).output()?; + + if !output.status.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Failed to unzip: {}", String::from_utf8_lossy(&output.stderr) - ); - } - } else { - eprintln!( - "Error downloading file: {}", - String::from_utf8_lossy(&output.stderr) - ); + ), + )); } + println!("Unzipping completed successfully."); + Ok(()) +} + +/// Removes the downloaded ZIP file after extraction. +/// +/// This cleanup step removes the ZIP file to save disk space. +/// +/// Returns `Ok(())` if removal succeeds, or an error if it fails. +fn remove_zip() -> Result<()> { + fs::remove_file(WOLFSSL_ZIP)?; + println!("Removed ZIP file successfully."); + Ok(()) +} - // Final step: we change the directory back to the root directory - // to finally generate the bindings. - if let Err(e) = env::set_current_dir("../") { - eprintln!("Error changing directory: {}", e); - } else { - println!("Changed directory to wolfssl-5.7.6-stable."); +/// Builds WolfSSL from source. +/// +/// This function: +/// 1. Changes to the source directory +/// 2. Runs autogen.sh to generate build files +/// 3. Configures the build with specific options +/// 4. Builds the library +/// 5. Installs the library system-wide +/// +/// Returns `Ok(())` if all build steps succeed, or an error if any step fails. +fn build_wolfssl() -> Result<()> { + env::set_current_dir(WOLFSSL_DIR)?; + println!("Changed directory to {}.", WOLFSSL_DIR); + + run_command("./autogen.sh", &[])?; + run_command( + "./configure", + &[ + "--enable-all", + "--enable-all-crypto", + "--enable-debug", + "--disable-shared", + "--prefix=/opt/wolfssl-rs/", + ], + )?; + run_command("make", &[])?; + run_command("sudo", &["make", "install"])?; + + Ok(()) +} + +/// Helper function to execute shell commands. +/// +/// Executes a command with given arguments and handles the output appropriately. +/// +/// # Arguments +/// * `cmd` - The command to execute +/// * `args` - Array of arguments for the command +/// +/// Returns `Ok(())` if the command executes successfully, or an error if it fails. +fn run_command(cmd: &str, args: &[&str]) -> Result<()> { + let output = Command::new(cmd).args(args).output()?; + + if !output.status.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Failed to execute {}: {}", + cmd, + String::from_utf8_lossy(&output.stderr) + ), + )); } + println!("{} completed successfully.", cmd); + Ok(()) +} + +/// Changes the working directory back to the root directory. +/// +/// This function is called after building WolfSSL to return to the original +/// working directory for the rest of the build process. +/// +/// Returns `Ok(())` if the directory change succeeds, or an error if it fails. +fn change_back_to_root() -> Result<()> { + env::set_current_dir("../")?; + println!("Changed directory back to root."); + Ok(()) } diff --git a/wolfcrypt-rs/src/lib.rs b/wolfcrypt-rs/src/lib.rs index 1a2a172..ca6f032 100644 --- a/wolfcrypt-rs/src/lib.rs +++ b/wolfcrypt-rs/src/lib.rs @@ -9,73 +9,82 @@ mod tests { #[test] fn rsa_encrypt_decrypt() { - unsafe { - let mut rng: WC_RNG = mem::zeroed(); - let mut rsa_key: RsaKey = mem::zeroed(); - let mut input: String = "I use Turing Machines to ask questions".to_string(); - let input_length: word32 = input.len() as word32; - let mut out: [u8; 256] = [0; 256]; - let mut plain: [u8; 256] = [0; 256]; - let mut ret; + let mut rng: WC_RNG = unsafe { mem::zeroed() }; + let mut rsa_key: RsaKey = unsafe { mem::zeroed() }; + let input = "I use Turing Machines to ask questions"; + let input_length = input.len() as word32; + let mut out = [0u8; 256]; + let mut plain = [0u8; 256]; - ret = wc_InitRsaKey(&mut rsa_key, std::ptr::null_mut()); - if ret != 0 { - panic!("Error while initializing Rsa key! Ret value: {}", ret); - } + // Initialize RSA key + let ret = unsafe { wc_InitRsaKey(&mut rsa_key, std::ptr::null_mut()) }; + if ret != 0 { + panic!("Error while initializing Rsa key! Ret value: {}", ret); + } - ret = wc_InitRng(&mut rng); - if ret != 0 { - panic!("Error while initializing RNG!"); - } + // Initialize RNG + let ret = unsafe { wc_InitRng(&mut rng) }; + if ret != 0 { + panic!("Error while initializing RNG!"); + } - ret = wc_RsaSetRNG(&mut rsa_key, &mut rng); - if ret != 0 { - panic!("Error while setting rng to Rsa key! Ret value: {}", ret); - } + // Set RNG for RSA + let ret = unsafe { wc_RsaSetRNG(&mut rsa_key, &mut rng) }; + if ret != 0 { + panic!("Error while setting rng to Rsa key! Ret value: {}", ret); + } - ret = wc_MakeRsaKey( + // Generate RSA key + let ret = unsafe { + wc_MakeRsaKey( &mut rsa_key, 2048 as c_int, WC_RSA_EXPONENT.into(), &mut rng, - ); - if ret != 0 { - panic!("Error while creating the Rsa Key! Ret value: {}", ret); - } + ) + }; + if ret != 0 { + panic!("Error while creating the Rsa Key! Ret value: {}", ret); + } - ret = wc_RsaPublicEncrypt( - input.as_mut_ptr(), + // Encrypt + let ret = unsafe { + wc_RsaPublicEncrypt( + input.as_ptr() as *mut u8, input_length, out.as_mut_ptr(), - mem::size_of_val(&out).try_into().unwrap(), + out.len() as word32, &mut rsa_key, &mut rng, - ); - - if ret < 0 { - panic!("Error while encrypting with RSA! Ret value: {}", ret); - } + ) + }; + if ret < 0 { + panic!("Error while encrypting with RSA! Ret value: {}", ret); + } + let encrypted_len = ret; - ret = wc_RsaPrivateDecrypt( + // Decrypt + let ret = unsafe { + wc_RsaPrivateDecrypt( out.as_mut_ptr(), - ret.try_into().unwrap(), + encrypted_len as word32, plain.as_mut_ptr(), - mem::size_of_val(&plain).try_into().unwrap(), + plain.len() as word32, &mut rsa_key, - ); - - if ret < 0 { - panic!("Error while decrypting with RSA! Ret value: {}", ret); - } - - let plain_str = String::from_utf8_lossy(&plain).to_string(); - let input_str = - std::ffi::CStr::from_ptr(input.as_mut_ptr() as *const std::os::raw::c_char) - .to_str() - .expect("Failed to convert C string to str"); + ) + }; + if ret < 0 { + panic!("Error while decrypting with RSA! Ret value: {}", ret); + } + let decrypted_len = ret as usize; - assert_eq!(plain_str.trim_end_matches('\0'), input_str); + // Compare results + let decrypted_str = std::str::from_utf8(&plain[..decrypted_len]) + .expect("Failed to convert decrypted data to string"); + assert_eq!(decrypted_str, input); + // Cleanup + unsafe { wc_FreeRsaKey(&mut rsa_key); wc_FreeRng(&mut rng); }