From d2ba008951e9fb1a41ed1a1fc776ac597320df9c Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Tue, 21 May 2024 17:01:01 +0800 Subject: [PATCH] Support cryptography module --- .github/workflows/rust.yml | 17 +- Cargo.toml | 16 +- build.rs | 45 ++ src/errors.rs | 23 +- src/modules/crypto/crypto_adaptors/mod.rs | 11 + .../crypto/crypto_adaptors/openssl_adaptor.rs | 343 +++++++++ .../crypto/crypto_adaptors/tongsuo_adaptor.rs | 644 ++++++++++++++++ src/modules/crypto/mod.rs | 727 +++++++++++++++++- src/modules/mod.rs | 1 + 9 files changed, 1817 insertions(+), 10 deletions(-) create mode 100644 src/modules/crypto/crypto_adaptors/mod.rs create mode 100644 src/modules/crypto/crypto_adaptors/openssl_adaptor.rs create mode 100644 src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c5aba16..0a4ed8f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -31,6 +31,21 @@ jobs: - name: Run tests run: cargo test --verbose + # unix-tongsuo-test: + # strategy: + # matrix: + # os: + # - ubuntu-latest + # - macos-latest + # runs-on: ${{matrix.os}} + # + # steps: + # - uses: actions/checkout@v3 + # - name: Build + # run : cargo build --verbose --features crypto_adaptor_tongsuo --no-default-features --config 'patch.crates-io.openssl.git="https://github.com/Tongsuo-Project/rust-tongsuo,git"' --config 'patch.crates-io.openssl-sys.git="https://github.com/Tongsuo-Project/rust-tongsuo.git"' + # - name: Run tests + # run : cargo test --verbose --features crypto_adaptor_tongsuo --no-default-features --config 'patch.crates-io.openssl.git="https://github.com/Tongsuo-Project/rust-tongsuo,git"' --config 'patch.crates-io.openssl-sys.git="https://github.com/Tongsuo-Project/rust-tongsuo.git"' + unix-mysql-test: strategy: matrix: @@ -113,7 +128,7 @@ jobs: with: toolchain: stable - name: install diesel_cli - run: cargo install diesel_cli --no-default-features --features mysql + run: cargo install diesel_cli --no-default-features --features mysql --features crypto_adaptor_openssl - name: init database run: diesel setup --database-url mysql://root:password@127.0.0.1:3306/vault - name: Build diff --git a/Cargo.toml b/Cargo.toml index 591410d..b055ccb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,6 @@ serde_json = "^1.0" serde_bytes = "0.11" go-defer = "^0.1" rand = "^0.8" -openssl = { version = "0.10" } -openssl-sys = { version = "0.9" } derivative = "2.2.0" enum-map = "2.6.1" strum = { version = "0.25", features = ["derive"] } @@ -61,12 +59,20 @@ serde_asn1_der = "0.8" base64 = "0.22" ipnetwork = "0.20" -[patch.crates-io] -openssl = { git = "https://github.com/Tongsuo-Project/rust-tongsuo.git" } -openssl-sys = { git = "https://github.com/Tongsuo-Project/rust-tongsuo.git" } +# optional dependencies +openssl = { version = "0.10", optional = true } +openssl-sys = { version = "0.9", optional = true } + +# uncomment the following lines to use Tongsuo as underlying crypto adaptor +#[patch.crates-io] +#openssl = { git = "https://github.com/Tongsuo-Project/rust-tongsuo.git" } +#openssl-sys = { git = "https://github.com/Tongsuo-Project/rust-tongsuo.git" } [features] +default = ["crypto_adaptor_openssl"] storage_mysql = ["diesel", "r2d2", "r2d2-diesel"] +crypto_adaptor_openssl = ["dep:openssl", "dep:openssl-sys"] +crypto_adaptor_tongsuo = ["dep:openssl", "dep:openssl-sys"] [target.'cfg(unix)'.dependencies] daemonize = "0.5" diff --git a/build.rs b/build.rs index 2c0ca9e..dd45d26 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,52 @@ use std::env; +// This is not going to happen any more since we have a default feature definition in Cargo.toml +//#[cfg(not(any(feature = "crypto_adaptor_openssl", feature = "crypto_adaptor_tongsuo")))] +//compile_error! { +// r#" +// No cryptography adaptor is enabled! +// +// In RustyVault, the real cryptographic operations are done via "crypto_adaptor"s. +// +// A crypto adaptor is a module that conveys and translates high level cryptography +// operations like encryption, signing into the APIs provided by underlying cryptography +// libraries such as OpenSSL, Tongsuo and so forth. +// +// At current stage, only one crypto_adaptor can be enabled at compilation phase and later +// be used at run-time. "crypto_adaptor"s are configured as 'feature's in the Cargo context. +// +// Currently, the supported feature names of crypto adaptors are as follows, you can enable +// them by adding one '--features crypto_adaptor_name' option when running "cargo build": +// 1. the OpenSSL adaptor: crypto_adaptor_openssl +// 2. the Tongsuo adaptor: crypto_adaptor_tongsuo +// "# +//} + +#[cfg(all(feature = "crypto_adaptor_openssl", feature = "crypto_adaptor_tongsuo"))] +compile_error! { + r#" + Only one cryptography adapator can be enabled! + + In RustyVault, the real cryptographic operations are done via "crypto_adaptor"s. + + A crypto adaptor is a module that conveys and translates high level cryptography + operations like encryption, signing into the APIs provided by underlying cryptography + libraries such as OpenSSL, Tongsuo and so forth. + + At current stage, only one crypto_adaptor can be enabled at compilation phase and later + be used at run-time. "crypto_adaptor"s are configured as 'feature's in the Cargo context. + + Currently, the supported feature names of crypto adaptors are as follows, you can enable + them by adding one '--features crypto_adaptor_name' option when running "cargo build": + 1. the OpenSSL adaptor: crypto_adaptor_openssl + 2. the Tongsuo adaptor: crypto_adaptor_tongsuo + "# +} + fn main() { if let Ok(_) = env::var("DEP_OPENSSL_TONGSUO") { println!("cargo:rustc-cfg=tongsuo"); + } else if cfg!(feature = "crypto_adaptor_tongsuo") { + println!("cargo:rustc-cfg=tongsuo"); } } diff --git a/src/errors.rs b/src/errors.rs index 764eeba..3c75f31 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -12,6 +12,20 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum RvError { + #[error("Cipher operation update failed.")] + ErrCryptoCipherUpdateFailed, + #[error("Cipher operation finalization failed.")] + ErrCryptoCipherFinalizeFailed, + #[error("Cipher initialization failed.")] + ErrCryptoCipherInitFailed, + #[error("Cipher not initialized.")] + ErrCryptoCipherNotInited, + #[error("Cipher operation not supported.")] + ErrCryptoCipherOPNotSupported, + #[error("AEAD Cipher tag is missing.")] + ErrCryptoCipherNoTag, + #[error("AEAD Cipher tag should not be present.")] + ErrCryptoCipherAEADTagPresent, #[error("Config path is invalid.")] ErrConfigPathInvalid, #[error("Config load failed.")] @@ -274,7 +288,14 @@ pub enum RvError { impl PartialEq for RvError { fn eq(&self, other: &Self) -> bool { match (self, other) { - (RvError::ErrCoreLogicalBackendExist, RvError::ErrCoreLogicalBackendExist) + (RvError::ErrCryptoCipherUpdateFailed, RvError::ErrCryptoCipherUpdateFailed) + | (RvError::ErrCryptoCipherFinalizeFailed, RvError::ErrCryptoCipherFinalizeFailed) + | (RvError::ErrCryptoCipherInitFailed, RvError::ErrCryptoCipherInitFailed) + | (RvError::ErrCryptoCipherNotInited, RvError::ErrCryptoCipherNotInited) + | (RvError::ErrCryptoCipherOPNotSupported, RvError::ErrCryptoCipherOPNotSupported) + | (RvError::ErrCryptoCipherNoTag, RvError::ErrCryptoCipherNoTag) + | (RvError::ErrCryptoCipherAEADTagPresent, RvError::ErrCryptoCipherAEADTagPresent) + | (RvError::ErrCoreLogicalBackendExist, RvError::ErrCoreLogicalBackendExist) | (RvError::ErrCoreNotInit, RvError::ErrCoreNotInit) | (RvError::ErrCoreLogicalBackendNoExist, RvError::ErrCoreLogicalBackendNoExist) | (RvError::ErrCoreSealConfigInvalid, RvError::ErrCoreSealConfigInvalid) diff --git a/src/modules/crypto/crypto_adaptors/mod.rs b/src/modules/crypto/crypto_adaptors/mod.rs new file mode 100644 index 0000000..a67ac39 --- /dev/null +++ b/src/modules/crypto/crypto_adaptors/mod.rs @@ -0,0 +1,11 @@ +//! This is a Rust module that contains several adaptors to different cryptography libraries. +//! The rusty_vault::crypto module utilize these adaptors to do the real crypto operations. +//! +//! Only one crypto adaptor can be used in one build. It's configured when building RustyVault. +//! An adaptor implements a set of methods that perform cryptograhpy operations like encryption, +//! decription, signing, verification and so on. + +#[cfg(feature = "crypto_adaptor_openssl")] +pub mod openssl_adaptor; +#[cfg(feature = "crypto_adaptor_tongsuo")] +pub mod tongsuo_adaptor; diff --git a/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs new file mode 100644 index 0000000..411f1eb --- /dev/null +++ b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs @@ -0,0 +1,343 @@ +//! This is the OpenSSL adaptor. + +use crate::errors::RvError; +use crate::modules::crypto::{AEADCipher, AESKeySize, BlockCipher, CipherMode, AES}; +use openssl::symm::{Cipher, Crypter, Mode, encrypt, encrypt_aead, decrypt, decrypt_aead}; +use openssl::rand::rand_priv_bytes; + +pub struct AdaptorCTX { + ctx: Crypter, + tag_set: bool, + aad_set: bool, +} + +impl AES { + /// This function is the constructor of the AES struct, it returns a new AES object on success. + /// + /// keygen: true stands for generating a key and iv; if false, then the caller needs to feed in + /// the specific key and iv values through the parameters. + /// size: bit-length of AES. If omitted, AESKeySize::AES128 is used as default. + /// mode: cipher mode of AES, such as CBC, GCM, etc. If omitted, CipherMode::CBC is default. + /// key: symmetric key that is used to encrypt and decrypt data. + /// iv: initialization vector. This depends on specific mode, for instance, ECB requires no IV. + pub fn new( + keygen: bool, + size: Option, + mode: Option, + key: Option>, + iv: Option>, + ) -> Result { + // default algorithm: AES128 + CBC. + let mut k_size = AESKeySize::AES128; + let mut c_mode = CipherMode::CBC; + let aes_key: Vec; + let aes_iv: Vec; + + if let Some(x) = size { + k_size = x; + } + + if let Some(y) = mode { + c_mode = y; + } + + if keygen == false { + match (key, iv) { + (Some(x), Some(y)) => { + aes_key = x.clone(); + aes_iv = y.clone(); + }, + _ => return Err(RvError::ErrCryptoCipherInitFailed), + } + } else { + // generate new key and iv based on k_size. + match k_size { + AESKeySize::AES128 => { + // for aes-128, key is 16 bytes and iv is 16 bytes + let mut buf = [0; 16]; + let mut buf2 = [0; 16]; + rand_priv_bytes(&mut buf).unwrap(); + aes_key = buf.to_vec(); + rand_priv_bytes(&mut buf2).unwrap(); + aes_iv = buf2.to_vec(); + }, + AESKeySize::AES192 => todo!(), + AESKeySize::AES256 => { + // for aes-256, key is 32 bytes and iv is 16 bytes + let mut buf = [0; 32]; + let mut buf2 = [0; 16]; + rand_priv_bytes(&mut buf).unwrap(); + aes_key = buf.to_vec(); + rand_priv_bytes(&mut buf2).unwrap(); + aes_iv = buf2.to_vec(); + }, + } + } + + Ok ( + AES { + alg: (k_size, c_mode), + key: aes_key, + iv: aes_iv, + aad: None, + ctx: None, + tag: None, + } + ) + } + + /// This function returns the key and iv vaule stored in one AES object. + /// + /// Two values are returned in a tuple: the first element represents the key, and the second + /// element represents the IV. Elements may be None if unset. + pub fn get_key_iv(&self) -> (Vec, Vec) { + (self.key.clone(), self.iv.clone()) + } +} + +impl BlockCipher for AES { + fn encrypt(&mut self, plaintext: &Vec) -> Result, RvError> { + match self.alg { + (AESKeySize::AES128, CipherMode::CBC) => { + let ciphertext = encrypt( + Cipher::aes_128_cbc(), + &self.key, + Some(&self.iv), + plaintext).unwrap(); + return Ok(ciphertext.to_vec()); + } + (AESKeySize::AES192, CipherMode::CBC) => { + let ciphertext = encrypt( + Cipher::aes_192_cbc(), + &self.key, + Some(&self.iv), + plaintext).unwrap(); + return Ok(ciphertext.to_vec()); + } + (AESKeySize::AES256, CipherMode::CBC) => { + let ciphertext = encrypt( + Cipher::aes_256_cbc(), + &self.key, + Some(&self.iv), + plaintext).unwrap(); + return Ok(ciphertext.to_vec()); + } + (AESKeySize::AES128, CipherMode::GCM) => { + // aes_128_gcm's tag is 16-bytes long. + let tag: &mut [u8] = &mut [0; 16]; + let ciphertext = encrypt_aead( + Cipher::aes_128_gcm(), + &self.key, + Some(&self.iv), + &self.aad.clone().unwrap(), + plaintext, + tag + ).unwrap(); + self.tag = Some(tag.to_vec()); + return Ok(ciphertext.to_vec()); + } + _ => Err(RvError::ErrCryptoCipherOPNotSupported), + } + } + + fn encrypt_update(&mut self, plaintext: Vec, ciphertext: &mut Vec + ) -> Result { + let cipher; + + match self.alg { + (AESKeySize::AES128, CipherMode::CBC) => { + cipher = Cipher::aes_128_cbc(); + } + (AESKeySize::AES128, CipherMode::GCM) => { + cipher = Cipher::aes_128_gcm(); + } + _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } + } + + if let None = self.ctx { + // init adaptor ctx if it's not inited. + let encrypter = Crypter::new( + cipher, + Mode::Encrypt, + &self.key, + Some(&self.iv) + ).unwrap(); + let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; + + self.ctx = Some(adaptor_ctx); + } + + if self.alg.1 == CipherMode::GCM || self.alg.1 == CipherMode::CCM { + // set additional authenticated data before doing real jobs. + if self.ctx.as_mut().unwrap().aad_set == false { + if let Some(aad) = &self.aad { + self.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + self.ctx.as_mut().unwrap().aad_set = true; + } + } + } + + // do real jobs. + // this Crypter::update returns a Result, we simply ignore the detailed + // error information by unwrapping it. + // we also can't use the question mark operatior since the error codes are differently + // defined in RustyVault and underlying adaptor, such as rust-openssl. + let count = self.ctx.as_mut().unwrap().ctx.update(&plaintext, &mut ciphertext[..]).unwrap(); + Ok(count) + } + + fn encrypt_final(&mut self, ciphertext: &mut Vec + ) -> Result { + // Unlike encrypt_update() function, we don't do auto-initialization here. + if let None = self.ctx { + return Err(RvError::ErrCryptoCipherNotInited); + } + + let count = self.ctx.as_mut().unwrap().ctx.finalize(ciphertext).unwrap(); + + if self.alg.1 == CipherMode::GCM { + // set tag for caller to obtain. + if let Some(_) = self.tag { + // tag should not be set before encrypt_final() is called. + return Err(RvError::ErrCryptoCipherAEADTagPresent); + } + + // 16-byte long is enough for all types of AEAD cipher tag. + // TODO: this is for AES-128-GCM only. + let mut tag: Vec = vec![0; 16]; + self.ctx.as_mut().unwrap().ctx.get_tag(&mut tag).unwrap(); + self.tag = Some(tag); + } + + Ok(count) + } + + fn decrypt(&mut self, ciphertext: &Vec) -> Result, RvError> { + match self.alg { + (AESKeySize::AES128, CipherMode::CBC) => { + let plaintext = decrypt( + Cipher::aes_128_cbc(), + &self.key, + Some(&self.iv), + ciphertext).unwrap(); + return Ok(plaintext.to_vec()); + } + (AESKeySize::AES128, CipherMode::GCM) => { + // aes_128_gcm's tag is 16-bytes long. + let plaintext = decrypt_aead( + Cipher::aes_128_gcm(), + &self.key, + Some(&self.iv), + &self.aad.clone().unwrap(), + ciphertext, + &self.tag.clone().unwrap() + ).unwrap(); + return Ok(plaintext.to_vec()); + } + _ => Err(RvError::ErrCryptoCipherOPNotSupported), + } + + } + + fn decrypt_update(&mut self, ciphertext: Vec, plaintext: &mut Vec + ) -> Result { + let cipher; + + match self.alg { + (AESKeySize::AES128, CipherMode::CBC) => { + cipher = Cipher::aes_128_cbc(); + } + (AESKeySize::AES128, CipherMode::GCM) => { + cipher = Cipher::aes_128_gcm(); + } + _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } + } + + if let None = self.ctx { + // init adaptor ctx if it's not inited. + let encrypter = Crypter::new( + cipher, + Mode::Decrypt, + &self.key, + Some(&self.iv) + ).unwrap(); + let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; + + self.ctx = Some(adaptor_ctx); + } + + // set additional authenticated data before doing real jobs. + if self.alg.1 == CipherMode::GCM { + if self.ctx.as_mut().unwrap().aad_set == false { + if let Some(aad) = &self.aad { + self.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + self.ctx.as_mut().unwrap().aad_set = true; + } + } + } + + // do real jobs. + // this Crypter::update returns a Result, print detailed error if any. + match self.ctx.as_mut().unwrap().ctx.update(&ciphertext, plaintext) { + Ok(count) => { return Ok(count); } + Err(err_stack) => { + let errs = err_stack.errors(); + println!("{}", errs.len()); + for err in errs.iter() { + println!("{:?}", err.reason()); + } + Err(RvError::ErrCryptoCipherUpdateFailed) + } + } + } + + fn decrypt_final(&mut self, plaintext: &mut Vec + ) -> Result { + // Unlike decrypt_update() function, we don't do auto-initialization here. + if let None = self.ctx { + return Err(RvError::ErrCryptoCipherNotInited); + } + + // set tag before doing real jobs. + if self.alg.1 == CipherMode::GCM { + if self.ctx.as_mut().unwrap().tag_set == false { + if let Some(tag) = &self.tag { + self.ctx.as_mut().unwrap().ctx.set_tag(tag).unwrap(); + self.ctx.as_mut().unwrap().tag_set = true; + } else { + // if tag is missing, then return an error. + return Err(RvError::ErrCryptoCipherNoTag); + } + } + } + + match self.ctx.as_mut().unwrap().ctx.finalize(plaintext) { + Ok(count) => { return Ok(count); } + Err(err_stack) => { + let errs = err_stack.errors(); + println!("{}", errs.len()); + for err in errs.iter() { + println!("{:?}", err.reason()); + } + Err(RvError::ErrCryptoCipherFinalizeFailed) + } + } + } +} + +impl AEADCipher for AES { + fn set_aad(&mut self, aad: Vec) -> Result<(), RvError> { + self.aad = Some(aad.clone()); + Ok(()) + } + fn get_tag(&mut self) -> Result, RvError> { + if self.tag == None { + return Err(RvError::ErrCryptoCipherNoTag); + } + Ok(self.tag.clone().unwrap()) + } + fn set_tag(&mut self, tag: Vec) -> Result<(), RvError> { + self.tag = Some(tag.clone()); + Ok(()) + } +} diff --git a/src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs b/src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs new file mode 100644 index 0000000..394be88 --- /dev/null +++ b/src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs @@ -0,0 +1,644 @@ +//! This is the Tongsuo adaptor. + +use crate::errors::RvError; +use crate::modules::crypto::{AEADCipher, AESKeySize, BlockCipher, CipherMode, AES}; +use openssl::symm::{Cipher, Crypter, Mode, encrypt, encrypt_aead, decrypt, decrypt_aead}; +use openssl::rand::rand_priv_bytes; +use crate::modules::crypto::SM4; + +pub struct AdaptorCTX { + ctx: Crypter, + tag_set: bool, + aad_set: bool, +} + +impl AES { + /// This function is the constructor of the AES struct, it returns a new AES object on success. + /// + /// keygen: true stands for generating a key and iv; if false, then the caller needs to feed in + /// the specific key and iv values through the parameters. + /// size: bit-length of AES. If omitted, AESKeySize::AES128 is used as default. + /// mode: cipher mode of AES, such as CBC, GCM, etc. If omitted, CipherMode::CBC is default. + /// key: symmetric key that is used to encrypt and decrypt data. + /// iv: initialization vector. This depends on specific mode, for instance, ECB requires no IV. + pub fn new( + keygen: bool, + size: Option, + mode: Option, + key: Option>, + iv: Option>, + ) -> Result { + // default algorithm: AES128 + CBC. + let mut k_size = AESKeySize::AES128; + let mut c_mode = CipherMode::CBC; + let aes_key: Vec; + let aes_iv: Vec; + + if let Some(x) = size { + k_size = x; + } + + if let Some(y) = mode { + c_mode = y; + } + + if keygen == false { + match (key, iv) { + (Some(x), Some(y)) => { + aes_key = x.clone(); + aes_iv = y.clone(); + }, + _ => return Err(RvError::ErrCryptoCipherInitFailed), + } + } else { + // generate new key and iv based on k_size. + match k_size { + AESKeySize::AES128 => { + // for aes-128, key is 16 bytes and iv is 16 bytes + let mut buf = [0; 16]; + let mut buf2 = [0; 16]; + rand_priv_bytes(&mut buf).unwrap(); + aes_key = buf.to_vec(); + rand_priv_bytes(&mut buf2).unwrap(); + aes_iv = buf2.to_vec(); + }, + AESKeySize::AES192 => { + // for aes-192, key is 24 bytes and iv is 16 bytes + let mut buf = [0; 24]; + let mut buf2 = [0; 16]; + rand_priv_bytes(&mut buf).unwrap(); + aes_key = buf.to_vec(); + rand_priv_bytes(&mut buf2).unwrap(); + aes_iv = buf2.to_vec(); + }, + AESKeySize::AES256 => { + // for aes-256, key is 32 bytes and iv is 16 bytes + let mut buf = [0; 32]; + let mut buf2 = [0; 16]; + rand_priv_bytes(&mut buf).unwrap(); + aes_key = buf.to_vec(); + rand_priv_bytes(&mut buf2).unwrap(); + aes_iv = buf2.to_vec(); + }, + } + } + + Ok ( + AES { + alg: (k_size, c_mode), + key: aes_key, + iv: aes_iv, + aad: None, + ctx: None, + tag: None, + } + ) + } + + /// This function returns the key and iv vaule stored in one AES object. + /// + /// Two values are returned in a tuple: the first element represents the key, and the second + /// element represents the IV. Elements may be None if unset. + pub fn get_key_iv(&self) -> (Vec, Vec) { + (self.key.clone(), self.iv.clone()) + } +} + +impl BlockCipher for AES { + fn encrypt(&mut self, plaintext: &Vec) -> Result, RvError> { + let cipher; + let mut aead = false; + + match self.alg { + (AESKeySize::AES128, CipherMode::CBC) => cipher = Cipher::aes_128_cbc(), + (AESKeySize::AES192, CipherMode::CBC) => cipher = Cipher::aes_192_cbc(), + (AESKeySize::AES256, CipherMode::CBC) => cipher = Cipher::aes_256_cbc(), + (AESKeySize::AES128, CipherMode::GCM) => { + cipher = Cipher::aes_128_gcm(); + aead = true; + }, + (AESKeySize::AES192, CipherMode::GCM) => { + cipher = Cipher::aes_192_gcm(); + aead = true; + }, + (AESKeySize::AES256, CipherMode::GCM) => { + cipher = Cipher::aes_256_gcm(); + aead = true; + }, + _ => return Err(RvError::ErrCryptoCipherOPNotSupported), + } + + if aead == false { + let ciphertext = encrypt( + cipher, + &self.key, + Some(&self.iv), + plaintext).unwrap(); + return Ok(ciphertext.to_vec()); + } else { + // aes_xxx_gcm's tag is at most 16-bytes long. + let tag: &mut [u8] = &mut [0; 16]; + let ciphertext = encrypt_aead( + cipher, + &self.key, + Some(&self.iv), + &self.aad.clone().unwrap(), + plaintext, + tag + ).unwrap(); + self.tag = Some(tag.to_vec()); + return Ok(ciphertext.to_vec()); + } + } + + fn encrypt_update(&mut self, plaintext: Vec, ciphertext: &mut Vec + ) -> Result { + let cipher; + + match self.alg { + (AESKeySize::AES128, CipherMode::CBC) => { + cipher = Cipher::aes_128_cbc(); + } + (AESKeySize::AES128, CipherMode::GCM) => { + cipher = Cipher::aes_128_gcm(); + } + _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } + } + + if let None = self.ctx { + // init adaptor ctx if it's not inited. + let encrypter = Crypter::new( + cipher, + Mode::Encrypt, + &self.key, + Some(&self.iv) + ).unwrap(); + let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; + + self.ctx = Some(adaptor_ctx); + } + + if self.alg.1 == CipherMode::GCM || self.alg.1 == CipherMode::CCM { + // set additional authenticated data before doing real jobs. + if self.ctx.as_mut().unwrap().aad_set == false { + if let Some(aad) = &self.aad { + self.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + self.ctx.as_mut().unwrap().aad_set = true; + } + } + } + + // do real jobs. + // this Crypter::update returns a Result, we simply ignore the detailed + // error information by unwrapping it. + // we also can't use the question mark operatior since the error codes are differently + // defined in RustyVault and underlying adaptor, such as rust-openssl. + let count = self.ctx.as_mut().unwrap().ctx.update(&plaintext, &mut ciphertext[..]).unwrap(); + Ok(count) + } + + fn encrypt_final(&mut self, ciphertext: &mut Vec + ) -> Result { + // Unlike encrypt_update() function, we don't do auto-initialization here. + if let None = self.ctx { + return Err(RvError::ErrCryptoCipherNotInited); + } + + let count = self.ctx.as_mut().unwrap().ctx.finalize(ciphertext).unwrap(); + + if self.alg.1 == CipherMode::GCM { + // set tag for caller to obtain. + if let Some(_) = self.tag { + // tag should not be set before encrypt_final() is called. + return Err(RvError::ErrCryptoCipherAEADTagPresent); + } + + // 16-byte long is enough for all types of AEAD cipher tag. + let mut tag: Vec = vec![0; 16]; + self.ctx.as_mut().unwrap().ctx.get_tag(&mut tag).unwrap(); + self.tag = Some(tag); + } + + Ok(count) + } + + fn decrypt(&mut self, ciphertext: &Vec) -> Result, RvError> { + match self.alg { + (AESKeySize::AES128, CipherMode::CBC) => { + let plaintext = decrypt( + Cipher::aes_128_cbc(), + &self.key, + Some(&self.iv), + ciphertext).unwrap(); + return Ok(plaintext.to_vec()); + } + (AESKeySize::AES128, CipherMode::GCM) => { + // aes_128_gcm's tag is 16-bytes long. + let plaintext = decrypt_aead( + Cipher::aes_128_gcm(), + &self.key, + Some(&self.iv), + &self.aad.clone().unwrap(), + ciphertext, + &self.tag.clone().unwrap() + ).unwrap(); + return Ok(plaintext.to_vec()); + } + _ => Err(RvError::ErrCryptoCipherOPNotSupported), + } + + } + + fn decrypt_update(&mut self, ciphertext: Vec, plaintext: &mut Vec + ) -> Result { + let cipher; + + match self.alg { + (AESKeySize::AES128, CipherMode::CBC) => { + cipher = Cipher::aes_128_cbc(); + } + (AESKeySize::AES128, CipherMode::GCM) => { + cipher = Cipher::aes_128_gcm(); + } + _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } + } + + if let None = self.ctx { + // init adaptor ctx if it's not inited. + let encrypter = Crypter::new( + cipher, + Mode::Decrypt, + &self.key, + Some(&self.iv) + ).unwrap(); + let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; + + self.ctx = Some(adaptor_ctx); + } + + // set additional authenticated data before doing real jobs. + if self.alg.1 == CipherMode::GCM { + if self.ctx.as_mut().unwrap().aad_set == false { + if let Some(aad) = &self.aad { + self.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + self.ctx.as_mut().unwrap().aad_set = true; + } + } + } + + // do real jobs. + // this Crypter::update returns a Result, print detailed error if any. + match self.ctx.as_mut().unwrap().ctx.update(&ciphertext, plaintext) { + Ok(count) => { return Ok(count); } + Err(err_stack) => { + let errs = err_stack.errors(); + println!("{}", errs.len()); + for err in errs.iter() { + println!("{:?}", err.reason()); + } + Err(RvError::ErrCryptoCipherUpdateFailed) + } + } + } + + fn decrypt_final(&mut self, plaintext: &mut Vec + ) -> Result { + // Unlike decrypt_update() function, we don't do auto-initialization here. + if let None = self.ctx { + return Err(RvError::ErrCryptoCipherNotInited); + } + + // set tag before doing real jobs. + if self.alg.1 == CipherMode::GCM { + if self.ctx.as_mut().unwrap().tag_set == false { + if let Some(tag) = &self.tag { + self.ctx.as_mut().unwrap().ctx.set_tag(tag).unwrap(); + self.ctx.as_mut().unwrap().tag_set = true; + } else { + // if tag is missing, then return an error. + return Err(RvError::ErrCryptoCipherNoTag); + } + } + } + + match self.ctx.as_mut().unwrap().ctx.finalize(plaintext) { + Ok(count) => { return Ok(count); } + Err(err_stack) => { + let errs = err_stack.errors(); + println!("{}", errs.len()); + for err in errs.iter() { + println!("{:?}", err.reason()); + } + Err(RvError::ErrCryptoCipherFinalizeFailed) + } + } + } +} + +impl AEADCipher for AES { + fn set_aad(&mut self, aad: Vec) -> Result<(), RvError> { + self.aad = Some(aad.clone()); + Ok(()) + } + fn get_tag(&mut self) -> Result, RvError> { + if self.tag == None { + return Err(RvError::ErrCryptoCipherNoTag); + } + Ok(self.tag.clone().unwrap()) + } + fn set_tag(&mut self, tag: Vec) -> Result<(), RvError> { + self.tag = Some(tag.clone()); + Ok(()) + } +} + +impl SM4 { + /// This function is the constructor of the SM4 struct, it returns a new SM4 object on success. + /// + /// keygen: true stands for generating a key and iv; if false, then the caller needs to feed in + /// the specific key and iv values through the parameters. + /// mode: cipher mode of SM4, such as CBC, GCM, etc. If omitted, CipherMode::CBC is default. + /// key: symmetric key that is used to encrypt and decrypt data. + /// iv: initialization vector. This depends on specific mode, for instance, ECB requires no IV. + pub fn new( + keygen: bool, + mode: Option, + key: Option>, + iv: Option>, + ) -> Result { + // default algorithm: SM4-CBC. + let mut c_mode = CipherMode::CBC; + let sm4_key: Vec; + let sm4_iv: Vec; + + if let Some(x) = mode { + c_mode = x; + } + + if keygen == false { + match (key, iv) { + (Some(x), Some(y)) => { + sm4_key = x.clone(); + sm4_iv = y.clone(); + }, + _ => return Err(RvError::ErrCryptoCipherInitFailed), + } + } else { + // generate new key and iv based on k_size. + // for SM4, key is 16 bytes and iv is 16 bytes + let mut buf = [0; 16]; + let mut buf2 = [0; 16]; + rand_priv_bytes(&mut buf).unwrap(); + sm4_key = buf.to_vec(); + rand_priv_bytes(&mut buf2).unwrap(); + sm4_iv = buf2.to_vec(); + } + + Ok ( + SM4 { + mode: c_mode, + key: sm4_key, + iv: sm4_iv, + aad: None, + ctx: None, + tag: None, + } + ) + } + + /// This function returns the key and iv vaule stored in one SM4 object. + /// + /// Two values are returned in a tuple: the first element represents the key, and the second + /// element represents the IV. Elements may be None if unset. + pub fn get_key_iv(&self) -> (Vec, Vec) { + (self.key.clone(), self.iv.clone()) + } +} + +impl BlockCipher for SM4 { + fn encrypt(&mut self, plaintext: &Vec) -> Result, RvError> { + match self.mode { + CipherMode::CBC => { + let ciphertext = encrypt( + Cipher::sm4_cbc(), + &self.key, + Some(&self.iv), + plaintext).unwrap(); + return Ok(ciphertext.to_vec()); + } + CipherMode::GCM => { + // aes_128_gcm's tag is 16-bytes long. + let tag: &mut [u8] = &mut [0; 16]; + let ciphertext = encrypt_aead( + Cipher::sm4_gcm(), + &self.key, + Some(&self.iv), + &self.aad.clone().unwrap(), + plaintext, + tag + ).unwrap(); + self.tag = Some(tag.to_vec()); + return Ok(ciphertext.to_vec()); + } + _ => Err(RvError::ErrCryptoCipherOPNotSupported), + } + } + + fn encrypt_update(&mut self, plaintext: Vec, ciphertext: &mut Vec + ) -> Result { + let cipher; + + match self.mode { + CipherMode::CBC => { + cipher = Cipher::sm4_cbc(); + } + CipherMode::GCM => { + cipher = Cipher::sm4_gcm(); + } + _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } + } + + if let None = self.ctx { + // init adaptor ctx if it's not inited. + let encrypter = Crypter::new( + cipher, + Mode::Encrypt, + &self.key, + Some(&self.iv) + ).unwrap(); + let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; + + self.ctx = Some(adaptor_ctx); + } + + if self.mode == CipherMode::GCM || self.mode == CipherMode::CCM { + // set additional authenticated data before doing real jobs. + if self.ctx.as_mut().unwrap().aad_set == false { + if let Some(aad) = &self.aad { + self.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + self.ctx.as_mut().unwrap().aad_set = true; + } + } + } + + // do real jobs. + // this Crypter::update returns a Result, we simply ignore the detailed + // error information by unwrapping it. + // we also can't use the question mark operatior since the error codes are differently + // defined in RustyVault and underlying adaptor, such as rust-openssl. + let count = self.ctx.as_mut().unwrap().ctx.update(&plaintext, &mut ciphertext[..]).unwrap(); + Ok(count) + } + + fn encrypt_final(&mut self, ciphertext: &mut Vec + ) -> Result { + // Unlike encrypt_update() function, we don't do auto-initialization here. + if let None = self.ctx { + return Err(RvError::ErrCryptoCipherNotInited); + } + + let count = self.ctx.as_mut().unwrap().ctx.finalize(ciphertext).unwrap(); + + if self.mode == CipherMode::GCM { + // set tag for caller to obtain. + if let Some(_) = self.tag { + // tag should not be set before encrypt_final() is called. + return Err(RvError::ErrCryptoCipherAEADTagPresent); + } + + // 16-byte long is enough for all types of AEAD cipher tag. + let mut tag: Vec = vec![0; 16]; + self.ctx.as_mut().unwrap().ctx.get_tag(&mut tag).unwrap(); + self.tag = Some(tag); + } + + Ok(count) + } + + fn decrypt(&mut self, ciphertext: &Vec) -> Result, RvError> { + match self.mode { + CipherMode::CBC => { + let plaintext = decrypt( + Cipher::sm4_cbc(), + &self.key, + Some(&self.iv), + ciphertext).unwrap(); + return Ok(plaintext.to_vec()); + } + CipherMode::GCM => { + // SM4 is a fixed 128-bit cipher, the tag is 16-bytes long. + let plaintext = decrypt_aead( + Cipher::sm4_gcm(), + &self.key, + Some(&self.iv), + &self.aad.clone().unwrap(), + ciphertext, + &self.tag.clone().unwrap() + ).unwrap(); + return Ok(plaintext.to_vec()); + } + _ => Err(RvError::ErrCryptoCipherOPNotSupported), + } + } + fn decrypt_update(&mut self, ciphertext: Vec, plaintext: &mut Vec + ) -> Result { + let cipher; + + match self.mode { + CipherMode::CBC => { + cipher = Cipher::sm4_cbc(); + } + CipherMode::GCM => { + cipher = Cipher::sm4_gcm(); + } + _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } + } + + if let None = self.ctx { + // init adaptor ctx if it's not inited. + let encrypter = Crypter::new( + cipher, + Mode::Decrypt, + &self.key, + Some(&self.iv) + ).unwrap(); + let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; + + self.ctx = Some(adaptor_ctx); + } + + // set additional authenticated data before doing real jobs. + if self.mode == CipherMode::GCM { + if self.ctx.as_mut().unwrap().aad_set == false { + if let Some(aad) = &self.aad { + self.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + self.ctx.as_mut().unwrap().aad_set = true; + } + } + } + + // do real jobs. + // this Crypter::update returns a Result, print detailed error if any. + match self.ctx.as_mut().unwrap().ctx.update(&ciphertext, plaintext) { + Ok(count) => { return Ok(count); } + Err(err_stack) => { + let errs = err_stack.errors(); + println!("{}", errs.len()); + for err in errs.iter() { + println!("{:?}", err.reason()); + } + Err(RvError::ErrCryptoCipherUpdateFailed) + } + } + } + + fn decrypt_final(&mut self, plaintext: &mut Vec + ) -> Result { + // Unlike decrypt_update() function, we don't do auto-initialization here. + if let None = self.ctx { + return Err(RvError::ErrCryptoCipherNotInited); + } + + // set tag before doing real jobs. + if self.mode == CipherMode::GCM { + if self.ctx.as_mut().unwrap().tag_set == false { + if let Some(tag) = &self.tag { + self.ctx.as_mut().unwrap().ctx.set_tag(tag).unwrap(); + self.ctx.as_mut().unwrap().tag_set = true; + } else { + // if tag is missing, then return an error. + return Err(RvError::ErrCryptoCipherNoTag); + } + } + } + + match self.ctx.as_mut().unwrap().ctx.finalize(plaintext) { + Ok(count) => { return Ok(count); } + Err(err_stack) => { + let errs = err_stack.errors(); + println!("{}", errs.len()); + for err in errs.iter() { + println!("{:?}", err.reason()); + } + Err(RvError::ErrCryptoCipherFinalizeFailed) + } + } + } +} + +impl AEADCipher for SM4 { + fn set_aad(&mut self, aad: Vec) -> Result<(), RvError> { + self.aad = Some(aad.clone()); + Ok(()) + } + fn get_tag(&mut self) -> Result, RvError> { + if self.tag == None { + return Err(RvError::ErrCryptoCipherNoTag); + } + Ok(self.tag.clone().unwrap()) + } + fn set_tag(&mut self, tag: Vec) -> Result<(), RvError> { + self.tag = Some(tag.clone()); + Ok(()) + } +} diff --git a/src/modules/crypto/mod.rs b/src/modules/crypto/mod.rs index 5f51dee..ea21f53 100644 --- a/src/modules/crypto/mod.rs +++ b/src/modules/crypto/mod.rs @@ -1,5 +1,726 @@ +//! The rusty_vault::crypto module abstracts a set of generic cryptography methods. These methods +//! are used by other modules in RustyVault. +//! +//! This module depends on underlying cryptography library. One crypto adaptors MUST be specified +//! during the configuration, then it's compiled into RustyVault. +//! +//! # Crypto Adaptor Configurations +//! +//! In RustyVault, the real cryptographic operations are done via "crypto_adaptor"s. +//! +//! A crypto adaptor is a module that conveys and translates high level cryptography +//! operations like encryption, signing into the APIs provided by underlying cryptography +//! libraries such as OpenSSL, Tongsuo and so forth. +//! +//! At current stage, only one crypto_adaptor can be enabled at compilation phase and later +//! be used at run-time. "crypto_adaptor"s are configured as 'feature's in the Cargo context. +//! +//! Currently, the supported feature names of crypto adaptors are as follows, you can enable +//! them by adding one '--features crypto_adaptor_name' option when running "cargo build": +//! +//! 1. the OpenSSL adaptor: crypto_adaptor_openssl +//! 2. the Tongsuo adaptor: crypto_adaptor_tongsuo +//! +//! If there is no explicit crypto adpator configured, then the `crypto_adaptor_openssl` is used as +//! the default option. +//! +//! # Enable the Tongsuo adaptor +//! +//! Tongsuo is a variant of OpenSSL but with more features on SMx algorithms and protocols. +//! RustyVault can use SM algorithms only if Tongsuo is built as the crypto adaptor. +//! +//! You need to build and install Tongsuo first into your local environment before building +//! RustyVault with Tongsuo. Check the following link for detailed installation steps: +//! [Tongsuo](https://github.com/Tongsuo-Project/Tongsuo) +//! +//! ~~~text +//! $ export OPENSSL_DIR=/path/to/tongsuo/install/directory +//! $ cargo build --features crypto_adaptor_tongsuo \ +//! --no-default-features \ +//! --config 'patch.crates-io.openssl.git="https://github.com/Tongsuo-Project/rust-tongsuo.git"'\ +//! --config 'patch.crates-io.openssl-sys.git="https://github.com/Tongsuo-Project/rust-tongsuo.git"' +//! ~~~ +//! +//! Or you can just uncomment the following lines in Cargo.toml: +//! +//! ~~~text +//! #[patch.crates-io] +//! #openssl = { git = "https://github.com/Tongsuo-Project/rust-tongsuo.git" } +//! #openssl-sys = { git = "https://github.com/Tongsuo-Project/rust-tongsuo.git" } +//! ~~~ +//! +//! and then: +//! +//! ~~~text +//! $ cargo build --features crypto_adaptor_tongsuo --no-default-features +//! ~~~ + +use crate::errors::RvError; +#[cfg(feature = "crypto_adaptor_openssl")] +use crypto_adaptors::openssl_adaptor::AdaptorCTX; +#[cfg(feature = "crypto_adaptor_tongsuo")] +use crypto_adaptors::tongsuo_adaptor::AdaptorCTX; + +pub mod crypto_adaptors; + +/// This defines common modes for block ciphers. +#[derive(PartialEq)] +pub enum CipherMode { + CBC, + GCM, + CCM, +} + +/// This enum defines common AES key size constants. +pub enum AESKeySize { + AES128, + AES192, + AES256, +} + +/// This enum defines public key algorithm type constants. +pub enum PublicKeyType { + RSA, + ECDSA, + SM2, +} + +// All structs are defined here. Every struct represents a type of cryptography algorithm. + +/// The AES block cipher structure. +// we add this lint here because it's not guaranteed all underlying adaptors support this +// algorithm. And it's logical to suppress warnings when building the code. +#[allow(dead_code)] +pub struct AES { + alg: (AESKeySize, CipherMode), + key: Vec, + iv: Vec, + aad: Option>, + tag: Option>, + ctx: Option, +} + +/// The SM4 block cipher structure. +#[allow(dead_code)] +pub struct SM4 { + mode: CipherMode, + key: Vec, + iv: Vec, + aad: Option>, + tag: Option>, + 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. +/// +/// # Examples +/// +/// The following are some examples on how to use the functions of trait BlockCipher. +/// +/// ## One-shot encryption and decryption +/// +/// ~~~ +/// use rusty_vault::modules::crypto::{AES, AESKeySize, CipherMode, BlockCipher}; +/// +/// let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); +/// let mut aes_encrypter = AES::new(false, Some(AESKeySize::AES128), +/// Some(CipherMode::CBC), Some(key.clone()), Some(iv.clone())).unwrap(); +/// let mut aes_decrypter = AES::new(false, Some(AESKeySize::AES128), +/// Some(CipherMode::CBC), Some(key), Some(iv)).unwrap(); +/// +/// let ct = aes_encrypter.encrypt(&data).unwrap(); +/// let pt = aes_decrypter.decrypt(&ct).unwrap(); +/// assert_eq!(data.to_vec(), pt); +/// ~~~ +/// +/// ## Stream encryption and decryption +/// +/// The following code works only with `crypto_adaptor_tongsuo`. +/// +#[cfg_attr(feature = "crypto_adaptor_tongsuo", doc = "~~~")] +#[cfg_attr(not(feature = "crypto_adaptor_tongsuo"), doc = "~~~ignore")] +/// use rusty_vault::modules::crypto::{SM4, CipherMode, BlockCipher}; +/// +/// let data: [&[u8]; 2] = [b"The best way to not feel hopeless ", +/// b"is to get up and do something."]; +/// let data2 = b"The best way to not feel hopeless is to get up and do something."; +/// let data_len = data.iter().fold(0, |sum, x| sum + x.len()); +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); +/// let mut sm4_encrypter = SM4::new(false, Some(CipherMode::CBC), +/// Some(key.clone()), Some(iv.clone())).unwrap(); +/// let mut sm4_decrypter = SM4::new(false, Some(CipherMode::CBC), +/// Some(key), Some(iv)).unwrap(); +/// let mut ct: Vec = vec![]; +/// +/// let mut v1: Vec = vec![0; data_len + 16]; +/// let mut v2: Vec= vec![0; data_len + 16]; +/// let mut v3: Vec= vec![0; data_len + 16]; +/// let mut count = sm4_encrypter.encrypt_update((&data[0]).to_vec(), &mut v1).unwrap(); +/// v1.truncate(count); +/// count = sm4_encrypter.encrypt_update((&data[1]).to_vec(), &mut v2).unwrap(); +/// v2.truncate(count); +/// count = sm4_encrypter.encrypt_final(&mut v3).unwrap(); +/// v3.truncate(count); +/// ct.extend(v1); +/// ct.extend(v2); +/// ct.extend(v3); +/// +/// let data_len2 = ct.len(); +/// let mut pt1: Vec = vec![0; data_len2 + 16]; +/// let mut pt2: Vec= vec![0; data_len2 + 16]; +/// let mut pt3: Vec= vec![0; data_len2 + 16]; +/// let mut pt: Vec = vec![]; +/// // separate ciphertext into 2 pieces. +/// let cts = [&ct[..9], &ct[9..]]; +/// +/// count = sm4_decrypter.decrypt_update((&cts[0]).to_vec(), &mut pt1).unwrap(); +/// pt1.truncate(count); +/// count = sm4_decrypter.decrypt_update((&cts[1]).to_vec(), &mut pt2).unwrap(); +/// pt2.truncate(count); +/// count = sm4_decrypter.decrypt_final(&mut pt3).unwrap(); +/// pt3.truncate(count); +/// pt.extend(pt1); +/// pt.extend(pt2); +/// pt.extend(pt3); +/// +/// // evaluate the result. +/// assert_eq!(data2.to_vec(), pt); +/// ~~~ +/// +/// ## Use an auto-generated key +/// +/// ~~~ +/// use rusty_vault::modules::crypto::{AES, AESKeySize, CipherMode, BlockCipher}; +/// +/// let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); +/// let mut aes_encrypter = AES::new(true, Some(AESKeySize::AES128), +/// Some(CipherMode::CBC), None, None).unwrap(); +/// let mut aes_decrypter = AES::new(false, Some(AESKeySize::AES128), +/// Some(CipherMode::CBC), Some(aes_encrypter.get_key_iv().0), +/// Some(aes_encrypter.get_key_iv().1)).unwrap(); +/// +/// let ct = aes_encrypter.encrypt(&data).unwrap(); +/// let pt = aes_decrypter.decrypt(&ct).unwrap(); +/// assert_eq!(data, pt); +/// ~~~ pub trait BlockCipher { - fn new(key: &[u8], iv &[u8]) -> Self; - fn encrypt(&mut self, input: &[u8]) -> Vec; - fn decrypt(&mut self, input: &[u8]) -> Vec; + /// One-shot encryption. + /// + /// This function performs a "one-shot' style encryption. The data to be encrypted is fed by + /// the `plaintext` parameter, while the ciphertext is returned in another `Vec`. + fn encrypt(&mut self, plaintext: &Vec) -> Result, RvError>; + + /// Stream encryption - update phase. + /// + /// The ciphertext (encrypted data) is returned via the `ciphertext` parameter. The bytes that + /// has been encrypted is returned in the return value of this function. + /// + /// Plaintext is fed by the `plaintext` parameter. + fn encrypt_update(&mut self, plaintext: Vec, ciphertext: &mut Vec) -> Result; + + /// Stream encryption - final phase. + /// + /// This function finishes the encryption. Residual ciphertext is returned in the `ciphertext` + /// parameter. `encrypt_update()` function should not be called after this function calling. + fn encrypt_final(&mut self, ciphertext: &mut Vec) -> Result; + + /// One-shot decryption. + /// + /// This function performs a "one-shot' style decryption. The data to be decrypted is fed by + /// the `ciphertext` parameter, while the plaintext is returned in another `Vec`. + fn decrypt(&mut self, ciphertext: &Vec) -> Result, RvError>; + + /// Stream decryption - update phase. + /// + /// The plaintext (decrypted data) is returned via the `plaintext` parameter. The bytes that + /// has been decrypted is returned in the return value of this function. + /// + /// Ciphertext is fed by the `ciphertext` parameter. + fn decrypt_update(&mut self, ciphertext: Vec, plaintext: &mut Vec) -> Result; + + /// Stream decryption - final phase. + /// + /// This function finishes the decryption. Residual plaintext is returned in the `plaintext` + /// parameter. `decrypt_update()` function should not be called after this function calling. + fn decrypt_final(&mut self, plaintext: &mut Vec) -> Result; +} + +/// AEADCipher defines a block cipher in AEAD mode, such as GCM or CCM. +/// This trait is an extention of BlockCipher for some additional functions. +/// +/// # Examples +/// +/// The following are some examples on how to use the functions of trait AEADCipher. +/// +/// # One-shot encryption and decryption using AEAD cipher +/// +/// ~~~ +/// use rusty_vault::modules::crypto::{AES, AESKeySize, CipherMode, BlockCipher, AEADCipher}; +/// +/// let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); +/// let aad = b"some additional authenticated data.".to_vec(); +/// let mut aes_encrypter = AES::new(false, Some(AESKeySize::AES128), +/// Some(CipherMode::GCM), Some(key.clone()), Some(iv.clone())).unwrap(); +/// let mut aes_decrypter = AES::new(false, Some(AESKeySize::AES128), +/// Some(CipherMode::GCM), Some(key), Some(iv)).unwrap(); +/// +/// // set aad, encrypt and get tag. +/// aes_encrypter.set_aad(aad.clone()).unwrap(); +/// let ct = aes_encrypter.encrypt(&data).unwrap(); +/// let tag = aes_encrypter.get_tag().unwrap(); +/// +/// // set aad, set tag and decrypt. +/// aes_decrypter.set_aad(aad).unwrap(); +/// aes_decrypter.set_tag(tag).unwrap(); +/// let pt = aes_decrypter.decrypt(&ct).unwrap(); +/// +/// // evaluate the result. +/// assert_eq!(data.to_vec(), pt); +/// ~~~ +/// +/// # Stream encryption and decryption using AEAD cipher +/// +/// The following code works only with `crypto_adaptor_tongsuo`. +/// +#[cfg_attr(feature = "crypto_adaptor_tongsuo", doc = "~~~")] +#[cfg_attr(not(feature = "crypto_adaptor_tongsuo"), doc = "~~~ignore")] +/// ~~~ +/// use rusty_vault::modules::crypto::{SM4, CipherMode, BlockCipher, AEADCipher}; +/// +/// let data: [&[u8]; 2] = [b"The best way to not feel hopeless ", +/// b"is to get up and do something."]; +/// let data2 = b"The best way to not feel hopeless is to get up and do something."; +/// let data_len = data.iter().fold(0, |sum, x| sum + x.len()); +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); +/// let aad = b"some additional authenticated data.".to_vec(); +/// let mut sm4_encrypter = SM4::new(false, Some(CipherMode::GCM), +/// Some(key.clone()), Some(iv.clone())).unwrap(); +/// let mut sm4_decrypter = SM4::new(false, Some(CipherMode::GCM), +/// Some(key), Some(iv)).unwrap(); +/// let mut ct: Vec = vec![]; +/// +/// // set aad, encrypt and get tag. +/// sm4_encrypter.set_aad(aad.clone()).unwrap(); +/// let mut v1: Vec = vec![0; data_len + 16]; +/// let mut v2: Vec= vec![0; data_len + 16]; +/// let mut v3: Vec= vec![0; data_len + 16]; +/// let mut count = sm4_encrypter.encrypt_update((&data[0]).to_vec(), &mut v1).unwrap(); +/// v1.truncate(count); +/// count = sm4_encrypter.encrypt_update((&data[1]).to_vec(), &mut v2).unwrap(); +/// v2.truncate(count); +/// count = sm4_encrypter.encrypt_final(&mut v3).unwrap(); +/// v3.truncate(count); +/// ct.extend(v1); +/// ct.extend(v2); +/// ct.extend(v3); +/// let tag = sm4_encrypter.get_tag().unwrap(); +/// +/// // set aad, set tag and decrypt. +/// sm4_decrypter.set_aad(aad).unwrap(); +/// sm4_decrypter.set_tag(tag).unwrap(); +/// // separate cipher into 2 pieces. +/// let data_len2 = ct.len(); +/// let mut pt1: Vec = vec![0; data_len2 + 16]; +/// let mut pt2: Vec= vec![0; data_len2 + 16]; +/// let mut pt3: Vec= vec![0; data_len2 + 16]; +/// let mut pt: Vec = vec![]; +/// let cts = [&ct[..9], &ct[9..]]; +/// count = sm4_decrypter.decrypt_update((&cts[0]).to_vec(), &mut pt1).unwrap(); +/// pt1.truncate(count); +/// count = sm4_decrypter.decrypt_update((&cts[1]).to_vec(), &mut pt2).unwrap(); +/// pt2.truncate(count); +/// count = sm4_decrypter.decrypt_final(&mut pt3).unwrap(); +/// pt3.truncate(count); +/// pt.extend(pt1); +/// pt.extend(pt2); +/// pt.extend(pt3); +/// +/// // evaluate the result. +/// assert_eq!(data2.to_vec(), pt); +/// ~~~ +pub trait AEADCipher: BlockCipher { + /// Set additional authenticated data (AAD) into AEAD cipher. + /// + /// This must be set both at encryption and decryption. This function must be called before the + /// updates functions like `encrypt_update()` and `decrypt_update()`. + fn set_aad(&mut self, aad: Vec) -> Result<(), RvError>; + + /// Get the authentication tag in AEAD ciphers. + /// + /// This must be called after `encrypt_final()`. Tag value is returned in a `Vec`. + fn get_tag(&mut self) -> Result, RvError>; + + /// Set the authentication tag to authenticate the ciphertext in AEAD decryption procedure. + /// + /// This function must be called before the `decrypt_final()` function. + fn set_tag(&mut self, tag: Vec) -> Result<(), RvError>; +} + +/// The PublicKey trait abstracts a common function set for public key algorithms. Public key +/// 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>; +} + +/// The Signature trait defines a signature algorithm, such as RSA, ECDSA or SM2. +/// This trait is a sub-trait of PublicKey trait. +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>; + + /// 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; +} + +/// The Encryption trait defines an public key encryption algorithm, such as RSA and SM4. +/// 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>; + + /// Decrypt a piece of data using the public key. + /// + /// The plaintext is returned on success. + fn decrypt(&mut self, ciphertext: &Vec) -> Result, RvError>; +} + +#[cfg(test)] +mod crypto_test { + use crate::modules::crypto::{AES, AESKeySize, CipherMode, BlockCipher, AEADCipher}; + #[cfg(feature = "crypto_adaptor_tongsuo")] + use crate::modules::crypto::SM4; + + #[test] + fn test_aes_keygen() { + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let mut aes_encrypter = AES::new(true, Some(AESKeySize::AES128), + Some(CipherMode::CBC), None, None).unwrap(); + let mut aes_decrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::CBC), Some(aes_encrypter.get_key_iv().0), + Some(aes_encrypter.get_key_iv().1)).unwrap(); + + let ct = aes_encrypter.encrypt(&data).unwrap(); + let pt = aes_decrypter.decrypt(&ct).unwrap(); + assert_eq!(data, pt); + } + + #[test] + fn test_aes_one_shot() { + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); + let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); + let mut aes_encrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::CBC), Some(key.clone()), Some(iv.clone())).unwrap(); + let mut aes_decrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::CBC), Some(key), Some(iv)).unwrap(); + + let ct = aes_encrypter.encrypt(&data).unwrap(); + let pt = aes_decrypter.decrypt(&ct).unwrap(); + assert_eq!(data.to_vec(), pt); + } + + #[test] + fn test_aes_aead_one_shot() { + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); + let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); + let aad = b"some additional authenticated data.".to_vec(); + let mut aes_encrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::GCM), Some(key.clone()), Some(iv.clone())).unwrap(); + let mut aes_decrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::GCM), Some(key), Some(iv)).unwrap(); + + // set aad, encrypt and get tag. + aes_encrypter.set_aad(aad.clone()).unwrap(); + let ct = aes_encrypter.encrypt(&data).unwrap(); + let tag = aes_encrypter.get_tag().unwrap(); + + // set aad, set tag and decrypt. + aes_decrypter.set_aad(aad).unwrap(); + aes_decrypter.set_tag(tag).unwrap(); + let pt = aes_decrypter.decrypt(&ct).unwrap(); + + // evaluate the result. + assert_eq!(data.to_vec(), pt); + } + + #[test] + fn test_aes_stream() { + let data: [&[u8]; 2] = [b"The best way to not feel hopeless ", + b"is to get up and do something."]; + let data2 = b"The best way to not feel hopeless is to get up and do something."; + let data_len = data.iter().fold(0, |sum, x| sum + x.len()); + let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); + let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); + let mut aes_encrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::CBC), Some(key.clone()), Some(iv.clone())).unwrap(); + let mut aes_decrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::CBC), Some(key), Some(iv)).unwrap(); + let mut ct: Vec = vec![]; + + let mut v1: Vec = vec![0; data_len + 16]; + let mut v2: Vec= vec![0; data_len + 16]; + let mut v3: Vec= vec![0; data_len + 16]; + let mut count = aes_encrypter.encrypt_update((&data[0]).to_vec(), &mut v1).unwrap(); + v1.truncate(count); + count = aes_encrypter.encrypt_update((&data[1]).to_vec(), &mut v2).unwrap(); + v2.truncate(count); + count = aes_encrypter.encrypt_final(&mut v3).unwrap(); + v3.truncate(count); + ct.extend(v1); + ct.extend(v2); + ct.extend(v3); + + let data_len2 = ct.len(); + let mut pt1: Vec = vec![0; data_len2 + 16]; + let mut pt2: Vec= vec![0; data_len2 + 16]; + let mut pt3: Vec= vec![0; data_len2 + 16]; + let mut pt: Vec = vec![]; + // separate ciphertext into 2 pieces. + let cts = [&ct[..9], &ct[9..]]; + + count = aes_decrypter.decrypt_update((&cts[0]).to_vec(), &mut pt1).unwrap(); + pt1.truncate(count); + count = aes_decrypter.decrypt_update((&cts[1]).to_vec(), &mut pt2).unwrap(); + pt2.truncate(count); + count = aes_decrypter.decrypt_final(&mut pt3).unwrap(); + pt3.truncate(count); + pt.extend(pt1); + pt.extend(pt2); + pt.extend(pt3); + + // evaluate the result. + assert_eq!(data2.to_vec(), pt); + } + + #[test] + fn test_aes_aead_stream() { + let data: [&[u8]; 2] = [b"The best way to not feel hopeless ", + b"is to get up and do something."]; + let data2 = b"The best way to not feel hopeless is to get up and do something."; + let data_len = data.iter().fold(0, |sum, x| sum + x.len()); + let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); + let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); + let aad = b"some additional authenticated data.".to_vec(); + let mut aes_encrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::GCM), Some(key.clone()), Some(iv.clone())).unwrap(); + let mut aes_decrypter = AES::new(false, Some(AESKeySize::AES128), + Some(CipherMode::GCM), Some(key), Some(iv)).unwrap(); + let mut ct: Vec = vec![]; + + // set aad, encrypt and get tag. + aes_encrypter.set_aad(aad.clone()).unwrap(); + let mut v1: Vec = vec![0; data_len + 16]; + let mut v2: Vec= vec![0; data_len + 16]; + let mut v3: Vec= vec![0; data_len + 16]; + let mut count = aes_encrypter.encrypt_update((&data[0]).to_vec(), &mut v1).unwrap(); + v1.truncate(count); + count = aes_encrypter.encrypt_update((&data[1]).to_vec(), &mut v2).unwrap(); + v2.truncate(count); + count = aes_encrypter.encrypt_final(&mut v3).unwrap(); + v3.truncate(count); + ct.extend(v1); + ct.extend(v2); + ct.extend(v3); + let tag = aes_encrypter.get_tag().unwrap(); + + // set aad, set tag and decrypt. + aes_decrypter.set_aad(aad).unwrap(); + aes_decrypter.set_tag(tag).unwrap(); + // separate cipher into 2 pieces. + let data_len2 = ct.len(); + let mut pt1: Vec = vec![0; data_len2 + 16]; + let mut pt2: Vec= vec![0; data_len2 + 16]; + let mut pt3: Vec= vec![0; data_len2 + 16]; + let mut pt: Vec = vec![]; + let cts = [&ct[..9], &ct[9..]]; + count = aes_decrypter.decrypt_update((&cts[0]).to_vec(), &mut pt1).unwrap(); + pt1.truncate(count); + count = aes_decrypter.decrypt_update((&cts[1]).to_vec(), &mut pt2).unwrap(); + pt2.truncate(count); + count = aes_decrypter.decrypt_final(&mut pt3).unwrap(); + pt3.truncate(count); + pt.extend(pt1); + pt.extend(pt2); + pt.extend(pt3); + + // evaluate the result. + assert_eq!(data2.to_vec(), pt); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm4_keygen() { + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let mut sm4_encrypter = SM4::new(true, Some(CipherMode::CBC), None, None).unwrap(); + let mut sm4_decrypter = SM4::new(false, Some(CipherMode::CBC), + Some(sm4_encrypter.get_key_iv().0), Some(sm4_encrypter.get_key_iv().1)).unwrap(); + + let ct = sm4_encrypter.encrypt(&data).unwrap(); + let pt = sm4_decrypter.decrypt(&ct).unwrap(); + assert_eq!(data, pt); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm4_one_shot() { + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); + let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); + let mut sm4_encrypter = SM4::new(false, Some(CipherMode::CBC), + Some(key.clone()), Some(iv.clone())).unwrap(); + let mut sm4_decrypter = SM4::new(false, Some(CipherMode::CBC), + Some(key), Some(iv)).unwrap(); + + let ct = sm4_encrypter.encrypt(&data).unwrap(); + let pt = sm4_decrypter.decrypt(&ct).unwrap(); + assert_eq!(data.to_vec(), pt); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm4_aead_one_shot() { + let data = b"The best way to not feel hopeless is to get up and do something.".to_vec(); + let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); + let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); + let aad = b"some additional authenticated data.".to_vec(); + let mut sm4_encrypter = SM4::new(false, Some(CipherMode::GCM), + Some(key.clone()), Some(iv.clone())).unwrap(); + let mut sm4_decrypter = SM4::new(false, Some(CipherMode::GCM), + Some(key), Some(iv)).unwrap(); + + // set aad, encrypt and get tag. + sm4_encrypter.set_aad(aad.clone()).unwrap(); + let ct = sm4_encrypter.encrypt(&data).unwrap(); + let tag = sm4_encrypter.get_tag().unwrap(); + + // set aad, set tag and decrypt. + sm4_decrypter.set_aad(aad).unwrap(); + sm4_decrypter.set_tag(tag).unwrap(); + let pt = sm4_decrypter.decrypt(&ct).unwrap(); + + // evaluate the result. + assert_eq!(data.to_vec(), pt); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm4_stream() { + let data: [&[u8]; 2] = [b"The best way to not feel hopeless ", + b"is to get up and do something."]; + let data2 = b"The best way to not feel hopeless is to get up and do something."; + let data_len = data.iter().fold(0, |sum, x| sum + x.len()); + let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); + let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); + let mut sm4_encrypter = SM4::new(false, Some(CipherMode::CBC), + Some(key.clone()), Some(iv.clone())).unwrap(); + let mut sm4_decrypter = SM4::new(false, Some(CipherMode::CBC), + Some(key), Some(iv)).unwrap(); + let mut ct: Vec = vec![]; + + let mut v1: Vec = vec![0; data_len + 16]; + let mut v2: Vec= vec![0; data_len + 16]; + let mut v3: Vec= vec![0; data_len + 16]; + let mut count = sm4_encrypter.encrypt_update((&data[0]).to_vec(), &mut v1).unwrap(); + v1.truncate(count); + count = sm4_encrypter.encrypt_update((&data[1]).to_vec(), &mut v2).unwrap(); + v2.truncate(count); + count = sm4_encrypter.encrypt_final(&mut v3).unwrap(); + v3.truncate(count); + ct.extend(v1); + ct.extend(v2); + ct.extend(v3); + + let data_len2 = ct.len(); + let mut pt1: Vec = vec![0; data_len2 + 16]; + let mut pt2: Vec= vec![0; data_len2 + 16]; + let mut pt3: Vec= vec![0; data_len2 + 16]; + let mut pt: Vec = vec![]; + // separate ciphertext into 2 pieces. + let cts = [&ct[..9], &ct[9..]]; + + count = sm4_decrypter.decrypt_update((&cts[0]).to_vec(), &mut pt1).unwrap(); + pt1.truncate(count); + count = sm4_decrypter.decrypt_update((&cts[1]).to_vec(), &mut pt2).unwrap(); + pt2.truncate(count); + count = sm4_decrypter.decrypt_final(&mut pt3).unwrap(); + pt3.truncate(count); + pt.extend(pt1); + pt.extend(pt2); + pt.extend(pt3); + + // evaluate the result. + assert_eq!(data2.to_vec(), pt); + } + + #[cfg(feature = "crypto_adaptor_tongsuo")] + #[test] + fn test_sm4_aead_stream() { + let data: [&[u8]; 2] = [b"The best way to not feel hopeless ", + b"is to get up and do something."]; + let data2 = b"The best way to not feel hopeless is to get up and do something."; + let data_len = data.iter().fold(0, |sum, x| sum + x.len()); + let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F".to_vec(); + let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07".to_vec(); + let aad = b"some additional authenticated data.".to_vec(); + let mut sm4_encrypter = SM4::new(false, Some(CipherMode::GCM), + Some(key.clone()), Some(iv.clone())).unwrap(); + let mut sm4_decrypter = SM4::new(false, Some(CipherMode::GCM), + Some(key), Some(iv)).unwrap(); + let mut ct: Vec = vec![]; + + // set aad, encrypt and get tag. + sm4_encrypter.set_aad(aad.clone()).unwrap(); + let mut v1: Vec = vec![0; data_len + 16]; + let mut v2: Vec= vec![0; data_len + 16]; + let mut v3: Vec= vec![0; data_len + 16]; + let mut count = sm4_encrypter.encrypt_update((&data[0]).to_vec(), &mut v1).unwrap(); + v1.truncate(count); + count = sm4_encrypter.encrypt_update((&data[1]).to_vec(), &mut v2).unwrap(); + v2.truncate(count); + count = sm4_encrypter.encrypt_final(&mut v3).unwrap(); + v3.truncate(count); + ct.extend(v1); + ct.extend(v2); + ct.extend(v3); + let tag = sm4_encrypter.get_tag().unwrap(); + + // set aad, set tag and decrypt. + sm4_decrypter.set_aad(aad).unwrap(); + sm4_decrypter.set_tag(tag).unwrap(); + // separate cipher into 2 pieces. + let data_len2 = ct.len(); + let mut pt1: Vec = vec![0; data_len2 + 16]; + let mut pt2: Vec= vec![0; data_len2 + 16]; + let mut pt3: Vec= vec![0; data_len2 + 16]; + let mut pt: Vec = vec![]; + let cts = [&ct[..9], &ct[9..]]; + count = sm4_decrypter.decrypt_update((&cts[0]).to_vec(), &mut pt1).unwrap(); + pt1.truncate(count); + count = sm4_decrypter.decrypt_update((&cts[1]).to_vec(), &mut pt2).unwrap(); + pt2.truncate(count); + count = sm4_decrypter.decrypt_final(&mut pt3).unwrap(); + pt3.truncate(count); + pt.extend(pt1); + pt.extend(pt2); + pt.extend(pt3); + + // evaluate the result. + assert_eq!(data2.to_vec(), pt); + } } diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 4a4fd18..2f351b8 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -11,6 +11,7 @@ use crate::{core::Core, errors::RvError}; pub mod auth; pub mod credential; +pub mod crypto; pub mod kv; pub mod pki; pub mod system;