From a8b5e98a47ff30d48c47debc1ccb95c825d9c3ae Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Fri, 14 Jun 2024 23:27:36 +0800 Subject: [PATCH 1/5] Improve links in doc --- src/utils/cidr.rs | 2 +- src/utils/ip_sock_addr.rs | 2 +- src/utils/salt.rs | 2 +- src/utils/sock_addr.rs | 2 +- src/utils/unix_sock_addr.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/cidr.rs b/src/utils/cidr.rs index 6be607b..b3d83e5 100644 --- a/src/utils/cidr.rs +++ b/src/utils/cidr.rs @@ -1,5 +1,5 @@ //! This module is a Rust replica of -//! https://github.com/hashicorp/vault/blob/main/sdk/helper/cidrutil/cidr.go +//! use std::{ str::FromStr, diff --git a/src/utils/ip_sock_addr.rs b/src/utils/ip_sock_addr.rs index 9e1e0da..e404732 100644 --- a/src/utils/ip_sock_addr.rs +++ b/src/utils/ip_sock_addr.rs @@ -1,5 +1,5 @@ //! This module is a Rust replica of -//! https://github.com/hashicorp/go-sockaddr/blob/master/ipv4addr.go +//! use std::{ fmt, diff --git a/src/utils/salt.rs b/src/utils/salt.rs index 0221468..65e21a7 100644 --- a/src/utils/salt.rs +++ b/src/utils/salt.rs @@ -1,5 +1,5 @@ //! This module is a Rust replica of -//! https://github.com/hashicorp/vault/blob/main/sdk/helper/salt/salt.go +//! use openssl::{ hash::{hash, MessageDigest}, diff --git a/src/utils/sock_addr.rs b/src/utils/sock_addr.rs index 63574ca..a73c7ef 100644 --- a/src/utils/sock_addr.rs +++ b/src/utils/sock_addr.rs @@ -1,5 +1,5 @@ //! This module is a Rust replica of -//! https://github.com/hashicorp/go-sockaddr/blob/master/sockaddr.go +//! use std::{ fmt, diff --git a/src/utils/unix_sock_addr.rs b/src/utils/unix_sock_addr.rs index c7cc8ca..d63b99d 100644 --- a/src/utils/unix_sock_addr.rs +++ b/src/utils/unix_sock_addr.rs @@ -1,5 +1,5 @@ //! This module is a Rust replica of -//! https://github.com/hashicorp/go-sockaddr/blob/master/unixsock.go +//! use std::fmt; use as_any::Downcast; From 4d6ce999a271d1fdf136eeae8dd9c6b046e94988 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Tue, 21 May 2024 17:01:01 +0800 Subject: [PATCH 2/5] Support cryptography module --- .github/workflows/rust.yml | 31 + Cargo.toml | 16 +- build.rs | 45 ++ doc/crypto.md | 68 ++ src/errors.rs | 23 +- src/modules/crypto/crypto_adaptors/common.rs | 339 ++++++++ src/modules/crypto/crypto_adaptors/mod.rs | 13 + .../crypto/crypto_adaptors/openssl_adaptor.rs | 82 ++ .../crypto/crypto_adaptors/tongsuo_adaptor.rs | 374 +++++++++ src/modules/crypto/mod.rs | 727 +++++++++++++++++- src/modules/mod.rs | 1 + 11 files changed, 1710 insertions(+), 9 deletions(-) create mode 100644 doc/crypto.md create mode 100644 src/modules/crypto/crypto_adaptors/common.rs 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..99cfc35 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,6 +14,7 @@ on: env: CARGO_TERM_COLOR: always + TONGSUO_VERSION: 8.4.0 jobs: unix-default-test: @@ -31,6 +32,34 @@ jobs: - name: Run tests run: cargo test --verbose + unix-tongsuo-test: + strategy: + matrix: + os: + - macos-latest + - ubuntu-latest + runs-on: ${{matrix.os}} + + steps: + - name: Build Tongsuo + run: | + wget "https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/${TONGSUO_VERSION}.tar.gz" + tar zxf "${TONGSUO_VERSION}.tar.gz" + pushd "Tongsuo-${TONGSUO_VERSION}" + ./config --prefix=${RUNNER_TEMP}/tongsuo --libdir=${RUNNER_TEMP}/tongsuo/lib + make -j4 + make install + popd + - uses: actions/checkout@v3 + - name: Build + run : | + OPENSSL_DIR=${RUNNER_TEMP}/tongsuo 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 : | + export LD_LIBRARY_PATH=${RUNNER_TEMP}/tongsuo/lib + OPENSSL_DIR=${RUNNER_TEMP}/tongsuo 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: @@ -101,6 +130,8 @@ jobs: Start-Process msiexec.exe -ArgumentList '/i', 'mysql-connector.msi', '/quiet', '/norestart' -NoNewWindow -Wait - name: Set MySQLCLIENT_LIB_DIR run: echo "MYSQLCLIENT_LIB_DIR=C:\Program Files\MySQL\MySQL Connector C 6.1\lib\vs14" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: Set MYSQLCLIENT_VERSION + run: echo "MYSQLCLIENT_VERSION=8.4.0" | Out-File -FilePath $env:GITHUB_ENV -Append - uses: shogo82148/actions-setup-mysql@v1 with: mysql-version: "5.7" 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/doc/crypto.md b/doc/crypto.md new file mode 100644 index 0000000..7daef42 --- /dev/null +++ b/doc/crypto.md @@ -0,0 +1,68 @@ +# RustyVault Crypto Adaptor + +In RustyVault, we provide a mechanism for the users to build with selectable underlying cryptography libraries. This is the "crypto adaptor" mechanism. + +Currently, only two adaptors are supported: + +* OpenSSL crypto adaptor +* Tongsuo crypto adaptor + +## The OpenSSL Crypto Adaptor + +The following steps require a properly installed OpenSSL library. There are many ways of installing an OpenSSL on various platforms, so in this docuemnt we don't discuss that part. + +The OpenSSL crypto adaptor is configured by default in RustyVault, so you can simply build RustyVault to enable it: + +~~~ +cargo build +~~~ + +Otherwise if you want to explicitly configure it, you can still use something like: + +~~~ +cargo build --features crypto_adaptor_openssl +~~~ + +But this is not necessary. + +## The Tongsuo Crypto Adaptor + +Tongsuo is a fork of OpenSSL aiming to have a better support on Chinese cryptography algorithms and standards. To use Tongsuo as the cryptography functionality provider in RustyVault, typically you need to build RustyVault as follows. + +### Download and Install Tongsuo + +Firstly, you need to have a copy of Tongsuo code and successfully build it into libraires and finally install it into somewhere in your machine. + +Go to [https://tongsuo.net/docs/compilation/compile-and-install](https://tongsuo.net/docs/compilation/compile-and-install) for more detailed information. + +### Configure RustyVault to use Tongsuo + +RustyVault uses rust-tongsuo crate to call C APIs provided by Tongsuo. So we need to configure Cargo to use it, let's assume Tongsuo is successfully installed to `/path/to/tongsuo` directory: + +~~~ +OPENSSL_DIR=/path/to/tongsuo 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"' +~~~ + +Furthermore, if you choose to use a local copy of rust-tongsuo crate, you can use the file path form as well. Assume the local rust-tongsuo crate is located in `/path/to/rust-tongsuo` directory: + +~~~ +OPENSSL_DIR=/path/to/tongsuo cargo build \ + --features crypto_adaptor_tongsuo \ + --no-default-features \ + --config 'patch.crates-io.openssl.path="/path/to/rust-tongsuo/openssl"' \ + --config 'patch.crates-io.openssl-sys.path="/path/to/rust-tongsuo/openssl-sys"' +~~~ + +### The `LD_LIBRARY_PATH` Variable + +If you are using Linux, then you may need to specify which path for RustyVault to look for the Tongsuo libraries. There are many ways of having this done, but in this document we demonstrate with the global environment variable way. + +~~~ +export LD_LIBRARY_PATH=/path/to/tongsuol/lib +~~~ + +Then you can run RustyVault smoothly. \ No newline at end of file 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/common.rs b/src/modules/crypto/crypto_adaptors/common.rs new file mode 100644 index 0000000..2e5b38b --- /dev/null +++ b/src/modules/crypto/crypto_adaptors/common.rs @@ -0,0 +1,339 @@ +//! This module contains some common functions used by openssl and tongsuo adaptors. +//! Functions in this module SHOULD NOT be used directly by applications. + +macro_rules! common_aes_set_aad { + ($aes: expr, $aad: expr) => { + $aes.aad = Some($aad.clone()); + return Ok(()); + } +} + +macro_rules! common_aes_get_tag { + ($aes: expr) => { + if $aes.tag == None { + return Err(RvError::ErrCryptoCipherNoTag); + } + return Ok($aes.tag.clone().unwrap()); + } +} + +macro_rules! common_aes_set_tag { + ($aes: expr, $tag: expr) => { + $aes.tag = Some($tag.clone()); + return Ok(()); + } +} + +macro_rules! common_aes_new { + ($keygen: expr, $size: expr, $mode: expr, $key: expr, $iv: expr) => { + // 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(); + }, + } + } + + return Ok ( + AES { + alg: (k_size, c_mode), + key: aes_key, + iv: aes_iv, + aad: None, + ctx: None, + tag: None, + } + ); + } +} + +macro_rules! common_get_key_iv { + ($aes: expr) => { + return ($aes.key.clone(), $aes.iv.clone()); + } +} + +macro_rules! common_aes_encrypt { + ($aes: expr, $plaintext: expr) => { + let cipher; + let mut aead = false; + + match $aes.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, + &$aes.key, + Some(&$aes.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, + &$aes.key, + Some(&$aes.iv), + &$aes.aad.clone().unwrap(), + $plaintext, + tag + ).unwrap(); + $aes.tag = Some(tag.to_vec()); + return Ok(ciphertext.to_vec()); + } + } +} + +macro_rules! common_aes_decrypt { + ($aes: expr, $ciphertext: expr) => { + match $aes.alg { + (AESKeySize::AES128, CipherMode::CBC) => { + let plaintext = decrypt( + Cipher::aes_128_cbc(), + &$aes.key, + Some(&$aes.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(), + &$aes.key, + Some(&$aes.iv), + &$aes.aad.clone().unwrap(), + $ciphertext, + &$aes.tag.clone().unwrap() + ).unwrap(); + return Ok(plaintext.to_vec()); + } + _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } + } + } +} + +macro_rules! common_aes_encrypt_update { + ($aes: expr, $plaintext: expr, $ciphertext: expr) => { + let cipher; + + match $aes.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 = $aes.ctx { + // init adaptor ctx if it's not inited. + let encrypter = Crypter::new( + cipher, + Mode::Encrypt, + &$aes.key, + Some(&$aes.iv) + ).unwrap(); + let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; + + $aes.ctx = Some(adaptor_ctx); + } + + if $aes.alg.1 == CipherMode::GCM || $aes.alg.1 == CipherMode::CCM { + // set additional authenticated data before doing real jobs. + if $aes.ctx.as_mut().unwrap().aad_set == false { + if let Some(aad) = &$aes.aad { + $aes.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + $aes.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 = $aes.ctx.as_mut().unwrap().ctx.update(&$plaintext, &mut $ciphertext[..]).unwrap(); + + return Ok(count); + } +} + +macro_rules! common_aes_encrypt_final { + ($aes: expr, $ciphertext: expr) => { + // Unlike encrypt_update() function, we don't do auto-initialization here. + if let None = $aes.ctx { + return Err(RvError::ErrCryptoCipherNotInited); + } + + let count = $aes.ctx.as_mut().unwrap().ctx.finalize($ciphertext).unwrap(); + + if $aes.alg.1 == CipherMode::GCM { + // set tag for caller to obtain. + if let Some(_) = $aes.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]; + $aes.ctx.as_mut().unwrap().ctx.get_tag(&mut tag).unwrap(); + $aes.tag = Some(tag); + } + + return Ok(count); + } +} + +macro_rules! common_aes_decrypt_update { + ($aes: expr, $ciphertext: expr, $plaintext: expr) => { + let cipher; + + match $aes.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 = $aes.ctx { + // init adaptor ctx if it's not inited. + let encrypter = Crypter::new( + cipher, + Mode::Decrypt, + &$aes.key, + Some(&$aes.iv) + ).unwrap(); + let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; + + $aes.ctx = Some(adaptor_ctx); + } + + // set additional authenticated data before doing real jobs. + if $aes.alg.1 == CipherMode::GCM { + if $aes.ctx.as_mut().unwrap().aad_set == false { + if let Some(aad) = &$aes.aad { + $aes.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + $aes.ctx.as_mut().unwrap().aad_set = true; + } + } + } + + // do real jobs. + // this Crypter::update returns a Result, print detailed error if any. + match $aes.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()); + } + return Err(RvError::ErrCryptoCipherUpdateFailed); + } + } + } +} + +macro_rules! common_aes_decrypt_final { + ($aes: expr, $plaintext: expr) => { + // Unlike decrypt_update() function, we don't do auto-initialization here. + if let None = $aes.ctx { + return Err(RvError::ErrCryptoCipherNotInited); + } + + // set tag before doing real jobs. + if $aes.alg.1 == CipherMode::GCM { + if $aes.ctx.as_mut().unwrap().tag_set == false { + if let Some(tag) = &$aes.tag { + $aes.ctx.as_mut().unwrap().ctx.set_tag(tag).unwrap(); + $aes.ctx.as_mut().unwrap().tag_set = true; + } else { + // if tag is missing, then return an error. + return Err(RvError::ErrCryptoCipherNoTag); + } + } + } + + match $aes.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()); + } + return Err(RvError::ErrCryptoCipherFinalizeFailed); + } + } + } +} diff --git a/src/modules/crypto/crypto_adaptors/mod.rs b/src/modules/crypto/crypto_adaptors/mod.rs new file mode 100644 index 0000000..09f3da8 --- /dev/null +++ b/src/modules/crypto/crypto_adaptors/mod.rs @@ -0,0 +1,13 @@ +//! 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. + +#[macro_use] +pub mod common; +#[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..59af645 --- /dev/null +++ b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs @@ -0,0 +1,82 @@ +//! 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 { + common_aes_new!(keygen, size, mode, key, iv); + } + + /// 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) { + common_get_key_iv!(self); + } +} + +impl BlockCipher for AES { + fn encrypt(&mut self, plaintext: &Vec) -> Result, RvError> { + common_aes_encrypt!(self, plaintext); + } + + fn encrypt_update(&mut self, plaintext: Vec, ciphertext: &mut Vec + ) -> Result { + common_aes_encrypt_update!(self, plaintext, ciphertext); + } + + fn encrypt_final(&mut self, ciphertext: &mut Vec + ) -> Result { + common_aes_encrypt_final!(self, ciphertext); + } + + fn decrypt(&mut self, ciphertext: &Vec) -> Result, RvError> { + common_aes_decrypt!(self, ciphertext); + } + + fn decrypt_update(&mut self, ciphertext: Vec, plaintext: &mut Vec + ) -> Result { + common_aes_decrypt_update!(self, ciphertext, plaintext); + } + + fn decrypt_final(&mut self, plaintext: &mut Vec + ) -> Result { + common_aes_decrypt_final!(self, plaintext); + } +} + +impl AEADCipher for AES { + fn set_aad(&mut self, aad: Vec) -> Result<(), RvError> { + common_aes_set_aad!(self, aad); + } + fn get_tag(&mut self) -> Result, RvError> { + common_aes_get_tag!(self); + } + fn set_tag(&mut self, tag: Vec) -> Result<(), RvError> { + common_aes_set_tag!(self, tag); + } +} 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..3906f60 --- /dev/null +++ b/src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs @@ -0,0 +1,374 @@ +//! 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 { + common_aes_new!(keygen, size, mode, key, iv); + } + + /// 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) { + common_get_key_iv!(self); + } +} + +impl BlockCipher for AES { + fn encrypt(&mut self, plaintext: &Vec) -> Result, RvError> { + common_aes_encrypt!(self, plaintext); + } + + fn encrypt_update(&mut self, plaintext: Vec, ciphertext: &mut Vec + ) -> Result { + common_aes_encrypt_update!(self, plaintext, ciphertext); + } + + fn encrypt_final(&mut self, ciphertext: &mut Vec + ) -> Result { + common_aes_encrypt_final!(self, ciphertext); + } + + fn decrypt(&mut self, ciphertext: &Vec) -> Result, RvError> { + common_aes_decrypt!(self, ciphertext); + } + + fn decrypt_update(&mut self, ciphertext: Vec, plaintext: &mut Vec + ) -> Result { + common_aes_decrypt_update!(self, ciphertext, plaintext); + } + + fn decrypt_final(&mut self, plaintext: &mut Vec + ) -> Result { + common_aes_decrypt_final!(self, plaintext); + } +} + +impl AEADCipher for AES { + fn set_aad(&mut self, aad: Vec) -> Result<(), RvError> { + common_aes_set_aad!(self, aad); + } + fn get_tag(&mut self) -> Result, RvError> { + common_aes_get_tag!(self); + } + fn set_tag(&mut self, tag: Vec) -> Result<(), RvError> { + common_aes_set_tag!(self, tag); + } +} + +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; From c64af3ae0ab9e1f4d19fd3d0f1e14ad13d6f1986 Mon Sep 17 00:00:00 2001 From: Xudong Guo Date: Mon, 8 Jul 2024 11:20:41 +0800 Subject: [PATCH 3/5] Fix netlify.toml config (#69) * fix netlify.toml --- docs/netlify.toml | 3 --- netlify.toml | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 docs/netlify.toml create mode 100644 netlify.toml diff --git a/docs/netlify.toml b/docs/netlify.toml deleted file mode 100644 index e6d5373..0000000 --- a/docs/netlify.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build] - base = "/docs" - ignore = "git diff --quiet HEAD^ HEAD /docs" \ No newline at end of file diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 0000000..04eafb0 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,4 @@ +[build] + base = "docs" + publish = "build" + ignore = "git diff --quiet $COMMIT_REF $CACHED_COMMIT_REF -- ../docs/ . ../netlify.toml" \ No newline at end of file From 44ec9a8c47f50f6845ed6fad6cdcdd332e59e715 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Thu, 1 Aug 2024 14:36:41 +0800 Subject: [PATCH 4/5] Use feature in cfg instead of self-defined label This is prepared for later usage of crypto adaptor module and can also suppress compilation warnings when using latest rustc compiler. --- src/modules/pki/path_keys.rs | 4 ++-- src/utils/key.rs | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/modules/pki/path_keys.rs b/src/modules/pki/path_keys.rs index 734810c..f290727 100644 --- a/src/modules/pki/path_keys.rs +++ b/src/modules/pki/path_keys.rs @@ -314,9 +314,9 @@ impl PkiBackendInner { }; let iv_value = req.get_data_or_default("iv")?; let is_iv_required = matches!(key_type, "aes-gcm" | "aes-cbc" | "sm4-gcm" | "sm4-ccm"); - #[cfg(tongsuo)] + #[cfg(feature = "crypto_adaptor_tongsuo")] let is_valid_key_type = matches!(key_type, "aes-gcm" | "aes-cbc" | "aes-ecb" | "sm4-gcm" | "sm4-ccm"); - #[cfg(not(tongsuo))] + #[cfg(not(feature = "crypto_adaptor_tongsuo"))] let is_valid_key_type = matches!(key_type, "aes-gcm" | "aes-cbc" | "aes-ecb"); // Check if the key type is valid, if not return an error. diff --git a/src/utils/key.rs b/src/utils/key.rs index 0348345..8e20a8d 100644 --- a/src/utils/key.rs +++ b/src/utils/key.rs @@ -51,6 +51,7 @@ fn key_bits_default(key_type: &str) -> u32 { } } +// TODO: this function needs to be refactored to use crypto adaptors. fn cipher_from_key_type_and_bits(key_type: &str, bits: u32) -> Result { match (key_type, bits) { ("aes-gcm", 128) => Ok(Cipher::aes_128_gcm()), @@ -62,9 +63,9 @@ fn cipher_from_key_type_and_bits(key_type: &str, bits: u32) -> Result Ok(Cipher::aes_128_ecb()), ("aes-ecb", 192) => Ok(Cipher::aes_192_ecb()), ("aes-ecb", 256) => Ok(Cipher::aes_256_ecb()), - #[cfg(tongsuo)] + #[cfg(feature = "crypto_adaptor_tongsuo")] ("sm4-gcm", 128) => Ok(Cipher::sm4_gcm()), - #[cfg(tongsuo)] + #[cfg(feature = "crypto_adaptor_tongsuo")] ("sm4-ccm", 128) => Ok(Cipher::sm4_ccm()), _ => Err(RvError::ErrPkiKeyBitsInvalid), } @@ -104,7 +105,7 @@ impl KeyBundle { let ec_key = EcKey::generate(&ec_group)?; PKey::from_ec_key(ec_key)?.private_key_to_pem_pkcs8()? }, - #[cfg(tongsuo)] + #[cfg(feature = "crypto_adaptor_tongsuo")] "sm2" => { self.bits = 256; let ec_group = EcGroup::from_curve_name(Nid::SM2)?; @@ -114,7 +115,7 @@ impl KeyBundle { "aes-gcm" | "aes-cbc" | "aes-ecb" | "sm4-gcm" | "sm4-ccm" => { let _ = cipher_from_key_type_and_bits(self.key_type.as_str(), self.bits)?; - #[cfg(not(tongsuo))] + #[cfg(not(feature = "crypto_adaptor_tongsuo"))] if self.key_type.starts_with("sm4-") { return Err(RvError::ErrPkiKeyTypeInvalid); } @@ -148,7 +149,7 @@ impl KeyBundle { pub fn sign(&self, data: &[u8]) -> Result, RvError> { let digest = match self.key_type.as_str() { "rsa" | "ec" => MessageDigest::sha256(), - #[cfg(tongsuo)] + #[cfg(feature = "crypto_adaptor_tongsuo")] "sm2" => MessageDigest::sm3(), _ => return Err(RvError::ErrPkiKeyOperationInvalid), }; @@ -167,7 +168,7 @@ impl KeyBundle { pub fn verify(&self, data: &[u8], signature: &[u8]) -> Result { let digest = match self.key_type.as_str() { "rsa" | "ec" => MessageDigest::sha256(), - #[cfg(tongsuo)] + #[cfg(feature = "crypto_adaptor_tongsuo")] "sm2" => MessageDigest::sm3(), _ => return Err(RvError::ErrPkiKeyOperationInvalid), }; @@ -343,7 +344,7 @@ mod test { } #[test] - #[cfg(tongsuo)] + #[cfg(feature = "crypto_adaptor_tongsuo")] fn test_sm2_key_operation() { let mut key_bundle = KeyBundle::new("sm2", "sm2", 256); test_key_sign_verify(&mut key_bundle); @@ -384,7 +385,7 @@ mod test { } #[test] - #[cfg(tongsuo)] + #[cfg(feature = "crypto_adaptor_tongsuo")] fn test_sm4_key_operation() { // test sm4-gcm let mut key_bundle = KeyBundle::new("sm4-gcm-128", "sm4-gcm", 128); From a9958d21173d7d58a82e353b763508bd1b18790c Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Thu, 1 Aug 2024 17:51:47 +0800 Subject: [PATCH 5/5] Refactor some code and use decent Rust slang --- src/modules/crypto/crypto_adaptors/common.rs | 148 ++++++++++-------- .../crypto/crypto_adaptors/openssl_adaptor.rs | 1 + .../crypto/crypto_adaptors/tongsuo_adaptor.rs | 47 +++--- 3 files changed, 106 insertions(+), 90 deletions(-) diff --git a/src/modules/crypto/crypto_adaptors/common.rs b/src/modules/crypto/crypto_adaptors/common.rs index 2e5b38b..e93f794 100644 --- a/src/modules/crypto/crypto_adaptors/common.rs +++ b/src/modules/crypto/crypto_adaptors/common.rs @@ -1,6 +1,10 @@ //! This module contains some common functions used by openssl and tongsuo adaptors. //! Functions in this module SHOULD NOT be used directly by applications. +use crate::modules::RvError; +use crate::modules::crypto::{ AESKeySize, CipherMode }; +use openssl::symm::Cipher; + macro_rules! common_aes_set_aad { ($aes: expr, $aad: expr) => { $aes.aad = Some($aad.clone()); @@ -10,7 +14,7 @@ macro_rules! common_aes_set_aad { macro_rules! common_aes_get_tag { ($aes: expr) => { - if $aes.tag == None { + if $aes.tag.is_none() { return Err(RvError::ErrCryptoCipherNoTag); } return Ok($aes.tag.clone().unwrap()); @@ -55,27 +59,27 @@ macro_rules! common_aes_new { // 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(); + rand_priv_bytes(&mut buf)?; aes_key = buf.to_vec(); - rand_priv_bytes(&mut buf2).unwrap(); + rand_priv_bytes(&mut buf2)?; 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(); + rand_priv_bytes(&mut buf)?; aes_key = buf.to_vec(); - rand_priv_bytes(&mut buf2).unwrap(); + rand_priv_bytes(&mut buf2)?; 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(); + rand_priv_bytes(&mut buf)?; aes_key = buf.to_vec(); - rand_priv_bytes(&mut buf2).unwrap(); + rand_priv_bytes(&mut buf2)?; aes_iv = buf2.to_vec(); }, } @@ -100,36 +104,46 @@ macro_rules! common_get_key_iv { } } +pub fn common_internal_get_cipher_alg(alg: &(AESKeySize, CipherMode)) -> +Result<(Cipher, bool), RvError> { + let cipher; + let mut aead = false; + + match 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), + } + + return Ok((cipher, aead)); +} + macro_rules! common_aes_encrypt { ($aes: expr, $plaintext: expr) => { let cipher; - let mut aead = false; + let aead: bool; - match $aes.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), - } + (cipher, aead) = common::common_internal_get_cipher_alg(&$aes.alg)?; if aead == false { let ciphertext = encrypt( cipher, &$aes.key, Some(&$aes.iv), - $plaintext).unwrap(); + $plaintext)?; return Ok(ciphertext.to_vec()); } else { // aes_xxx_gcm's tag is at most 16-bytes long. @@ -141,7 +155,7 @@ macro_rules! common_aes_encrypt { &$aes.aad.clone().unwrap(), $plaintext, tag - ).unwrap(); + )?; $aes.tag = Some(tag.to_vec()); return Ok(ciphertext.to_vec()); } @@ -150,28 +164,28 @@ macro_rules! common_aes_encrypt { macro_rules! common_aes_decrypt { ($aes: expr, $ciphertext: expr) => { - match $aes.alg { - (AESKeySize::AES128, CipherMode::CBC) => { - let plaintext = decrypt( - Cipher::aes_128_cbc(), - &$aes.key, - Some(&$aes.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(), - &$aes.key, - Some(&$aes.iv), - &$aes.aad.clone().unwrap(), - $ciphertext, - &$aes.tag.clone().unwrap() - ).unwrap(); - return Ok(plaintext.to_vec()); - } - _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } + let cipher; + let aead: bool; + + (cipher, aead) = common::common_internal_get_cipher_alg(&$aes.alg)?; + + if aead == false { + let plaintext = decrypt( + cipher, + &$aes.key, + Some(&$aes.iv), + $ciphertext)?; + return Ok(plaintext.to_vec()); + } else { + let plaintext = decrypt_aead( + cipher, + &$aes.key, + Some(&$aes.iv), + &$aes.aad.clone().unwrap(), + $ciphertext, + &$aes.tag.clone().unwrap() + )?; + return Ok(plaintext.to_vec()); } } } @@ -190,14 +204,14 @@ macro_rules! common_aes_encrypt_update { _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } } - if let None = $aes.ctx { + if $aes.ctx.is_none() { // init adaptor ctx if it's not inited. let encrypter = Crypter::new( cipher, Mode::Encrypt, &$aes.key, Some(&$aes.iv) - ).unwrap(); + )?; let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; $aes.ctx = Some(adaptor_ctx); @@ -207,7 +221,7 @@ macro_rules! common_aes_encrypt_update { // set additional authenticated data before doing real jobs. if $aes.ctx.as_mut().unwrap().aad_set == false { if let Some(aad) = &$aes.aad { - $aes.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + $aes.ctx.as_mut().unwrap().ctx.aad_update(aad)?; $aes.ctx.as_mut().unwrap().aad_set = true; } } @@ -218,7 +232,7 @@ macro_rules! common_aes_encrypt_update { // 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 = $aes.ctx.as_mut().unwrap().ctx.update(&$plaintext, &mut $ciphertext[..]).unwrap(); + let count = $aes.ctx.as_mut().unwrap().ctx.update(&$plaintext, &mut $ciphertext[..])?; return Ok(count); } @@ -227,15 +241,15 @@ macro_rules! common_aes_encrypt_update { macro_rules! common_aes_encrypt_final { ($aes: expr, $ciphertext: expr) => { // Unlike encrypt_update() function, we don't do auto-initialization here. - if let None = $aes.ctx { + if $aes.ctx.is_none() { return Err(RvError::ErrCryptoCipherNotInited); } - let count = $aes.ctx.as_mut().unwrap().ctx.finalize($ciphertext).unwrap(); + let count = $aes.ctx.as_mut().unwrap().ctx.finalize($ciphertext)?; if $aes.alg.1 == CipherMode::GCM { // set tag for caller to obtain. - if let Some(_) = $aes.tag { + if $aes.tag.is_some() { // tag should not be set before encrypt_final() is called. return Err(RvError::ErrCryptoCipherAEADTagPresent); } @@ -243,7 +257,7 @@ macro_rules! common_aes_encrypt_final { // 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]; - $aes.ctx.as_mut().unwrap().ctx.get_tag(&mut tag).unwrap(); + $aes.ctx.as_mut().unwrap().ctx.get_tag(&mut tag)?; $aes.tag = Some(tag); } @@ -265,14 +279,14 @@ macro_rules! common_aes_decrypt_update { _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } } - if let None = $aes.ctx { + if $aes.ctx.is_none() { // init adaptor ctx if it's not inited. let encrypter = Crypter::new( cipher, Mode::Decrypt, &$aes.key, Some(&$aes.iv) - ).unwrap(); + )?; let adaptor_ctx = AdaptorCTX { ctx: encrypter, tag_set: false, aad_set: false }; $aes.ctx = Some(adaptor_ctx); @@ -282,7 +296,7 @@ macro_rules! common_aes_decrypt_update { if $aes.alg.1 == CipherMode::GCM { if $aes.ctx.as_mut().unwrap().aad_set == false { if let Some(aad) = &$aes.aad { - $aes.ctx.as_mut().unwrap().ctx.aad_update(aad).unwrap(); + $aes.ctx.as_mut().unwrap().ctx.aad_update(aad)?; $aes.ctx.as_mut().unwrap().aad_set = true; } } @@ -294,9 +308,9 @@ macro_rules! common_aes_decrypt_update { Ok(count) => { return Ok(count); } Err(err_stack) => { let errs = err_stack.errors(); - println!("{}", errs.len()); + log::error!("{}", errs.len()); for err in errs.iter() { - println!("{:?}", err.reason()); + log::error!("{:?}", err.reason()); } return Err(RvError::ErrCryptoCipherUpdateFailed); } @@ -307,7 +321,7 @@ macro_rules! common_aes_decrypt_update { macro_rules! common_aes_decrypt_final { ($aes: expr, $plaintext: expr) => { // Unlike decrypt_update() function, we don't do auto-initialization here. - if let None = $aes.ctx { + if $aes.ctx.is_none() { return Err(RvError::ErrCryptoCipherNotInited); } @@ -315,7 +329,7 @@ macro_rules! common_aes_decrypt_final { if $aes.alg.1 == CipherMode::GCM { if $aes.ctx.as_mut().unwrap().tag_set == false { if let Some(tag) = &$aes.tag { - $aes.ctx.as_mut().unwrap().ctx.set_tag(tag).unwrap(); + $aes.ctx.as_mut().unwrap().ctx.set_tag(tag)?; $aes.ctx.as_mut().unwrap().tag_set = true; } else { // if tag is missing, then return an error. @@ -328,9 +342,9 @@ macro_rules! common_aes_decrypt_final { Ok(count) => { return Ok(count); } Err(err_stack) => { let errs = err_stack.errors(); - println!("{}", errs.len()); + log::error!("{}", errs.len()); for err in errs.iter() { - println!("{:?}", err.reason()); + log::error!("{:?}", err.reason()); } return Err(RvError::ErrCryptoCipherFinalizeFailed); } diff --git a/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs index 59af645..ab0d405 100644 --- a/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs +++ b/src/modules/crypto/crypto_adaptors/openssl_adaptor.rs @@ -4,6 +4,7 @@ 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::crypto_adaptors::common; pub struct AdaptorCTX { ctx: Crypter, diff --git a/src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs b/src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs index 3906f60..97f0304 100644 --- a/src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs +++ b/src/modules/crypto/crypto_adaptors/tongsuo_adaptor.rs @@ -5,6 +5,7 @@ use crate::modules::crypto::{AEADCipher, AESKeySize, BlockCipher, CipherMode, AE use openssl::symm::{Cipher, Crypter, Mode, encrypt, encrypt_aead, decrypt, decrypt_aead}; use openssl::rand::rand_priv_bytes; use crate::modules::crypto::SM4; +use crate::modules::crypto::crypto_adaptors::common; pub struct AdaptorCTX { ctx: Crypter, @@ -118,9 +119,9 @@ impl SM4 { // 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(); + rand_priv_bytes(&mut buf)?; sm4_key = buf.to_vec(); - rand_priv_bytes(&mut buf2).unwrap(); + rand_priv_bytes(&mut buf2)?; sm4_iv = buf2.to_vec(); } @@ -153,7 +154,7 @@ impl BlockCipher for SM4 { Cipher::sm4_cbc(), &self.key, Some(&self.iv), - plaintext).unwrap(); + plaintext)?; return Ok(ciphertext.to_vec()); } CipherMode::GCM => { @@ -166,7 +167,7 @@ impl BlockCipher for SM4 { &self.aad.clone().unwrap(), plaintext, tag - ).unwrap(); + )?; self.tag = Some(tag.to_vec()); return Ok(ciphertext.to_vec()); } @@ -195,7 +196,7 @@ impl BlockCipher for SM4 { 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); @@ -205,7 +206,7 @@ impl BlockCipher for SM4 { // 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().ctx.aad_update(aad)?; self.ctx.as_mut().unwrap().aad_set = true; } } @@ -216,29 +217,29 @@ impl BlockCipher for SM4 { // 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(); + let count = self.ctx.as_mut().unwrap().ctx.update(&plaintext, &mut ciphertext[..])?; 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 { + if self.ctx.is_none() { return Err(RvError::ErrCryptoCipherNotInited); } - let count = self.ctx.as_mut().unwrap().ctx.finalize(ciphertext).unwrap(); + let count = self.ctx.as_mut().unwrap().ctx.finalize(ciphertext)?; if self.mode == CipherMode::GCM { // set tag for caller to obtain. - if let Some(_) = self.tag { + if self.tag.is_some() { // 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.ctx.as_mut().unwrap().ctx.get_tag(&mut tag)?; self.tag = Some(tag); } @@ -252,7 +253,7 @@ impl BlockCipher for SM4 { Cipher::sm4_cbc(), &self.key, Some(&self.iv), - ciphertext).unwrap(); + ciphertext)?; return Ok(plaintext.to_vec()); } CipherMode::GCM => { @@ -264,7 +265,7 @@ impl BlockCipher for SM4 { &self.aad.clone().unwrap(), ciphertext, &self.tag.clone().unwrap() - ).unwrap(); + )?; return Ok(plaintext.to_vec()); } _ => Err(RvError::ErrCryptoCipherOPNotSupported), @@ -284,14 +285,14 @@ impl BlockCipher for SM4 { _ => { return Err(RvError::ErrCryptoCipherOPNotSupported); } } - if let None = self.ctx { + if self.ctx.is_none() { // 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); @@ -301,7 +302,7 @@ impl BlockCipher for SM4 { 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().ctx.aad_update(aad)?; self.ctx.as_mut().unwrap().aad_set = true; } } @@ -313,9 +314,9 @@ impl BlockCipher for SM4 { Ok(count) => { return Ok(count); } Err(err_stack) => { let errs = err_stack.errors(); - println!("{}", errs.len()); + log::error!("{}", errs.len()); for err in errs.iter() { - println!("{:?}", err.reason()); + log::error!("{:?}", err.reason()); } Err(RvError::ErrCryptoCipherUpdateFailed) } @@ -325,7 +326,7 @@ impl BlockCipher for SM4 { 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 { + if self.ctx.is_none() { return Err(RvError::ErrCryptoCipherNotInited); } @@ -333,7 +334,7 @@ impl BlockCipher for SM4 { 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().ctx.set_tag(tag)?; self.ctx.as_mut().unwrap().tag_set = true; } else { // if tag is missing, then return an error. @@ -346,9 +347,9 @@ impl BlockCipher for SM4 { Ok(count) => { return Ok(count); } Err(err_stack) => { let errs = err_stack.errors(); - println!("{}", errs.len()); + log::error!("{}", errs.len()); for err in errs.iter() { - println!("{:?}", err.reason()); + log::error!("{:?}", err.reason()); } Err(RvError::ErrCryptoCipherFinalizeFailed) } @@ -362,7 +363,7 @@ impl AEADCipher for SM4 { Ok(()) } fn get_tag(&mut self) -> Result, RvError> { - if self.tag == None { + if self.tag.is_none() { return Err(RvError::ErrCryptoCipherNoTag); } Ok(self.tag.clone().unwrap())