use alloc::vec;
use alloc::vec::Vec;
#[cfg(feature = "rust-crypto")]
pub use rust_crypto::*;
use wasefire_applet_api::crypto::gcm as api;
use crate::{Error, convert, convert_unit};
pub struct Support {
pub no_copy: bool,
pub in_place_no_copy: bool,
}
pub struct Cipher {
pub text: Vec<u8>,
pub tag: Vec<u8>,
}
pub fn is_supported() -> bool {
convert(unsafe { api::support() }).unwrap_or(0) != 0
}
pub fn support() -> Support {
let support = convert(unsafe { api::support() }).unwrap();
Support {
no_copy: (support & (1 << api::Support::NoCopy as u32)) != 0,
in_place_no_copy: (support & (1 << api::Support::InPlaceNoCopy as u32)) != 0,
}
}
pub fn tag_length() -> usize {
convert(unsafe { api::tag_length() }).unwrap()
}
pub fn encrypt(key: &[u8; 32], iv: &[u8; 12], aad: &[u8], clear: &[u8]) -> Result<Cipher, Error> {
let mut text = vec![0; clear.len()];
let mut tag = vec![0; tag_length()];
encrypt_mut(key, iv, aad, clear, &mut text, &mut tag)?;
Ok(Cipher { text, tag })
}
pub fn encrypt_mut(
key: &[u8; 32], iv: &[u8; 12], aad: &[u8], clear: &[u8], cipher: &mut [u8], tag: &mut [u8],
) -> Result<(), Error> {
if clear.len() != cipher.len() || tag.len() != tag_length() {
return Err(Error::user(0));
}
let params = api::encrypt::Params {
key: key.as_ptr(),
iv: iv.as_ptr(),
aad: aad.as_ptr(),
aad_len: aad.len(),
length: clear.len(),
clear: clear.as_ptr(),
cipher: cipher.as_mut_ptr(),
tag: tag.as_mut_ptr(),
};
convert_unit(unsafe { api::encrypt(params) })
}
pub fn encrypt_in_place(
key: &[u8; 32], iv: &[u8; 12], aad: &[u8], buffer: &mut [u8], tag: &mut [u8],
) -> Result<(), Error> {
if tag.len() != tag_length() {
return Err(Error::user(0));
}
let params = api::encrypt::Params {
key: key.as_ptr(),
iv: iv.as_ptr(),
aad: aad.as_ptr(),
aad_len: aad.len(),
length: buffer.len(),
clear: core::ptr::null(),
cipher: buffer.as_mut_ptr(),
tag: tag.as_mut_ptr(),
};
convert_unit(unsafe { api::encrypt(params) })
}
pub fn decrypt(
key: &[u8; 32], iv: &[u8; 12], aad: &[u8], cipher: &Cipher,
) -> Result<Vec<u8>, Error> {
let mut clear = vec![0; cipher.text.len()];
decrypt_mut(key, iv, aad, &cipher.tag, &cipher.text, &mut clear)?;
Ok(clear)
}
pub fn decrypt_mut(
key: &[u8; 32], iv: &[u8; 12], aad: &[u8], tag: &[u8], cipher: &[u8], clear: &mut [u8],
) -> Result<(), Error> {
if cipher.len() != clear.len() || tag.len() != tag_length() {
return Err(Error::user(0));
}
let params = api::decrypt::Params {
key: key.as_ptr(),
iv: iv.as_ptr(),
aad: aad.as_ptr(),
aad_len: aad.len(),
tag: tag.as_ptr(),
length: cipher.len(),
cipher: cipher.as_ptr(),
clear: clear.as_mut_ptr(),
};
convert_unit(unsafe { api::decrypt(params) })
}
pub fn decrypt_in_place(
key: &[u8; 32], iv: &[u8; 12], aad: &[u8], tag: &[u8], buffer: &mut [u8],
) -> Result<(), Error> {
if tag.len() != tag_length() {
return Err(Error::user(0));
}
let params = api::decrypt::Params {
key: key.as_ptr(),
iv: iv.as_ptr(),
aad: aad.as_ptr(),
aad_len: aad.len(),
tag: tag.as_ptr(),
length: buffer.len(),
cipher: core::ptr::null(),
clear: buffer.as_mut_ptr(),
};
convert_unit(unsafe { api::decrypt(params) })
}
#[cfg(feature = "rust-crypto")]
mod rust_crypto {
use super::*;
#[derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key<const IN_PLACE: bool> {
key: [u8; 32],
}
impl<const IN_PLACE: bool> core::ops::Deref for Key<IN_PLACE> {
type Target = aead::Key<Self>;
fn deref(&self) -> &Self::Target {
(&self.key).into()
}
}
pub type Aes256Gcm = Key<false>;
pub type Aes256GcmInPlace = Key<true>;
impl<const IN_PLACE: bool> aead::KeySizeUser for Key<IN_PLACE> {
type KeySize = aead::consts::U32;
}
impl<const IN_PLACE: bool> aead::KeyInit for Key<IN_PLACE> {
fn new(key: &aead::Key<Self>) -> Self {
Self { key: (*key).into() }
}
}
impl<const IN_PLACE: bool> aead::AeadCore for Key<IN_PLACE> {
type NonceSize = aead::consts::U12;
type TagSize = aead::consts::U16;
type CiphertextOverhead = aead::consts::U0;
}
impl aead::Aead for Key<false> {
fn encrypt<'msg, 'aad>(
&self, nonce: &aead::Nonce<Self>, plaintext: impl Into<aead::Payload<'msg, 'aad>>,
) -> aead::Result<Vec<u8>> {
let payload = plaintext.into();
let len = payload.msg.len();
let mut result = vec![0; len + 16];
let mut tag = [0; 16];
encrypt_mut(
&self.key,
nonce.as_ref(),
payload.aad,
payload.msg,
&mut result[.. len],
&mut tag[.. tag_length()],
)
.map_err(|_| aead::Error)?;
result[len ..].copy_from_slice(tag.as_ref());
Ok(result)
}
fn decrypt<'msg, 'aad>(
&self, nonce: &aead::Nonce<Self>, ciphertext: impl Into<aead::Payload<'msg, 'aad>>,
) -> aead::Result<Vec<u8>> {
let payload: aead::Payload = ciphertext.into();
let len = payload.msg.len().checked_sub(16).ok_or(aead::Error)?;
let (cipher, tag) = payload.msg.split_at(len);
let mut clear = vec![0; len];
decrypt_mut(
&self.key,
nonce.as_ref(),
payload.aad,
&tag[.. tag_length()],
cipher,
&mut clear,
)
.map_err(|_| aead::Error)?;
Ok(clear)
}
}
impl aead::AeadInPlace for Key<true> {
fn encrypt_in_place_detached(
&self, nonce: &aead::Nonce<Self>, associated_data: &[u8], buffer: &mut [u8],
) -> aead::Result<aead::Tag<Self>> {
let mut tag = [0; 16];
encrypt_in_place(
&self.key,
nonce.as_ref(),
associated_data,
buffer,
&mut tag[.. tag_length()],
)
.map_err(|_| aead::Error)?;
Ok(tag.into())
}
fn decrypt_in_place_detached(
&self, nonce: &aead::Nonce<Self>, associated_data: &[u8], buffer: &mut [u8],
tag: &aead::Tag<Self>,
) -> aead::Result<()> {
decrypt_in_place(
&self.key,
nonce.as_ref(),
associated_data,
&tag[.. tag_length()],
buffer,
)
.map_err(|_| aead::Error)
}
}
}