From e5c3709e306bc06ccf881a2880e2f8bc9f0eb3fa Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Thu, 31 Oct 2024 17:54:33 +0800 Subject: [PATCH 1/3] Support RSA in OpenSSL adaptor --- src/errors.rs | 24 +++ .../crypto/crypto_adaptors/openssl_adaptor.rs | 165 +++++++++++++++++- src/modules/crypto/mod.rs | 102 ++++++++++- 3 files changed, 281 insertions(+), 10 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 89e1d7dc..0cbe03d0 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -13,6 +13,30 @@ use actix_web::http::StatusCode; #[derive(Error, Debug)] pub enum RvError { + #[error("RSA key generation failed.")] + ErrCryptoPKeyRSAKeyGenFailed, + #[error("Public key encryption failed.")] + ErrCryptoPKeyEncFailed, + #[error("Public key decryption failed.")] + ErrCryptoPKeyDecFailed, + #[error("Public key encryption init failed.")] + ErrCryptoPKeyEncInitFailed, + #[error("Public key decryption init failed.")] + ErrCryptoPKeyDecInitFailed, + #[error("Crypro module internal error.")] + ErrCryptoPKeyInternalError, + #[error("Verification failed.")] + ErrCryptoPKeyVerifyFailed, + #[error("Verification initialization failed.")] + ErrCryptoPKeyVerifyInitFailed, + #[error("Signing failed.")] + ErrCryptoPKeySignFailed, + #[error("Signing initialization failed.")] + ErrCryptoPKeySignInitFailed, + #[error("Invalid RSA key size.")] + ErrCryptoPKeyInvalidRSASize, + #[error("Public key operation not supported.")] + ErrCryptoPKeyOPNotSupported, #[error("Cipher operation update failed.")] ErrCryptoCipherUpdateFailed, #[error("Cipher operation finalization failed.")] diff --git a/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs index ab0d4057..5375e273 100644 --- a/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs +++ b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs @@ -1,11 +1,22 @@ //! This is the OpenSSL adaptor. use crate::errors::RvError; -use crate::modules::crypto::{AEADCipher, AESKeySize, BlockCipher, CipherMode, AES}; +use crate::modules::crypto::{ + AEADCipher, AESKeySize, BlockCipher, + CipherMode, AES, + RSA, RSAKeySize, + PublicKey, PublicKeyType, + Signature, Encryption +}; use openssl::symm::{Cipher, Crypter, Mode, encrypt, encrypt_aead, decrypt, decrypt_aead}; use openssl::rand::rand_priv_bytes; +use openssl::rsa::{Rsa, Padding}; +use openssl::pkey::{PKey, Private}; +use openssl::pkey_ctx::PkeyCtx; use crate::modules::crypto::crypto_adaptors::common; +use zeroize::{Zeroize, Zeroizing}; + pub struct AdaptorCTX { ctx: Crypter, tag_set: bool, @@ -81,3 +92,155 @@ impl AEADCipher for AES { common_aes_set_tag!(self, tag); } } + +pub struct AdaptorPKeyCTX { + // The private key in OpenSSL context contains also the public key + private_key: PKey, +} + +// Simply do nothing since OpenSSL will safely clean the memory of a PKEY object (Drop trait) +impl Zeroize for AdaptorPKeyCTX { + fn zeroize(&mut self) {} +} + +impl RSA { + /// This function is the constructor of the RSA struct, it returns a new RSA object on success. + /// + /// size: RSA key size. Valid options are RSA2048 (default), RSA3072, RSA4096, RSA8192. + /// prime: for multi-prime RSA usage (RFC 8017), default is 2. + pub fn new( + prime: Option, + size: Option, + ) -> Result { + return Ok( + RSA { + key_type: PublicKeyType::RSA, + prime: prime.unwrap_or(2), + size: size.unwrap_or(RSAKeySize::RSA2048), + ctx: None, + } + ); + } +} + +impl PublicKey for RSA { + fn keygen(&mut self) -> Result<(), RvError> { + let bits: u32; + match &self.size { + RSAKeySize::RSA2048 => bits = 2048, + RSAKeySize::RSA3072 => bits = 3072, + RSAKeySize::RSA4096 => bits = 4096, + RSAKeySize::RSA8192 => bits = 8192, + } + + let rsa = match Rsa::generate(bits) { + Ok(r) => r, + Err(_e) => return Err(RvError::ErrCryptoPKeyRSAKeyGenFailed), + }; + + let pkey = match PKey::from_rsa(rsa) { + Ok(r) => r, + Err(_e) => return Err(RvError::ErrCryptoPKeyRSAKeyGenFailed), + }; + + let adaptor_ctx = AdaptorPKeyCTX { private_key: pkey }; + self.ctx = Some(adaptor_ctx); + + return Ok(()); + } + + fn get_key_type(&self) -> Result<&PublicKeyType, RvError> { + return Ok(&self.key_type); + } +} + +impl Signature for RSA { + fn sign(&self, data: &Vec) -> Result, RvError> { + let key = &self.ctx.as_ref().unwrap().private_key; + + let mut ctx = match PkeyCtx::new(&key) { + Ok(ctx) => ctx, + Err(_e) => return Err(RvError::ErrCryptoPKeyInternalError), + }; + + match ctx.sign_init() { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeySignInitFailed), + } + + let mut signature: Vec = Vec::new(); + match ctx.sign_to_vec(data, &mut signature) { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeySignFailed), + } + + return Ok(signature); + } + + fn verify(&self, data: &Vec, sig: &Vec) -> Result { + let key = &self.ctx.as_ref().unwrap().private_key; + + let mut ctx = match PkeyCtx::new(&key) { + Ok(ctx) => ctx, + Err(_e) => return Err(RvError::ErrCryptoPKeyInternalError), + }; + + match ctx.verify_init() { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeyVerifyInitFailed), + } + + let valid = match ctx.verify(data, sig) { + Ok(ret) => ret, + Err(_e) => return Err(RvError::ErrCryptoPKeyVerifyFailed), + }; + + return Ok(valid); + } +} + +impl Encryption for RSA { + fn encrypt(&self, plaintext: &Vec) -> Result, RvError> { + let key = &self.ctx.as_ref().unwrap().private_key; + + let mut ctx = match PkeyCtx::new(&key) { + Ok(ctx) => ctx, + Err(_e) => return Err(RvError::ErrCryptoPKeyInternalError), + }; + + match ctx.encrypt_init() { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeyEncInitFailed), + } + + let mut ciphertext: Vec = Vec::new(); + match ctx.encrypt_to_vec(plaintext, &mut ciphertext) { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeyEncFailed), + } + + return Ok(ciphertext); + } + + fn decrypt(&self, ciphertext: &Vec) -> Result, RvError> { + let key = &self.ctx.as_ref().unwrap().private_key; + + let mut ctx = match PkeyCtx::new(&key) { + Ok(ctx) => ctx, + Err(_e) => return Err(RvError::ErrCryptoPKeyInternalError), + }; + + match ctx.decrypt_init() { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeyDecInitFailed), + } + + let mut plaintext: Vec = Vec::new(); + match ctx.decrypt_to_vec(ciphertext, &mut plaintext) { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeyDecFailed), + } + + return Ok(plaintext); + } +} diff --git a/src/modules/crypto/mod.rs b/src/modules/crypto/mod.rs index ea21f531..56a1033d 100644 --- a/src/modules/crypto/mod.rs +++ b/src/modules/crypto/mod.rs @@ -57,9 +57,11 @@ use crate::errors::RvError; #[cfg(feature = "crypto_adaptor_openssl")] -use crypto_adaptors::openssl_adaptor::AdaptorCTX; +use crypto_adaptors::openssl_adaptor::{AdaptorCTX, AdaptorPKeyCTX}; #[cfg(feature = "crypto_adaptor_tongsuo")] -use crypto_adaptors::tongsuo_adaptor::AdaptorCTX; +use crypto_adaptors::tongsuo_adaptor::{AdaptorCTX, AdaptorPKeyCTX}; + +use zeroize::{Zeroize, Zeroizing}; pub mod crypto_adaptors; @@ -85,6 +87,14 @@ pub enum PublicKeyType { SM2, } +/// This enum defines different RSA key size +pub enum RSAKeySize { + RSA2048, + RSA3072, + RSA4096, + RSA8192, +} + // All structs are defined here. Every struct represents a type of cryptography algorithm. /// The AES block cipher structure. @@ -111,6 +121,17 @@ pub struct SM4 { ctx: Option, } +/// The RSA public key structure +#[allow(dead_code)] +#[derive(Zeroize)] +#[zeroize(drop)] +pub struct RSA { + key_type: PublicKeyType, + size: RSAKeySize, + prime: u8, + ctx: Option, +} + /// BlockCipher is the 'base' trait for all kinds of block cipher alogrithms. In this trait, /// neccessary methods are defined. Cryptography adaptors need to implement this trait to provide /// real algorithms. @@ -368,7 +389,10 @@ pub trait AEADCipher: BlockCipher { /// algorithms usually refer to signature or encryption algorithms such as RSA, SM2 and so forth. pub trait PublicKey { /// Generate a pair of public and private key, based on specific algorithm type. - fn keygen() -> Result<(), RvError>; + fn keygen(&mut self) -> Result<(), RvError>; + + /// Return the public key type of an object. + fn get_key_type(&self) -> Result<&PublicKeyType, RvError>; } /// The Signature trait defines a signature algorithm, such as RSA, ECDSA or SM2. @@ -377,31 +401,47 @@ pub trait Signature: PublicKey { /// Sign a piece of data and returns the generated signature value. /// /// This operation uses the private key of a specific algorithm. - fn sign(&mut self, data: &Vec) -> Result, RvError>; + fn sign(&self, data: &Vec) -> Result, RvError>; /// Verify a piece of data against a signature and returns the verification result. /// /// This operation uses the public key of a specific algorithm. - fn verify(&mut self, data: &Vec, sig: &Vec) -> Result; + fn verify(&self, data: &Vec, sig: &Vec) -> Result; } -/// The Encryption trait defines an public key encryption algorithm, such as RSA and SM4. +/// The Encryption trait defines an public key encryption algorithm, such as RSA and SM2. /// This trait is a sub-trait of PublicKey trait. pub trait Encryption: PublicKey { /// Encrypt a piece of data using the private key. /// /// The ciphertext is returned on success. - fn encrypt(&mut self, plaintext: &Vec) -> Result, RvError>; + fn encrypt(&self, plaintext: &Vec) -> Result, RvError>; /// Decrypt a piece of data using the public key. /// /// The plaintext is returned on success. - fn decrypt(&mut self, ciphertext: &Vec) -> Result, RvError>; + fn decrypt(&self, ciphertext: &Vec) -> Result, RvError>; +} + +// It's not very necessary for current PublicKey structures to be zeroized since every sensitive +// data is safely cleared by OpenSSL because the rust-openssl crate implements the 'Drop' trait. +impl Zeroize for PublicKeyType { + fn zeroize(&mut self) {} +} + +impl Zeroize for RSAKeySize { + fn zeroize(&mut self) {} } #[cfg(test)] mod crypto_test { - use crate::modules::crypto::{AES, AESKeySize, CipherMode, BlockCipher, AEADCipher}; + use crate::modules::crypto::{ + AEADCipher, AESKeySize, BlockCipher, + CipherMode, AES, + RSA, RSAKeySize, + PublicKey, PublicKeyType, + Signature, Encryption + }; #[cfg(feature = "crypto_adaptor_tongsuo")] use crate::modules::crypto::SM4; @@ -723,4 +763,48 @@ mod crypto_test { // evaluate the result. assert_eq!(data2.to_vec(), pt); } + + #[test] + fn test_rsa_sign_verify() { + let mut rsa = RSA::new(Some(2), Some(RSAKeySize::RSA4096)).unwrap(); + rsa.keygen().unwrap(); + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let sig = rsa.sign(&data).unwrap(); + let valid = rsa.verify(&data, &sig).unwrap(); + assert_eq!(valid, true); + } + + #[test] + fn test_rsa_encrypt_decrypt() { + let mut rsa = RSA::new(None, None).unwrap(); + rsa.keygen().unwrap(); + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let ct = rsa.encrypt(&data).unwrap(); + let pt = rsa.decrypt(&ct).unwrap(); + assert_eq!(data, pt); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm2_keygen() { + assert_eq!(1, 1); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm2_sign_decrypt() { + assert_eq!(1, 1); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm2_encrypt_decrypt() { + assert_eq!(1, 1); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm2_encrypt_decrypt() { + assert_eq!(1, 1); + } } From 1d8995b30d265c6f0b368955c3946e905158643f Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Fri, 1 Nov 2024 17:36:48 +0800 Subject: [PATCH 2/3] Add ECDSA --- src/errors.rs | 2 + .../crypto/crypto_adaptors/openssl_adaptor.rs | 98 ++++++++++++++++++- src/modules/crypto/mod.rs | 33 ++++++- 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 0cbe03d0..e2f5fe8a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -13,6 +13,8 @@ use actix_web::http::StatusCode; #[derive(Error, Debug)] pub enum RvError { + #[error("EC key generation failed.")] + ErrCryptoPKeyECKeyGenFailed, #[error("RSA key generation failed.")] ErrCryptoPKeyRSAKeyGenFailed, #[error("Public key encryption failed.")] diff --git a/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs index 5375e273..742cdea8 100644 --- a/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs +++ b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs @@ -6,7 +6,8 @@ use crate::modules::crypto::{ CipherMode, AES, RSA, RSAKeySize, PublicKey, PublicKeyType, - Signature, Encryption + Signature, Encryption, + ECDSA, ECCurveName }; use openssl::symm::{Cipher, Crypter, Mode, encrypt, encrypt_aead, decrypt, decrypt_aead}; use openssl::rand::rand_priv_bytes; @@ -14,6 +15,8 @@ use openssl::rsa::{Rsa, Padding}; use openssl::pkey::{PKey, Private}; use openssl::pkey_ctx::PkeyCtx; use crate::modules::crypto::crypto_adaptors::common; +use openssl::nid::Nid; +use openssl::ec::{EcGroup, EcKey}; use zeroize::{Zeroize, Zeroizing}; @@ -244,3 +247,96 @@ impl Encryption for RSA { return Ok(plaintext); } } + +impl ECDSA { + /// This function is the constructor of the ECDSA struct, it returns a new ECDSA object + /// on success. + /// + /// curve: RSA key size. Valid options are RSA2048 (default), RSA3072, RSA4096, RSA8192. + /// prime: for multi-prime RSA usage (RFC 8017), default is 2. + pub fn new( + curve: Option, + ) -> Result { + return Ok( + ECDSA { + key_type: PublicKeyType::ECDSA, + curve: curve.unwrap_or(ECCurveName::prime256v1), + ctx: None, + } + ); + } +} + +impl PublicKey for ECDSA { + fn keygen(&mut self) -> Result<(), RvError> { + let nid: Nid; + match &self.curve { + ECCurveName::prime256v1 => nid = Nid::X9_62_PRIME256V1, + } + + let group = EcGroup::from_curve_name(nid)?; + let ec = match EcKey::generate(&group) { + Ok(r) => r, + Err(_e) => return Err(RvError::ErrCryptoPKeyECKeyGenFailed), + }; + + let pkey = match PKey::from_ec_key(ec) { + Ok(r) => r, + Err(_e) => return Err(RvError::ErrCryptoPKeyECKeyGenFailed), + }; + + let adaptor_ctx = AdaptorPKeyCTX { private_key: pkey }; + self.ctx = Some(adaptor_ctx); + + return Ok(()); + } + + fn get_key_type(&self) -> Result<&PublicKeyType, RvError> { + return Ok(&self.key_type); + } +} + +impl Signature for ECDSA { + fn sign(&self, data: &Vec) -> Result, RvError> { + let key = &self.ctx.as_ref().unwrap().private_key; + + let mut ctx = match PkeyCtx::new(&key) { + Ok(ctx) => ctx, + Err(_e) => return Err(RvError::ErrCryptoPKeyInternalError), + }; + + match ctx.sign_init() { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeySignInitFailed), + } + + let mut signature: Vec = Vec::new(); + match ctx.sign_to_vec(data, &mut signature) { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeySignFailed), + } + + return Ok(signature); + } + + fn verify(&self, data: &Vec, sig: &Vec) -> Result { + let key = &self.ctx.as_ref().unwrap().private_key; + + let mut ctx = match PkeyCtx::new(&key) { + Ok(ctx) => ctx, + Err(_e) => return Err(RvError::ErrCryptoPKeyInternalError), + }; + + match ctx.verify_init() { + Ok(_ret) => {}, + Err(_e) => return Err(RvError::ErrCryptoPKeyVerifyInitFailed), + } + + let valid = match ctx.verify(data, sig) { + Ok(ret) => ret, + Err(_e) => return Err(RvError::ErrCryptoPKeyVerifyFailed), + }; + + return Ok(valid); + } +} diff --git a/src/modules/crypto/mod.rs b/src/modules/crypto/mod.rs index 56a1033d..51252d81 100644 --- a/src/modules/crypto/mod.rs +++ b/src/modules/crypto/mod.rs @@ -84,6 +84,7 @@ pub enum AESKeySize { pub enum PublicKeyType { RSA, ECDSA, + EdDSA, SM2, } @@ -95,6 +96,11 @@ pub enum RSAKeySize { RSA8192, } +/// This enum defines various EC curve names +pub enum ECCurveName { + prime256v1, +} + // All structs are defined here. Every struct represents a type of cryptography algorithm. /// The AES block cipher structure. @@ -132,6 +138,16 @@ pub struct RSA { ctx: Option, } +/// The EC public key structure +#[allow(dead_code)] +#[derive(Zeroize)] +#[zeroize(drop)] +pub struct ECDSA { + key_type: PublicKeyType, + curve: ECCurveName, + ctx: Option, +} + /// BlockCipher is the 'base' trait for all kinds of block cipher alogrithms. In this trait, /// neccessary methods are defined. Cryptography adaptors need to implement this trait to provide /// real algorithms. @@ -433,6 +449,10 @@ impl Zeroize for RSAKeySize { fn zeroize(&mut self) {} } +impl Zeroize for ECCurveName { + fn zeroize(&mut self) {} +} + #[cfg(test)] mod crypto_test { use crate::modules::crypto::{ @@ -440,7 +460,8 @@ mod crypto_test { CipherMode, AES, RSA, RSAKeySize, PublicKey, PublicKeyType, - Signature, Encryption + Signature, Encryption, + ECDSA, ECCurveName }; #[cfg(feature = "crypto_adaptor_tongsuo")] use crate::modules::crypto::SM4; @@ -784,6 +805,16 @@ mod crypto_test { assert_eq!(data, pt); } + #[test] + fn test_ecdsa_sign_verify() { + let mut ecdsa = ECDSA::new(None).unwrap(); + ecdsa.keygen().unwrap(); + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let sig = ecdsa.sign(&data).unwrap(); + let valid = ecdsa.verify(&data, &sig).unwrap(); + assert_eq!(valid, true); + } + #[cfg(feature = "crypto_adaptor_tongsuo")] #[test] fn test_sm2_keygen() { From 4d02ec585e54059ae15c1f1f84c379aa0ce9ebde Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Wed, 8 Jan 2025 19:28:51 +0800 Subject: [PATCH 3/3] fixup! Add ECDSA --- src/modules/crypto/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/modules/crypto/mod.rs b/src/modules/crypto/mod.rs index 51252d81..f9e65366 100644 --- a/src/modules/crypto/mod.rs +++ b/src/modules/crypto/mod.rs @@ -832,10 +832,4 @@ mod crypto_test { fn test_sm2_encrypt_decrypt() { assert_eq!(1, 1); } - - #[cfg(feature = "crypto_adaptor_tongsuo")] - #[test] - fn test_sm2_encrypt_decrypt() { - assert_eq!(1, 1); - } }