use crate::error::Unspecified;
use native_ossl::cipher::{AeadDecryptCtx, AeadEncryptCtx, CipherAlg};
use native_ossl::util::SecretBuf;
use std::ffi::CStr;
pub const MAX_TAG_LEN: usize = 16;
pub const NONCE_LEN: usize = 12;
#[derive(Debug)]
pub struct Algorithm {
cipher_name: &'static CStr,
key_len: usize,
tag_len: usize,
}
pub static AES_128_GCM: Algorithm = Algorithm {
cipher_name: c"AES-128-GCM",
key_len: 16,
tag_len: 16,
};
pub static AES_256_GCM: Algorithm = Algorithm {
cipher_name: c"AES-256-GCM",
key_len: 32,
tag_len: 16,
};
pub static CHACHA20_POLY1305: Algorithm = Algorithm {
cipher_name: c"ChaCha20-Poly1305",
key_len: 32,
tag_len: 16,
};
impl Algorithm {
#[must_use]
pub fn key_len(&self) -> usize {
self.key_len
}
#[must_use]
pub fn tag_len(&self) -> usize {
self.tag_len
}
#[must_use]
pub fn nonce_len(&self) -> usize {
NONCE_LEN
}
}
#[derive(Clone, Copy, Debug)]
pub struct Nonce(pub [u8; NONCE_LEN]);
impl Nonce {
#[must_use]
pub fn assume_unique_for_key(bytes: [u8; NONCE_LEN]) -> Self {
Self(bytes)
}
pub fn try_assume_unique_for_key(bytes: &[u8]) -> Result<Self, Unspecified> {
let arr: [u8; NONCE_LEN] = bytes.try_into().map_err(|_| Unspecified)?;
Ok(Self(arr))
}
}
pub struct Aad<A>(pub A);
impl<A: AsRef<[u8]>> Aad<A> {
pub fn from(val: A) -> Self {
Aad(val)
}
}
impl Aad<[u8; 0]> {
#[must_use]
pub fn empty() -> Self {
Aad([])
}
}
pub struct UnboundKey {
alg: &'static Algorithm,
key: SecretBuf,
}
impl std::fmt::Debug for UnboundKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UnboundKey")
.field("alg", &self.alg)
.finish_non_exhaustive()
}
}
impl std::fmt::Debug for LessSafeKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LessSafeKey")
.field("alg", &self.alg)
.finish_non_exhaustive()
}
}
impl UnboundKey {
pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, Unspecified> {
if key_bytes.len() != algorithm.key_len {
return Err(Unspecified);
}
Ok(Self {
alg: algorithm,
key: SecretBuf::from_slice(key_bytes),
})
}
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.alg
}
}
pub struct LessSafeKey {
alg: &'static Algorithm,
key: SecretBuf,
}
impl LessSafeKey {
#[must_use]
pub fn new(key: UnboundKey) -> Self {
Self {
alg: key.alg,
key: key.key,
}
}
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.alg
}
pub fn seal_in_place_append_tag<A: AsRef<[u8]>>(
&self,
nonce: Nonce,
aad: &Aad<A>,
in_out: &mut Vec<u8>,
) -> Result<(), Unspecified> {
let cipher_alg = CipherAlg::fetch(self.alg.cipher_name, None).map_err(|_| Unspecified)?;
let mut enc = AeadEncryptCtx::new(&cipher_alg, self.key.as_ref(), &nonce.0, None)
.map_err(|_| Unspecified)?;
enc.set_aad(aad.0.as_ref()).map_err(|_| Unspecified)?;
let plaintext_len = in_out.len();
let mut ciphertext = vec![0u8; plaintext_len + 32];
let n = enc
.update(in_out, &mut ciphertext)
.map_err(|_| Unspecified)?;
let n2 = enc
.finalize(&mut ciphertext[n..])
.map_err(|_| Unspecified)?;
let ct_len = n + n2;
let mut tag = [0u8; 16];
enc.tag(&mut tag[..self.alg.tag_len])
.map_err(|_| Unspecified)?;
in_out.clear();
in_out.extend_from_slice(&ciphertext[..ct_len]);
in_out.extend_from_slice(&tag[..self.alg.tag_len]);
Ok(())
}
pub fn open_in_place<'in_out, A: AsRef<[u8]>>(
&self,
nonce: Nonce,
aad: &Aad<A>,
in_out: &'in_out mut [u8],
) -> Result<&'in_out mut [u8], Unspecified> {
let tag_len = self.alg.tag_len;
if in_out.len() < tag_len {
return Err(Unspecified);
}
let ciphertext_len = in_out.len() - tag_len;
let (ciphertext, tag) = in_out.split_at_mut(ciphertext_len);
let tag = &*tag;
let cipher_alg = CipherAlg::fetch(self.alg.cipher_name, None).map_err(|_| Unspecified)?;
let mut dec = AeadDecryptCtx::new(&cipher_alg, self.key.as_ref(), &nonce.0, None)
.map_err(|_| Unspecified)?;
dec.set_aad(aad.0.as_ref()).map_err(|_| Unspecified)?;
dec.set_tag(tag).map_err(|_| Unspecified)?;
let mut plaintext = vec![0u8; ciphertext_len + 32];
let n = dec
.update(ciphertext, &mut plaintext)
.map_err(|_| Unspecified)?;
let n2 = dec.finalize(&mut plaintext[n..]).map_err(|_| Unspecified)?;
let pt_len = n + n2;
in_out[..pt_len].copy_from_slice(&plaintext[..pt_len]);
Ok(&mut in_out[..pt_len])
}
pub fn open_within<'in_out, A: AsRef<[u8]>>(
&self,
nonce: Nonce,
aad: &Aad<A>,
in_out: &'in_out mut [u8],
in_prefix_len: usize,
) -> Result<&'in_out mut [u8], Unspecified> {
let tag_len = self.alg.tag_len;
let total = in_out.len();
if total < in_prefix_len + tag_len {
return Err(Unspecified);
}
let ciphertext_and_tag = &mut in_out[in_prefix_len..];
let result = self.open_in_place(nonce, aad, ciphertext_and_tag)?;
let pt_len = result.len();
in_out.copy_within(in_prefix_len..in_prefix_len + pt_len, 0);
Ok(&mut in_out[..pt_len])
}
}