#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use evercrypt_sys::evercrypt_bindings::*;
#[cfg(feature = "rust-crypto-aes")]
use crate::util::*;
#[cfg(feature = "rust-crypto-aes")]
use aes_gcm::aead::{Aead as RcAead, NewAead, Payload};
#[cfg(feature = "rust-crypto-aes")]
use aes_gcm::{aead::generic_array::GenericArray, Aes128Gcm, Aes256Gcm};
#[derive(Clone, Copy, PartialEq)]
enum OpMode {
Hacl = 0,
RustCryptoAes128 = 1,
RustCryptoAes256 = 2,
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum Mode {
Aes128Gcm = Spec_Agile_AEAD_AES128_GCM as isize,
Aes256Gcm = Spec_Agile_AEAD_AES256_GCM as isize,
Chacha20Poly1305 = Spec_Agile_AEAD_CHACHA20_POLY1305 as isize,
}
impl From<u8> for Mode {
fn from(v: u8) -> Mode {
match v {
0 => Mode::Aes128Gcm,
1 => Mode::Aes256Gcm,
2 => Mode::Chacha20Poly1305,
_ => panic!("Unknown AEAD mode {}", v),
}
}
}
impl From<Mode> for Spec_Agile_AEAD_alg {
fn from(v: Mode) -> Spec_Agile_AEAD_alg {
match v {
Mode::Aes128Gcm => Spec_Agile_AEAD_AES128_GCM as Spec_Agile_AEAD_alg,
Mode::Aes256Gcm => Spec_Agile_AEAD_AES256_GCM as Spec_Agile_AEAD_alg,
Mode::Chacha20Poly1305 => Spec_Agile_AEAD_CHACHA20_POLY1305 as Spec_Agile_AEAD_alg,
}
}
}
pub fn key_size(mode: &Mode) -> usize {
match mode {
Mode::Aes128Gcm => 16,
Mode::Aes256Gcm => 32,
Mode::Chacha20Poly1305 => 32,
}
}
#[derive(Debug, PartialEq)]
pub enum Error {
InvalidInit = 0,
InvalidAlgorithm = 1,
InvalidCiphertext = 2,
InvalidNonce = 3,
UnsupportedConfig = 4,
Encrypting = 5,
Decrypting = 6,
InvalidKeySize = 7,
}
pub struct Aead {
mode: Mode,
c_state: Option<*mut EverCrypt_AEAD_state_s>,
op_mode: OpMode,
#[allow(dead_code)] key: Vec<u8>,
}
pub type Ciphertext = Vec<u8>;
pub type Tag = [u8; 16];
pub type Nonce = [u8; 12];
pub type Aad = [u8];
fn hacl_aes_available() -> bool {
if cfg!(feature = "force-rust-crypto-aes") {
return false;
}
unsafe {
EverCrypt_AutoConfig2_has_pclmulqdq()
&& EverCrypt_AutoConfig2_has_avx()
&& EverCrypt_AutoConfig2_has_sse()
&& EverCrypt_AutoConfig2_has_movbe()
&& EverCrypt_AutoConfig2_has_aesni()
}
}
fn alg_is_aes(alg: Mode) -> bool {
match alg {
Mode::Aes128Gcm | Mode::Aes256Gcm => true,
_ => false,
}
}
fn get_op_mode(alg: Mode) -> Result<OpMode, Error> {
match alg {
Mode::Aes128Gcm | Mode::Aes256Gcm => {
if hacl_aes_available() {
Ok(OpMode::Hacl)
} else if cfg!(feature = "rust-crypto-aes") {
match alg {
Mode::Aes128Gcm => Ok(OpMode::RustCryptoAes128),
Mode::Aes256Gcm => Ok(OpMode::RustCryptoAes256),
_ => panic!("Unreachable"),
}
} else {
Err(Error::InvalidAlgorithm)
}
}
Mode::Chacha20Poly1305 => Ok(OpMode::Hacl),
}
}
impl Aead {
pub fn new(alg: Mode, k: &[u8]) -> Result<Self, Error> {
if k.len() != key_size(&alg) {
return Err(Error::InvalidKeySize);
}
unsafe {
EverCrypt_AutoConfig2_init();
}
let op_mode = get_op_mode(alg);
match op_mode {
Ok(OpMode::Hacl) => {
let state = unsafe {
let mut state_ptr: *mut EverCrypt_AEAD_state_s = std::ptr::null_mut();
let e = EverCrypt_AEAD_create_in(
alg.into(),
&mut state_ptr,
k.to_vec().as_mut_ptr(),
);
if e != 0 {
return Err(Error::InvalidInit);
}
state_ptr
};
Ok(Self {
mode: alg,
c_state: Some(state),
op_mode: OpMode::Hacl,
key: k.to_vec(),
})
}
Ok(OpMode::RustCryptoAes128) | Ok(OpMode::RustCryptoAes256) => {
if !cfg!(feature = "rust-crypto-aes") {
return Err(Error::UnsupportedConfig);
}
debug_assert!(alg_is_aes(alg));
Ok(Self {
mode: alg,
c_state: None,
op_mode: op_mode.unwrap(),
key: k.to_vec(),
})
}
_ => Err(Error::UnsupportedConfig),
}
}
#[cfg(feature = "random")]
pub fn key_gen(alg: Mode) -> Vec<u8> {
key_gen(alg)
}
#[cfg(feature = "random")]
pub fn nonce_gen(&self) -> Nonce {
nonce_gen(self.mode)
}
#[cfg(feature = "rust-crypto-aes")]
fn encrypt_rs(&self, msg: &[u8], iv: &Nonce, aad: &Aad) -> Result<(Ciphertext, Tag), Error> {
let ctxt_tag = match self.op_mode {
OpMode::RustCryptoAes128 => Aes128Gcm::new(GenericArray::from_slice(&self.key))
.encrypt((&iv[..]).into(), Payload { msg: msg, aad: aad }),
OpMode::RustCryptoAes256 => Aes256Gcm::new(GenericArray::from_slice(&self.key))
.encrypt((&iv[..]).into(), Payload { msg: msg, aad: aad }),
_ => return Err(Error::UnsupportedConfig),
};
match ctxt_tag {
Ok(c) => {
let (ctxt, tag) = c.split_at(c.len() - 16);
Ok((ctxt.to_owned(), clone_into_array(&tag)))
}
Err(_) => Err(Error::Encrypting),
}
}
#[cfg(feature = "rust-crypto-aes")]
fn decrypt_rs(&self, ctxt: &[u8], tag: &[u8], iv: &Nonce, aad: &Aad) -> Result<Vec<u8>, Error> {
let mut p_in: Vec<u8> = vec![];
p_in.extend(ctxt);
p_in.extend(tag);
let msg = match self.op_mode {
OpMode::RustCryptoAes128 => Aes128Gcm::new(GenericArray::from_slice(&self.key))
.decrypt(
(&iv[..]).into(),
Payload {
msg: &p_in[..],
aad: aad,
},
),
OpMode::RustCryptoAes256 => Aes256Gcm::new(GenericArray::from_slice(&self.key))
.decrypt(
(&iv[..]).into(),
Payload {
msg: &p_in[..],
aad: aad,
},
),
_ => return Err(Error::UnsupportedConfig),
};
match msg {
Ok(c) => Ok(c),
Err(_) => Err(Error::InvalidCiphertext),
}
}
#[cfg(not(feature = "rust-crypto-aes"))]
fn encrypt_rs(&self, _msg: &[u8], _iv: &Nonce, _aad: &Aad) -> Result<(Ciphertext, Tag), Error> {
Err(Error::UnsupportedConfig)
}
#[cfg(not(feature = "rust-crypto-aes"))]
fn decrypt_rs(
&self,
_ctxt: &[u8],
_tag: &[u8],
_iv: &Nonce,
_aad: &Aad,
) -> Result<Vec<u8>, Error> {
Err(Error::UnsupportedConfig)
}
pub fn encrypt(&self, msg: &[u8], iv: &Nonce, aad: &Aad) -> Result<(Ciphertext, Tag), Error> {
if iv.len() != 12 {
return Err(Error::InvalidNonce);
}
match self.op_mode {
OpMode::Hacl => {
let mut ctxt = vec![0u8; msg.len()];
let mut tag = [0u8; 16];
unsafe {
EverCrypt_AEAD_encrypt(
self.c_state.unwrap(),
iv.as_ptr() as _,
12,
aad.as_ptr() as _,
aad.len() as u32,
msg.as_ptr() as _,
msg.len() as u32,
ctxt.as_mut_ptr(),
tag.as_mut_ptr(),
);
}
Ok((ctxt, tag))
}
OpMode::RustCryptoAes128 => self.encrypt_rs(msg, iv, aad),
OpMode::RustCryptoAes256 => self.encrypt_rs(msg, iv, aad),
}
}
pub fn decrypt(
&self,
ctxt: &[u8],
tag: &[u8],
iv: &Nonce,
aad: &Aad,
) -> Result<Vec<u8>, Error> {
if iv.len() != 12 {
return Err(Error::InvalidNonce);
}
match self.op_mode {
OpMode::Hacl => {
let mut msg = vec![0u8; ctxt.len()];
let r = unsafe {
EverCrypt_AEAD_decrypt(
self.c_state.unwrap(),
iv.as_ptr() as _,
12,
aad.as_ptr() as _,
aad.len() as u32,
ctxt.as_ptr() as _,
ctxt.len() as u32,
tag.as_ptr() as _,
msg.as_mut_ptr(),
)
};
if r as u32 != EverCrypt_Error_Success {
Err(Error::InvalidCiphertext)
} else {
Ok(msg)
}
}
OpMode::RustCryptoAes128 => self.decrypt_rs(ctxt, tag, iv, aad),
OpMode::RustCryptoAes256 => self.decrypt_rs(ctxt, tag, iv, aad),
}
}
}
impl Drop for Aead {
fn drop(&mut self) {
if let Some(c_state) = self.c_state {
unsafe { EverCrypt_AEAD_free(c_state) }
}
let zero_key: Vec<u8> = (0u8..self.key.len() as u8).collect();
let _ = std::mem::replace(&mut self.key, zero_key);
}
}
pub fn encrypt(
alg: Mode,
k: &[u8],
msg: &[u8],
iv: &Nonce,
aad: &Aad,
) -> Result<(Ciphertext, Tag), Error> {
let cipher = Aead::new(alg, k)?;
cipher.encrypt(msg, iv, aad)
}
pub fn decrypt(
alg: Mode,
k: &[u8],
ctxt: &[u8],
tag: &[u8],
iv: &Nonce,
aad: &Aad,
) -> Result<Vec<u8>, Error> {
let cipher = Aead::new(alg, k)?;
cipher.decrypt(ctxt, tag, iv, aad)
}
#[cfg(feature = "random")]
pub fn key_gen(alg: Mode) -> Vec<u8> {
crate::rand_util::get_random_vec(key_size(&alg))
}
#[cfg(feature = "random")]
pub fn nonce_gen(_alg: Mode) -> Nonce {
crate::rand_util::get_random_array()
}