Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cas-lib"
version = "0.2.84"
version = "0.2.85"
edition = "2021"
description = "A function wrapper layer for RustCrypto and Dalek-Cryptography. Intended to be used in FFI situations with a global heap deallactor at the top level project."
license = "Apache-2.0"
Expand All @@ -13,6 +13,7 @@ crate-type = ["lib"]

[dependencies]
aes-gcm = "0.10.3"
aes-gcm-siv = "0.11.1"
argon2 = "0.5.2"
bcrypt = "0.18.0"
blake2 = "0.10.6"
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod key_exchange {

pub mod symmetric {
pub mod aes;
pub mod aes_gcm_siv;
pub mod chacha20poly1305;
pub mod cas_symmetric_encryption;
}
Expand Down
154 changes: 154 additions & 0 deletions src/symmetric/aes_gcm_siv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use aes_gcm_siv::{
aead::{Aead, KeyInit},
Aes128GcmSiv, Aes256GcmSiv, Key, Nonce,
};
use hkdf::Hkdf;
use rand::{rngs::OsRng, RngCore};
use sha2::Sha256;

use crate::error::{CasError, CasResult};

use super::cas_symmetric_encryption::{CASAES128SIVEncryption, CASAES256SIVEncryption};

const AES_SIV_NONCE_LEN: usize = 12;
const AES128_KEY_LEN: usize = 16;
const AES256_KEY_LEN: usize = 32;

pub struct CASAES128SIV;
pub struct CASAES256SIV;

impl CASAES256SIVEncryption for CASAES256SIV {

/// Generates an AES-256-GCM-SIV key from a vector.
/// Returns an error if the input is not 32 bytes long.
fn key_from_vec(key_slice: Vec<u8>) -> CasResult<Vec<u8>> {
if key_slice.len() != AES256_KEY_LEN {
return Err(CasError::InvalidKey);
}
let key = Key::<Aes256GcmSiv>::from_slice(key_slice.as_slice());
Ok(key.to_vec())
}

/// Generates an AES-256-GCM-SIV 32-byte key
fn generate_key() -> Vec<u8> {
let mut os_rng = OsRng;
Aes256GcmSiv::generate_key(&mut os_rng).to_vec()
}

/// Encrypts with AES-256-GCM-SIV taking an aes_key and nonce
fn encrypt_plaintext(aes_key: Vec<u8>, nonce: Vec<u8>, plaintext: Vec<u8>) -> CasResult<Vec<u8>> {
if aes_key.len() != AES256_KEY_LEN {
return Err(CasError::InvalidKey);
}
if nonce.len() != AES_SIV_NONCE_LEN {
return Err(CasError::InvalidNonce);
}
let key = Key::<Aes256GcmSiv>::from_slice(aes_key.as_slice());
let cipher = Aes256GcmSiv::new(key);
let nonce = Nonce::from_slice(nonce.as_slice());
cipher
.encrypt(nonce, plaintext.as_ref())
.map_err(|_| CasError::EncryptionFailed)
}

/// Decrypts with AES-256-GCM-SIV taking an aes_key and nonce
fn decrypt_ciphertext(aes_key: Vec<u8>, nonce: Vec<u8>, ciphertext: Vec<u8>) -> CasResult<Vec<u8>> {
if aes_key.len() != AES256_KEY_LEN {
return Err(CasError::InvalidKey);
}
if nonce.len() != AES_SIV_NONCE_LEN {
return Err(CasError::InvalidNonce);
}
let key = Key::<Aes256GcmSiv>::from_slice(aes_key.as_slice());
let cipher = Aes256GcmSiv::new(key);
let nonce = Nonce::from_slice(nonce.as_slice());
cipher
.decrypt(nonce, ciphertext.as_ref())
.map_err(|_| CasError::DecryptionFailed)
}

/// Creates an AES-256-GCM-SIV 32-byte key from an X25519 shared secret
fn key_from_x25519_shared_secret(shared_secret: Vec<u8>) -> CasResult<Vec<u8>> {
let hk = Hkdf::<Sha256>::new(None, &shared_secret);
let mut aes_key: Box<[u8; 32]> = Box::new([0u8; 32]);
hk.expand(b"aes key", &mut *aes_key)
.map_err(|_| CasError::KeyGenerationFailed)?;
Ok(aes_key.to_vec())
}

/// Generates an AES-GCM-SIV nonce
fn generate_nonce() -> Vec<u8> {
let mut os_rng = OsRng;
let mut nonce = [0u8; AES_SIV_NONCE_LEN];
os_rng.fill_bytes(&mut nonce);
nonce.to_vec()
}
}

impl CASAES128SIVEncryption for CASAES128SIV {

/// Generates an AES-128-GCM-SIV key from a vector.
/// Returns an error if the input is not 16 bytes long.
fn key_from_vec(key_slice: Vec<u8>) -> CasResult<Vec<u8>> {
if key_slice.len() != AES128_KEY_LEN {
return Err(CasError::InvalidKey);
}
let key = Key::<Aes128GcmSiv>::from_slice(key_slice.as_slice());
Ok(key.to_vec())
}

/// Generates an AES-128-GCM-SIV 16-byte key
fn generate_key() -> Vec<u8> {
let mut os_rng = OsRng;
Aes128GcmSiv::generate_key(&mut os_rng).to_vec()
}

/// Encrypts with AES-128-GCM-SIV taking an aes_key and nonce
fn encrypt_plaintext(aes_key: Vec<u8>, nonce: Vec<u8>, plaintext: Vec<u8>) -> CasResult<Vec<u8>> {
if aes_key.len() != AES128_KEY_LEN {
return Err(CasError::InvalidKey);
}
if nonce.len() != AES_SIV_NONCE_LEN {
return Err(CasError::InvalidNonce);
}
let key = Key::<Aes128GcmSiv>::from_slice(aes_key.as_slice());
let cipher = Aes128GcmSiv::new(key);
let nonce = Nonce::from_slice(nonce.as_slice());
cipher
.encrypt(nonce, plaintext.as_ref())
.map_err(|_| CasError::EncryptionFailed)
}

/// Decrypts with AES-128-GCM-SIV taking an aes_key and nonce
fn decrypt_ciphertext(aes_key: Vec<u8>, nonce: Vec<u8>, ciphertext: Vec<u8>) -> CasResult<Vec<u8>> {
if aes_key.len() != AES128_KEY_LEN {
return Err(CasError::InvalidKey);
}
if nonce.len() != AES_SIV_NONCE_LEN {
return Err(CasError::InvalidNonce);
}
let key = Key::<Aes128GcmSiv>::from_slice(aes_key.as_slice());
let cipher = Aes128GcmSiv::new(key);
let nonce = Nonce::from_slice(nonce.as_slice());
cipher
.decrypt(nonce, ciphertext.as_ref())
.map_err(|_| CasError::DecryptionFailed)
}

/// Creates an AES-128-GCM-SIV 16-byte key from an X25519 shared secret
fn key_from_x25519_shared_secret(shared_secret: Vec<u8>) -> CasResult<Vec<u8>> {
let hk = Hkdf::<Sha256>::new(None, &shared_secret);
let mut aes_key = Box::new([0u8; 16]);
hk.expand(b"aes key", &mut *aes_key)
.map_err(|_| CasError::KeyGenerationFailed)?;
Ok(aes_key.to_vec())
}

/// Generates an AES-GCM-SIV nonce
fn generate_nonce() -> Vec<u8> {
let mut os_rng = OsRng;
let mut nonce = [0u8; AES_SIV_NONCE_LEN];
os_rng.fill_bytes(&mut nonce);
nonce.to_vec()
}
}
18 changes: 18 additions & 0 deletions src/symmetric/cas_symmetric_encryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ pub trait CASAES128Encryption {
fn key_from_vec(key_slice: Vec<u8>) -> CasResult<Vec<u8>>;
}

pub trait CASAES256SIVEncryption {
fn generate_key() -> Vec<u8>;
fn encrypt_plaintext(aes_key: Vec<u8>, nonce: Vec<u8>, plaintext: Vec<u8>) -> CasResult<Vec<u8>>;
fn decrypt_ciphertext(aes_key: Vec<u8>, nonce: Vec<u8>, ciphertext: Vec<u8>) -> CasResult<Vec<u8>>;
fn key_from_x25519_shared_secret(shared_secret: Vec<u8>) -> CasResult<Vec<u8>>;
fn generate_nonce() -> Vec<u8>;
fn key_from_vec(key_slice: Vec<u8>) -> CasResult<Vec<u8>>;
}

pub trait CASAES128SIVEncryption {
fn generate_key() -> Vec<u8>;
fn encrypt_plaintext(aes_key: Vec<u8>, nonce: Vec<u8>, plaintext: Vec<u8>) -> CasResult<Vec<u8>>;
fn decrypt_ciphertext(aes_key: Vec<u8>, nonce: Vec<u8>, ciphertext: Vec<u8>) -> CasResult<Vec<u8>>;
fn key_from_x25519_shared_secret(shared_secret: Vec<u8>) -> CasResult<Vec<u8>>;
fn generate_nonce() -> Vec<u8>;
fn key_from_vec(key_slice: Vec<u8>) -> CasResult<Vec<u8>>;
}

pub trait Chacha20Poly1305Encryption {
fn generate_key() -> Vec<u8>;
fn encrypt_plaintext(aes_key: Vec<u8>, nonce: Vec<u8>, plaintext: Vec<u8>) -> CasResult<Vec<u8>>;
Expand Down
Loading
Loading