use std::convert::TryInto;
#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use evercrypt_sys::evercrypt_bindings::*;
#[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,
}
}
}
#[inline]
pub const fn key_size(mode: Mode) -> usize {
match mode {
Mode::Aes128Gcm => 16,
Mode::Aes256Gcm => 32,
Mode::Chacha20Poly1305 => 32,
}
}
#[inline]
pub const fn tag_size(mode: Mode) -> usize {
match mode {
Mode::Aes128Gcm => 16,
Mode::Aes256Gcm => 16,
Mode::Chacha20Poly1305 => 16,
}
}
#[inline]
pub const fn nonce_size(mode: Mode) -> usize {
match mode {
Mode::Aes128Gcm => 12,
Mode::Aes256Gcm => 12,
Mode::Chacha20Poly1305 => 12,
}
}
#[derive(Debug, PartialEq)]
pub enum Error {
InvalidInit = 0,
InvalidAlgorithm = 1,
InvalidCiphertext = 2,
InvalidNonce = 3,
UnsupportedConfig = 4,
Encrypting = 5,
Decrypting = 6,
InvalidKeySize = 7,
InvalidTagSize = 8,
}
pub struct Aead {
mode: Mode,
c_state: Option<*mut EverCrypt_AEAD_state_s>,
}
pub type Ciphertext = Vec<u8>;
pub type Key = Vec<u8>;
pub type Tag = Vec<u8>;
pub type Nonce = Vec<u8>;
pub type Aad = [u8];
unsafe fn hacl_aes_available() -> bool {
EverCrypt_AutoConfig2_has_pclmulqdq()
&& EverCrypt_AutoConfig2_has_avx()
&& EverCrypt_AutoConfig2_has_sse()
&& EverCrypt_AutoConfig2_has_movbe()
&& EverCrypt_AutoConfig2_has_aesni()
}
impl Aead {
fn set_key_(&mut self, mut k: Vec<u8>) -> Result<(), Error> {
let state = unsafe {
let mut state_ptr: *mut EverCrypt_AEAD_state_s = std::ptr::null_mut();
let e = EverCrypt_AEAD_create_in(self.mode.into(), &mut state_ptr, k.as_mut_ptr());
if e != 0 {
return Err(Error::InvalidInit);
}
state_ptr
};
self.c_state = Some(state);
Ok(())
}
pub fn new(mode: Mode, k: &[u8]) -> Result<Self, Error> {
if k.len() != key_size(mode) {
return Err(Error::InvalidKeySize);
}
unsafe {
EverCrypt_AutoConfig2_init();
}
let mut out = Self::init(mode)?;
out.set_key_(k.to_vec())?;
Ok(out)
}
pub fn init(mode: Mode) -> Result<Self, Error> {
if unsafe {
EverCrypt_AutoConfig2_init();
(mode == Mode::Aes128Gcm || mode == Mode::Aes256Gcm) && !hacl_aes_available()
} {
return Err(Error::UnsupportedConfig);
}
Ok(Self {
mode,
c_state: None,
})
}
pub fn set_key(self, k: &[u8]) -> Result<Self, Error> {
Self::new(self.mode, k)
}
#[cfg(feature = "random")]
pub fn set_random_key(&mut self) -> Result<(), Error> {
let k = self.key_gen();
self.set_key_(k)
}
#[cfg(feature = "random")]
pub fn key_gen(&self) -> Key {
key_gen(self.mode)
}
#[cfg(feature = "random")]
pub fn nonce_gen(&self) -> Nonce {
nonce_gen(self.mode)
}
pub const fn nonce_size(&self) -> usize {
nonce_size(self.mode)
}
pub const fn key_size(&self) -> usize {
key_size(self.mode)
}
pub const fn tag_size(&self) -> usize {
tag_size(self.mode)
}
pub fn encrypt(&self, msg: &[u8], iv: &[u8], aad: &Aad) -> Result<(Ciphertext, Tag), Error> {
if iv.len() != self.nonce_size() {
return Err(Error::InvalidNonce);
}
let mut ctxt = vec![0u8; msg.len()];
let mut tag = vec![0u8; self.tag_size()];
unsafe {
EverCrypt_AEAD_encrypt(
self.c_state.unwrap(),
iv.as_ptr() as _,
self.nonce_size().try_into().unwrap(),
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))
}
pub fn encrypt_combined(&self, msg: &[u8], iv: &[u8], aad: &Aad) -> Result<Ciphertext, Error> {
if iv.len() != self.nonce_size() {
return Err(Error::InvalidNonce);
}
let mut ctxt = vec![0u8; msg.len() + self.tag_size()];
unsafe {
EverCrypt_AEAD_encrypt(
self.c_state.unwrap(),
iv.as_ptr() as _,
self.nonce_size().try_into().unwrap(),
aad.as_ptr() as _,
aad.len() as u32,
msg.as_ptr() as _,
msg.len() as u32,
ctxt.as_mut_ptr(),
ctxt.as_mut_ptr().offset(msg.len().try_into().unwrap()),
);
}
Ok(ctxt)
}
pub fn encrypt_in_place(&self, payload: &mut [u8], iv: &[u8], aad: &Aad) -> Result<Tag, Error> {
if iv.len() != self.nonce_size() {
return Err(Error::InvalidNonce);
}
let mut tag = vec![0u8; self.tag_size()];
unsafe {
EverCrypt_AEAD_encrypt(
self.c_state.unwrap(),
iv.as_ptr() as _,
self.nonce_size().try_into().unwrap(),
aad.as_ptr() as _,
aad.len() as u32,
payload.as_ptr() as _,
payload.len() as u32,
payload.as_ptr() as _,
tag.as_mut_ptr(),
);
}
Ok(tag)
}
#[inline]
fn _decrypt_checks(&self, tag: &[u8], iv: &[u8]) -> Result<(), Error> {
if iv.len() != 12 {
return Err(Error::InvalidNonce);
}
if tag.len() != self.tag_size() {
return Err(Error::InvalidTagSize);
}
Ok(())
}
#[inline]
fn _decrypt(&self, ctxt: &[u8], tag: &[u8], iv: &[u8], aad: &Aad) -> Result<Vec<u8>, Error> {
self._decrypt_checks(tag, iv)?;
let mut msg = vec![0u8; ctxt.len()];
let r = unsafe {
EverCrypt_AEAD_decrypt(
self.c_state.unwrap(),
iv.as_ptr() as _,
self.nonce_size().try_into().unwrap(),
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)
}
}
pub fn decrypt(&self, ctxt: &[u8], tag: &[u8], iv: &[u8], aad: &Aad) -> Result<Vec<u8>, Error> {
self._decrypt(ctxt, tag, iv, aad)
}
pub fn decrypt_combined(&self, ctxt: &[u8], iv: &[u8], aad: &Aad) -> Result<Vec<u8>, Error> {
if ctxt.len() < self.tag_size() {
return Err(Error::InvalidTagSize);
}
let msg_len = ctxt.len() - self.tag_size();
let tag = &ctxt[msg_len..];
let ctxt = &ctxt[..msg_len];
self._decrypt(ctxt, tag, iv, aad)
}
pub fn decrypt_in_place(
&self,
payload: &mut [u8],
tag: &[u8],
iv: &[u8],
aad: &Aad,
) -> Result<(), Error> {
self._decrypt_checks(tag, iv)?;
let r = unsafe {
EverCrypt_AEAD_decrypt(
self.c_state.unwrap(),
iv.as_ptr() as _,
self.nonce_size().try_into().unwrap(),
aad.as_ptr() as _,
aad.len() as u32,
payload.as_ptr() as _,
payload.len() as u32,
tag.as_ptr() as _,
payload.as_mut_ptr(),
)
};
if r as u32 != EverCrypt_Error_Success {
Err(Error::InvalidCiphertext)
} else {
Ok(())
}
}
}
impl Drop for Aead {
fn drop(&mut self) {
if let Some(c_state) = self.c_state {
unsafe { EverCrypt_AEAD_free(c_state) }
}
}
}
pub fn encrypt(
alg: Mode,
k: &[u8],
msg: &[u8],
iv: &[u8],
aad: &Aad,
) -> Result<(Ciphertext, Tag), Error> {
let cipher = Aead::new(alg, k)?;
cipher.encrypt(msg, iv, aad)
}
pub fn encrypt_combined(
alg: Mode,
k: &[u8],
msg: &[u8],
iv: &[u8],
aad: &Aad,
) -> Result<Ciphertext, Error> {
let cipher = Aead::new(alg, k)?;
cipher.encrypt_combined(msg, iv, aad)
}
pub fn encrypt_in_place(
alg: Mode,
k: &[u8],
payload: &mut [u8],
iv: &[u8],
aad: &Aad,
) -> Result<Tag, Error> {
let cipher = Aead::new(alg, k)?;
cipher.encrypt_in_place(payload, iv, aad)
}
pub fn decrypt(
alg: Mode,
k: &[u8],
ctxt: &[u8],
tag: &[u8],
iv: &[u8],
aad: &Aad,
) -> Result<Vec<u8>, Error> {
let cipher = Aead::new(alg, k)?;
cipher.decrypt(ctxt, tag, iv, aad)
}
pub fn decrypt_combined(
alg: Mode,
k: &[u8],
ctxt: &[u8],
iv: &[u8],
aad: &Aad,
) -> Result<Vec<u8>, Error> {
let cipher = Aead::new(alg, k)?;
cipher.decrypt_combined(ctxt, iv, aad)
}
pub fn decrypt_in_place(
alg: Mode,
k: &[u8],
payload: &mut [u8],
tag: &[u8],
iv: &[u8],
aad: &Aad,
) -> Result<(), Error> {
let cipher = Aead::new(alg, k)?;
cipher.decrypt_in_place(payload, tag, iv, aad)
}
#[cfg(feature = "random")]
pub fn key_gen(mode: Mode) -> Key {
crate::rand_util::random_vec(key_size(mode))
}
#[cfg(feature = "random")]
pub fn nonce_gen(mode: Mode) -> Nonce {
crate::rand_util::random_vec(nonce_size(mode))
}