mod boxed;
mod error;
mod multi;
use std::borrow::Cow;
use snafu::ensure;
pub use boxed::{BoxedAeadDecryptor, BoxedAeadEncryptor};
pub(crate) use error::InvalidBundleSnafu;
pub use error::UnsealError;
pub use multi::{MultiKeyCipher, MultiKeyDecryptor, MultiKeyDecryptorError};
use crate::{
crypto::KeyMatchStrength,
platform::{MaybeSend, MaybeSendSync},
};
pub struct AeadOutput {
pub nonce: Vec<u8>,
pub ciphertext: Vec<u8>,
pub tag: Vec<u8>,
}
#[derive(Debug, Clone, Copy)]
pub struct CipherMatch<'a> {
pub enc: Option<&'a str>,
pub kid: Option<&'a str>,
}
pub trait AeadEncryptor: MaybeSendSync {
type Error: crate::Error;
fn enc_algorithm(&self) -> Cow<'_, str>;
fn key_id(&self) -> Option<Cow<'_, str>>;
fn encrypt(
&self,
plaintext: &[u8],
aad: &[u8],
) -> impl Future<Output = Result<AeadOutput, Self::Error>> + MaybeSend;
}
pub trait AeadDecryptor: MaybeSendSync {
type Error: crate::Error;
fn cipher_match(&self, m: &CipherMatch<'_>) -> Option<KeyMatchStrength>;
fn decrypt(
&self,
cipher_match: Option<&CipherMatch<'_>>,
nonce: &[u8],
ciphertext: &[u8],
tag: &[u8],
aad: &[u8],
) -> impl Future<Output = Result<Vec<u8>, Self::Error>> + MaybeSend;
}
pub trait AeadCipherSelector: MaybeSendSync {
type Encryptor: AeadEncryptor;
fn select_cipher(&self) -> Self::Encryptor;
}
pub trait AeadSealer: AeadEncryptor {
fn seal(
&self,
plaintext: &[u8],
aad: &[u8],
) -> impl Future<Output = Result<Vec<u8>, Self::Error>> + MaybeSend;
}
pub trait AeadUnsealer: AeadDecryptor {
fn unseal(
&self,
cipher_match: Option<&CipherMatch<'_>>,
bundle: &[u8],
aad: &[u8],
) -> impl Future<Output = Result<Vec<u8>, Self::Error>> + MaybeSend;
}
pub struct AeadV1Sealer<E: AeadEncryptor>(E);
impl<E: AeadEncryptor> AeadV1Sealer<E> {
pub fn new(encryptor: E) -> Self {
Self(encryptor)
}
}
impl<E: AeadEncryptor> AeadEncryptor for AeadV1Sealer<E> {
type Error = E::Error;
fn enc_algorithm(&self) -> Cow<'_, str> {
self.0.enc_algorithm()
}
fn key_id(&self) -> Option<Cow<'_, str>> {
self.0.key_id()
}
async fn encrypt(&self, plaintext: &[u8], aad: &[u8]) -> Result<AeadOutput, Self::Error> {
self.0.encrypt(plaintext, aad).await
}
}
impl<E: AeadEncryptor> AeadSealer for AeadV1Sealer<E> {
async fn seal(&self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>, Self::Error> {
let output = self.encrypt(plaintext, aad).await?;
let nonce_len: u8 = output
.nonce
.len()
.try_into()
.expect("nonce length exceeds u8::MAX");
let tag_len: u8 = output
.tag
.len()
.try_into()
.expect("tag length exceeds u8::MAX");
let mut bundle =
Vec::with_capacity(3 + output.nonce.len() + output.ciphertext.len() + output.tag.len());
bundle.push(0x01);
bundle.push(nonce_len);
bundle.push(tag_len);
bundle.extend_from_slice(&output.nonce);
bundle.extend_from_slice(&output.ciphertext);
bundle.extend_from_slice(&output.tag);
Ok(bundle)
}
}
pub struct AeadV1Unsealer<D: AeadDecryptor>(D);
impl<D: AeadDecryptor> AeadV1Unsealer<D> {
pub fn new(decryptor: D) -> Self {
Self(decryptor)
}
}
impl<D: AeadDecryptor> AeadDecryptor for AeadV1Unsealer<D> {
type Error = UnsealError<D::Error>;
fn cipher_match(&self, m: &CipherMatch<'_>) -> Option<KeyMatchStrength> {
self.0.cipher_match(m)
}
async fn decrypt(
&self,
cipher_match: Option<&CipherMatch<'_>>,
nonce: &[u8],
ciphertext: &[u8],
tag: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, Self::Error> {
self.0
.decrypt(cipher_match, nonce, ciphertext, tag, aad)
.await
.map_err(Into::into)
}
}
impl<D: AeadDecryptor> AeadUnsealer for AeadV1Unsealer<D> {
async fn unseal(
&self,
cipher_match: Option<&CipherMatch<'_>>,
bundle: &[u8],
aad: &[u8],
) -> Result<Vec<u8>, Self::Error> {
ensure!(bundle.len() >= 3 && bundle[0] == 0x01, InvalidBundleSnafu);
let nonce_len = bundle[1] as usize;
let tag_len = bundle[2] as usize;
ensure!(bundle.len() >= 3 + nonce_len + tag_len, InvalidBundleSnafu);
let nonce = &bundle[3..3 + nonce_len];
let tag = &bundle[bundle.len() - tag_len..];
let ciphertext = &bundle[3 + nonce_len..bundle.len() - tag_len];
self.decrypt(cipher_match, nonce, ciphertext, tag, aad)
.await
}
}