use std::{convert::TryInto, ffi::CString};
use crate::{IOVec, IOVecTrait, KcapiError, KcapiResult, VMSplice, BITS_PER_BYTE, INIT_AIO};
const AES_BLOCKSIZE_BITS: usize = 128;
const AES128_KEYSIZE_BITS: usize = 128;
const AES192_KEYSIZE_BITS: usize = 192;
const AES256_KEYSIZE_BITS: usize = 256;
const SM4_BLOCKSIZE_BITS: usize = 128;
const SM4_KEYSIZE_BITS: usize = 128;
pub const AES_BLOCKSIZE: usize = AES_BLOCKSIZE_BITS / BITS_PER_BYTE;
pub const AES128_KEYSIZE: usize = AES128_KEYSIZE_BITS / BITS_PER_BYTE;
pub const AES192_KEYSIZE: usize = AES192_KEYSIZE_BITS / BITS_PER_BYTE;
pub const AES256_KEYSIZE: usize = AES256_KEYSIZE_BITS / BITS_PER_BYTE;
pub const SM4_BLOCKSIZE: usize = SM4_BLOCKSIZE_BITS / BITS_PER_BYTE;
pub const SM4_KEYSIZE: usize = SM4_KEYSIZE_BITS / BITS_PER_BYTE;
#[derive(Debug, Clone, Eq, PartialEq)]
enum SKCipherMode {
Decrypt = 0,
Encrypt,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct KcapiSKCipher {
handle: *mut kcapi_sys::kcapi_handle,
iv: Vec<u8>,
key: Vec<u8>,
pub algorithm: String,
pub blocksize: usize,
pub flags: u32,
pub ivsize: usize,
invec: Vec<Vec<u8>>,
stream_mode: SKCipherMode,
}
impl KcapiSKCipher {
pub fn new(algorithm: &str, flags: u32) -> KcapiResult<Self> {
let mut handle = Box::into_raw(Box::new(crate::kcapi_handle { _unused: [0u8; 0] }))
as *mut kcapi_sys::kcapi_handle;
let blocksize: usize;
let ivsize: usize;
let alg = CString::new(algorithm).expect("Failed to create CString");
unsafe {
let ret = kcapi_sys::kcapi_cipher_init(&mut handle as *mut _, alg.as_ptr(), flags);
if ret < 0 {
return Err(KcapiError {
code: ret,
message: format!(
"Failed to initialize symmetric key handle for algorithm '{}'",
algorithm
),
});
}
blocksize = kcapi_sys::kcapi_cipher_blocksize(handle)
.try_into()
.expect("Failed to convert u32 into usize");
if blocksize == 0 {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Failed to obtain block size for algorithm '{}'", algorithm),
});
}
ivsize = kcapi_sys::kcapi_cipher_ivsize(handle)
.try_into()
.expect("Failed to convert u32 into usize");
}
let key: Vec<u8> = Vec::new();
let iv: Vec<u8> = Vec::new();
let invec: Vec<Vec<u8>> = Vec::new();
Ok(KcapiSKCipher {
handle,
key,
iv,
algorithm: algorithm.to_string(),
blocksize,
flags,
ivsize,
stream_mode: SKCipherMode::Decrypt,
invec,
})
}
pub fn setkey(&mut self, key: Vec<u8>) -> KcapiResult<()> {
unsafe {
let ret = kcapi_sys::kcapi_cipher_setkey(self.handle, key.as_ptr(), key.len() as u32);
if ret < 0 {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Failed to set key for algorithm '{}'", self.algorithm),
});
}
self.key = key
}
Ok(())
}
fn check_skcipher_input(&self, iv: &[u8], input: &[u8]) -> KcapiResult<()> {
if self.key.is_empty() {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("No key has been set for algorithm '{}'", self.algorithm,),
});
}
if iv.len() != self.ivsize {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Invald IV Size for algorithm '{}'", self.algorithm),
});
}
if !input.len().is_multiple_of(self.blocksize) {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Input for algorithm {} should be a multiple of block size {}",
self.algorithm, self.blocksize,
),
});
}
Ok(())
}
pub fn encrypt(&self, pt: Vec<u8>, iv: Vec<u8>, access: u32) -> KcapiResult<Vec<u8>> {
self.check_skcipher_input(&iv, &pt)?;
let mut ct = vec![0u8; pt.len()];
unsafe {
let ret = kcapi_sys::kcapi_cipher_encrypt(
self.handle,
pt.as_ptr(),
pt.len() as kcapi_sys::size_t,
iv.as_ptr(),
ct.as_mut_ptr(),
ct.len() as kcapi_sys::size_t,
access as ::std::os::raw::c_int,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!("Encryption failed for algorithm '{}'", self.algorithm,),
});
}
}
Ok(ct)
}
pub fn encrypt_aio(
&self,
pt: Vec<Vec<u8>>,
iv: Vec<u8>,
access: u32,
) -> KcapiResult<Vec<Vec<u8>>> {
if iv.len() != self.ivsize {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Invalid IV size for algorithm '{}'", self.algorithm),
});
}
let mut iniov = IOVec::new(pt.clone())?;
let mut outiov = IOVec::new(pt)?;
unsafe {
let ret = kcapi_sys::kcapi_cipher_encrypt_aio(
self.handle,
iniov.iovec.as_mut_ptr(),
outiov.iovec.as_mut_ptr(),
iniov.len() as kcapi_sys::size_t,
iv.as_ptr(),
access as ::std::os::raw::c_int,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!("AIO Encryption failed for algorithm '{}'", self.algorithm),
});
}
}
Ok(outiov.data)
}
pub fn decrypt(&self, ct: Vec<u8>, iv: Vec<u8>, access: u32) -> KcapiResult<Vec<u8>> {
self.check_skcipher_input(&iv, &ct)?;
let mut pt = vec![0u8; ct.len()];
unsafe {
let ret = kcapi_sys::kcapi_cipher_decrypt(
self.handle,
ct.as_ptr(),
ct.len() as kcapi_sys::size_t,
iv.as_ptr(),
pt.as_mut_ptr(),
pt.len() as kcapi_sys::size_t,
access as ::std::os::raw::c_int,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!("Decryption failed for algorithm '{}'", self.algorithm,),
});
}
}
Ok(pt)
}
pub fn decrypt_aio(
&self,
ct: Vec<Vec<u8>>,
iv: Vec<u8>,
access: u32,
) -> KcapiResult<Vec<Vec<u8>>> {
if iv.len() != self.ivsize {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Invalid IV size for algorithm '{}'", self.algorithm),
});
}
let mut iniov = IOVec::new(ct.clone())?;
let mut outiov = IOVec::new(ct)?;
unsafe {
let ret = kcapi_sys::kcapi_cipher_decrypt_aio(
self.handle,
iniov.iovec.as_mut_ptr(),
outiov.iovec.as_mut_ptr(),
iniov.len() as kcapi_sys::size_t,
iv.as_ptr(),
access as ::std::os::raw::c_int,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!("AIO Decryption failed for algorithm '{}'", self.algorithm,),
});
}
}
Ok(outiov.data)
}
pub fn new_enc_stream(
algorithm: &str,
key: Vec<u8>,
iv: Vec<u8>,
pt: Vec<Vec<u8>>,
) -> KcapiResult<Self> {
let mut cipher = Self::new(algorithm, !INIT_AIO)?;
cipher.setkey(key)?;
if iv.len() != cipher.ivsize {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Invalid IV of length {}, Expected IV of length {} for algorithm '{}'",
iv.len(),
cipher.ivsize,
algorithm,
),
});
}
cipher.iv = iv.clone();
cipher.stream_mode = SKCipherMode::Encrypt;
if pt.is_empty() {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Invalid input vector of length 0 for algorithm '{}'",
cipher.algorithm,
),
});
}
let mut iov = IOVec::new(pt)?;
unsafe {
let ret = kcapi_sys::kcapi_cipher_stream_init_enc(
cipher.handle,
iv.as_ptr(),
iov.iovec.as_mut_ptr(),
iov.len() as kcapi_sys::size_t,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!(
"Failed to initialize stream cipher operation for algorithm '{}'",
algorithm,
),
});
}
}
cipher.invec.extend_from_slice(iov.data.as_slice());
Ok(cipher)
}
pub fn new_dec_stream(
algorithm: &str,
key: Vec<u8>,
iv: Vec<u8>,
ct: Vec<Vec<u8>>,
) -> KcapiResult<Self> {
let mut cipher = Self::new(algorithm, !INIT_AIO)?;
cipher.setkey(key)?;
if iv.len() != cipher.ivsize {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Invalid IV of length {}, Expected IV of length {} for algorithm '{}'",
iv.len(),
cipher.ivsize,
algorithm,
),
});
}
cipher.iv = iv.clone();
cipher.stream_mode = SKCipherMode::Decrypt;
if ct.is_empty() {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Invalid input vector of length 0 for algorithm '{}'",
cipher.algorithm,
),
});
}
let mut iov = IOVec::new(ct)?;
unsafe {
let ret = kcapi_sys::kcapi_cipher_stream_init_dec(
cipher.handle,
iv.as_ptr(),
iov.iovec.as_mut_ptr(),
iov.len() as kcapi_sys::size_t,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!(
"Failed to initialize stream cipher operation for algorithm '{}'",
algorithm,
),
});
}
}
cipher.invec.extend_from_slice(iov.data.as_slice());
Ok(cipher)
}
pub fn stream_update(&mut self, input: Vec<Vec<u8>>) -> KcapiResult<()> {
let mut iov = IOVec::new(input)?;
unsafe {
let ret = kcapi_sys::kcapi_cipher_stream_update(
self.handle,
iov.iovec.as_mut_ptr(),
iov.len() as kcapi_sys::size_t,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!(
"Failed to update data stream for algorithm '{}'",
self.algorithm,
),
});
}
}
self.invec.extend_from_slice(iov.data.as_slice());
Ok(())
}
pub fn stream_update_last(&mut self, input: Vec<Vec<u8>>) -> KcapiResult<()> {
let mut iov = IOVec::new(input)?;
unsafe {
let ret = kcapi_sys::kcapi_cipher_stream_update_last(
self.handle,
iov.iovec.as_mut_ptr(),
iov.len() as kcapi_sys::size_t,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!(
"Failed to update last data stream for algorithm '{}'",
self.algorithm,
),
});
}
}
self.invec.extend_from_slice(iov.data.as_slice());
Ok(())
}
pub fn stream_op(&mut self) -> KcapiResult<Vec<Vec<u8>>> {
let outvec = self.invec.clone();
let mut iov = IOVec::new(outvec)?;
unsafe {
let ret = kcapi_sys::kcapi_cipher_stream_op(
self.handle,
iov.iovec.as_mut_ptr(),
iov.len() as kcapi_sys::size_t,
);
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: format!(
"Failed to obtain output stream for algorithm '{}'",
self.algorithm,
),
});
}
}
self.invec = Vec::new();
Ok(iov.data)
}
}
impl Drop for KcapiSKCipher {
fn drop(&mut self) {
unsafe {
kcapi_sys::kcapi_cipher_destroy(self.handle);
}
}
}
impl VMSplice for KcapiSKCipher {
fn get_max_splicesize(&self) -> usize {
let size: usize;
unsafe {
size = kcapi_sys::kcapi_get_maxsplicesize(self.handle) as usize;
}
size
}
fn set_max_splicesize(&self, size: usize) -> KcapiResult<()> {
unsafe {
let ret =
kcapi_sys::kcapi_set_maxsplicesize(self.handle, size as ::std::os::raw::c_uint);
if ret < 0 {
return Err(KcapiError {
code: ret,
message: format!(
"Unable to set max splice size {} for algorithm {}",
size, self.algorithm,
),
});
}
}
Ok(())
}
}
pub fn encrypt(alg: &str, key: Vec<u8>, pt: Vec<u8>, iv: Vec<u8>) -> KcapiResult<Vec<u8>> {
let mut cipher = KcapiSKCipher::new(alg, !INIT_AIO)?;
cipher.setkey(key)?;
let ct = cipher.encrypt(pt, iv, crate::ACCESS_HEURISTIC)?;
Ok(ct)
}
pub fn encrypt_aio(
alg: &str,
key: Vec<u8>,
pt: Vec<Vec<u8>>,
iv: Vec<u8>,
) -> KcapiResult<Vec<Vec<u8>>> {
let mut cipher = KcapiSKCipher::new(alg, INIT_AIO)?;
cipher.setkey(key)?;
let ct = cipher.encrypt_aio(pt, iv, crate::ACCESS_HEURISTIC)?;
Ok(ct)
}
pub fn decrypt(alg: &str, key: Vec<u8>, ct: Vec<u8>, iv: Vec<u8>) -> KcapiResult<Vec<u8>> {
let mut cipher = KcapiSKCipher::new(alg, !INIT_AIO)?;
cipher.setkey(key)?;
let pt = cipher.decrypt(ct, iv, crate::ACCESS_HEURISTIC)?;
Ok(pt)
}
pub fn decrypt_aio(
alg: &str,
key: Vec<u8>,
ct: Vec<Vec<u8>>,
iv: Vec<u8>,
) -> KcapiResult<Vec<Vec<u8>>> {
let mut cipher = crate::skcipher::KcapiSKCipher::new(alg, INIT_AIO)?;
cipher.setkey(key)?;
let pt = cipher.decrypt_aio(ct, iv, crate::ACCESS_HEURISTIC)?;
Ok(pt)
}
fn check_aes_input(key: &[u8], input: &[u8]) -> KcapiResult<()> {
if !input.len().is_multiple_of(AES_BLOCKSIZE) {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Input plaintext must be a multiple of {} bytes",
AES_BLOCKSIZE
),
});
}
let keylen = key.len();
match keylen {
AES128_KEYSIZE => {}
AES192_KEYSIZE => {}
AES256_KEYSIZE => {}
_ => {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Key must be {}, {}, or {} bytes long",
AES128_KEYSIZE, AES192_KEYSIZE, AES256_KEYSIZE
),
})
}
}
Ok(())
}
fn check_sm4_input(key: &[u8], input: &[u8]) -> KcapiResult<()> {
if !input.len().is_multiple_of(SM4_BLOCKSIZE) {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!(
"Input plaintext must be a multiple of {} bytes",
SM4_BLOCKSIZE
),
});
}
if key.len() != SM4_KEYSIZE {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Key must be {} bytes long", SM4_KEYSIZE),
});
}
Ok(())
}
pub fn enc_aes_cbc(key: Vec<u8>, pt: Vec<u8>, iv: [u8; AES_BLOCKSIZE]) -> KcapiResult<Vec<u8>> {
check_aes_input(&key, &pt)?;
let mut ct = vec![0u8; pt.len()];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_cipher_enc_aes_cbc(
key.as_ptr(),
key.len()
.try_into()
.expect("Failed to convert usize to u32"),
pt.as_ptr(),
pt.len() as kcapi_sys::size_t,
iv.as_ptr(),
ct.as_mut_ptr(),
ct.len() as kcapi_sys::size_t,
);
}
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed skcipher operation".to_string(),
});
}
Ok(ct)
}
pub fn dec_aes_cbc(key: Vec<u8>, ct: Vec<u8>, iv: [u8; AES_BLOCKSIZE]) -> KcapiResult<Vec<u8>> {
check_aes_input(&key, &ct)?;
let mut pt = vec![0u8; ct.len()];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_cipher_dec_aes_cbc(
key.as_ptr(),
key.len()
.try_into()
.expect("Failed to convert usize to u32"),
ct.as_ptr(),
ct.len() as kcapi_sys::size_t,
iv.as_ptr(),
pt.as_mut_ptr(),
pt.len() as kcapi_sys::size_t,
);
}
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed skcipher operation".to_string(),
});
}
Ok(pt)
}
pub fn enc_aes_ctr(key: Vec<u8>, pt: Vec<u8>, ctr: [u8; AES_BLOCKSIZE]) -> KcapiResult<Vec<u8>> {
check_aes_input(&key, &pt)?;
let mut ct = vec![0u8; pt.len()];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_cipher_enc_aes_ctr(
key.as_ptr(),
key.len()
.try_into()
.expect("Failed to convert usize to u32"),
pt.as_ptr(),
pt.len() as kcapi_sys::size_t,
ctr.as_ptr(),
ct.as_mut_ptr(),
ct.len() as kcapi_sys::size_t,
);
}
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed skcipher operation".to_string(),
});
}
Ok(ct)
}
pub fn dec_aes_ctr(key: Vec<u8>, ct: Vec<u8>, ctr: [u8; AES_BLOCKSIZE]) -> KcapiResult<Vec<u8>> {
check_aes_input(&key, &ct)?;
let mut pt = vec![0u8; ct.len()];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_cipher_dec_aes_ctr(
key.as_ptr(),
key.len()
.try_into()
.expect("Failed to convert usize to u32"),
ct.as_ptr(),
ct.len() as kcapi_sys::size_t,
ctr.as_ptr(),
pt.as_mut_ptr(),
pt.len() as kcapi_sys::size_t,
);
}
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed skcipher operation".to_string(),
});
}
Ok(pt)
}
pub fn enc_sm4_cbc(key: Vec<u8>, pt: Vec<u8>, iv: [u8; SM4_BLOCKSIZE]) -> KcapiResult<Vec<u8>> {
check_sm4_input(&key, &pt)?;
let mut ct = vec![0u8; pt.len()];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_cipher_enc_sm4_cbc(
key.as_ptr(),
key.len()
.try_into()
.expect("Failed to convert usize to u32"),
pt.as_ptr(),
pt.len() as kcapi_sys::size_t,
iv.as_ptr(),
ct.as_mut_ptr(),
ct.len() as kcapi_sys::size_t,
);
}
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed skcipher operation".to_string(),
});
}
Ok(ct)
}
pub fn dec_sm4_cbc(key: Vec<u8>, ct: Vec<u8>, iv: [u8; SM4_BLOCKSIZE]) -> KcapiResult<Vec<u8>> {
check_sm4_input(&key, &ct)?;
let mut pt = vec![0u8; ct.len()];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_cipher_dec_sm4_cbc(
key.as_ptr(),
key.len()
.try_into()
.expect("Failed to convert usize to u32"),
ct.as_ptr(),
ct.len() as kcapi_sys::size_t,
iv.as_ptr(),
pt.as_mut_ptr(),
pt.len() as kcapi_sys::size_t,
);
}
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed skcipher operation".to_string(),
});
}
Ok(pt)
}
pub fn enc_sm4_ctr(key: Vec<u8>, pt: Vec<u8>, ctr: [u8; SM4_BLOCKSIZE]) -> KcapiResult<Vec<u8>> {
if key.len() != SM4_KEYSIZE {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Key must be {} bytes long", SM4_KEYSIZE),
});
}
let mut ct = vec![0u8; pt.len()];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_cipher_enc_sm4_ctr(
key.as_ptr(),
key.len()
.try_into()
.expect("Failed to convert usize to u32"),
pt.as_ptr(),
pt.len() as kcapi_sys::size_t,
ctr.as_ptr(),
ct.as_mut_ptr(),
ct.len() as kcapi_sys::size_t,
);
}
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed skcipher operation".to_string(),
});
}
Ok(ct)
}
pub fn dec_sm4_ctr(key: Vec<u8>, ct: Vec<u8>, ctr: [u8; SM4_BLOCKSIZE]) -> KcapiResult<Vec<u8>> {
if key.len() != SM4_KEYSIZE {
return Err(KcapiError {
code: -libc::EINVAL,
message: format!("Key must be {} bytes long", SM4_KEYSIZE),
});
}
let mut pt = vec![0u8; ct.len()];
let ret: kcapi_sys::ssize_t;
unsafe {
ret = kcapi_sys::kcapi_cipher_dec_sm4_ctr(
key.as_ptr(),
key.len()
.try_into()
.expect("Failed to convert usize to u32"),
ct.as_ptr(),
ct.len() as kcapi_sys::size_t,
ctr.as_ptr(),
pt.as_mut_ptr(),
pt.len() as kcapi_sys::size_t,
);
}
if ret < 0 {
return Err(KcapiError {
code: ret.try_into().expect("failed to convert i64 into i32"),
message: "Failed skcipher operation".to_string(),
});
}
Ok(pt)
}