use std::borrow::Borrow;
use std::fmt::Display;
#[cfg(feature = "rustcrypto")]
use aes_siv::{Key, KeyInit, siv::Aes128Siv, siv::Aes256Siv};
use rand::Rng;
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::keyset::DecodedServerCookie;
use super::extension_fields::ExtensionField;
#[cfg(all(feature = "openssl", not(feature = "rustcrypto")))]
mod openssl_defs;
#[cfg(all(feature = "openssl", not(feature = "rustcrypto")))]
use openssl_defs::{Aes128Siv, Aes256Siv, EVPCipher, Key};
#[derive(Debug)]
pub struct DecryptError;
impl Display for DecryptError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Could not decrypt ciphertext")
}
}
impl std::error::Error for DecryptError {}
#[derive(Debug)]
pub struct KeyError;
impl Display for KeyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Invalid key")
}
}
impl std::error::Error for KeyError {}
#[cfg(feature = "rustcrypto")]
mod buffer;
#[cfg(feature = "rustcrypto")]
use buffer::Buffer;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EncryptResult {
pub nonce_length: usize,
pub ciphertext_length: usize,
}
pub trait Cipher: Sync + Send + ZeroizeOnDrop + 'static {
fn encrypt(
&self,
buffer: &mut [u8],
plaintext_length: usize,
associated_data: &[u8],
) -> std::io::Result<EncryptResult>;
fn decrypt(
&self,
nonce: &[u8],
ciphertext: &[u8],
associated_data: &[u8],
) -> Result<Vec<u8>, DecryptError>;
fn key_bytes(&self) -> &[u8];
}
pub enum CipherHolder<'a> {
DecodedServerCookie(DecodedServerCookie),
Other(&'a dyn Cipher),
}
impl AsRef<dyn Cipher> for CipherHolder<'_> {
fn as_ref(&self) -> &dyn Cipher {
match self {
CipherHolder::DecodedServerCookie(cookie) => cookie.c2s.as_ref(),
CipherHolder::Other(cipher) => *cipher,
}
}
}
pub trait CipherProvider {
fn get(&self, context: &[ExtensionField<'_>]) -> Option<CipherHolder<'_>>;
}
pub struct NoCipher;
impl CipherProvider for NoCipher {
fn get<'a>(&self, _context: &[ExtensionField<'_>]) -> Option<CipherHolder<'_>> {
None
}
}
impl CipherProvider for dyn Cipher {
fn get(&self, _context: &[ExtensionField<'_>]) -> Option<CipherHolder<'_>> {
Some(CipherHolder::Other(self))
}
}
impl CipherProvider for Option<&dyn Cipher> {
fn get(&self, _context: &[ExtensionField<'_>]) -> Option<CipherHolder<'_>> {
self.map(CipherHolder::Other)
}
}
impl<C: Cipher> CipherProvider for C {
fn get(&self, _context: &[ExtensionField<'_>]) -> Option<CipherHolder<'_>> {
Some(CipherHolder::Other(self))
}
}
impl<C: Cipher> CipherProvider for Option<C> {
fn get(&self, _context: &[ExtensionField<'_>]) -> Option<CipherHolder<'_>> {
self.as_ref().map(|v| CipherHolder::Other(v))
}
}
pub struct AesSivCmac256 {
key: Key<Aes128Siv>,
}
impl ZeroizeOnDrop for AesSivCmac256 {}
impl AesSivCmac256 {
pub fn new(key: Key<Aes128Siv>) -> Self {
AesSivCmac256 { key }
}
pub fn key_size() -> usize {
Self::new(Key::<Aes128Siv>::default()).key.len()
}
pub fn try_from(key_bytes: &[u8]) -> Result<Self, KeyError> {
let bytes = key_bytes.iter();
(bytes.len() == Self::key_size())
.then(|| Self {
key: bytes.copied().collect(),
})
.ok_or(KeyError)
}
}
impl Drop for AesSivCmac256 {
fn drop(&mut self) {
AsMut::<[u8]>::as_mut(&mut self.key).zeroize();
}
}
fn prepend_slice<'a>(
buffer: &'a mut [u8],
length: usize,
nonce: &[u8],
) -> std::io::Result<&'a mut [u8]> {
if buffer.len() < nonce.len() + length {
return Err(std::io::ErrorKind::WriteZero.into());
}
buffer.copy_within(..length, nonce.len());
buffer[..nonce.len()].copy_from_slice(nonce);
Ok(&mut buffer[nonce.len()..])
}
impl Cipher for AesSivCmac256 {
#[cfg(feature = "rustcrypto")]
fn encrypt(
&self,
buffer: &mut [u8],
plaintext_length: usize,
associated_data: &[u8],
) -> std::io::Result<EncryptResult> {
let mut siv = Aes128Siv::new(&self.key);
let nonce: [u8; 16] = rand::thread_rng().r#gen();
let buffer = prepend_slice(buffer, plaintext_length, &nonce)?;
let mut buffer_wrap = Buffer::new(buffer, plaintext_length);
siv.encrypt_in_place([associated_data, &nonce], &mut buffer_wrap)
.map_err(|_| std::io::ErrorKind::Other)?;
Ok(EncryptResult {
nonce_length: nonce.len(),
ciphertext_length: buffer_wrap.valid(),
})
}
#[cfg(feature = "rustcrypto")]
fn decrypt(
&self,
nonce: &[u8],
ciphertext: &[u8],
associated_data: &[u8],
) -> Result<Vec<u8>, DecryptError> {
let mut siv = Aes128Siv::new(&self.key);
siv.decrypt([associated_data, nonce], ciphertext)
.map_err(|_| DecryptError)
}
#[cfg(all(feature = "openssl", not(feature = "rustcrypto")))]
fn encrypt(
&self,
buffer: &mut [u8],
plaintext_length: usize,
associated_data: &[u8],
) -> std::io::Result<EncryptResult> {
let nonce: [u8; 16] = rand::thread_rng().r#gen();
let buffer = prepend_slice(buffer, plaintext_length, &nonce)?;
let ciphertext_length = openssl_defs::encrypt_in_place(
&self.key,
buffer,
plaintext_length,
[associated_data, nonce.as_slice()],
)?;
Ok(EncryptResult {
nonce_length: nonce.len(),
ciphertext_length,
})
}
#[cfg(all(feature = "openssl", not(feature = "rustcrypto")))]
fn decrypt(
&self,
nonce: &[u8],
ciphertext: &[u8],
associated_data: &[u8],
) -> Result<Vec<u8>, DecryptError> {
openssl_defs::decrypt_vec(&self.key, ciphertext, [associated_data, nonce])
.map_err(|_| DecryptError)
}
fn key_bytes(&self) -> &[u8] {
self.key.as_ref()
}
}
impl std::fmt::Debug for AesSivCmac256 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AesSivCmac256").finish()
}
}
pub struct AesSivCmac512 {
key: Key<Aes256Siv>,
}
impl AesSivCmac512 {
pub fn new(key: Key<Aes256Siv>) -> Self {
Self { key }
}
pub fn key_size() -> usize {
Self::new(Key::<Aes256Siv>::default()).key.len()
}
pub fn try_from(
key_bytes: impl IntoIterator<Item: Borrow<u8>, IntoIter: ExactSizeIterator>,
) -> Result<Self, KeyError> {
let bytes = key_bytes.into_iter();
(bytes.len() == Self::key_size())
.then(|| Self {
key: bytes.map(|x| *x.borrow()).collect(),
})
.ok_or(KeyError)
}
pub fn new_random() -> Self {
#[cfg(feature = "rustcrypto")]
let key = aes_siv::Aes256SivAead::generate_key(rand::thread_rng());
#[cfg(all(feature = "openssl", not(feature = "rustcrypto")))]
let key = {
let mut key_data = Key::<Aes256Siv>::default();
let cipher = &openssl::cipher::Cipher::fetch(None, Aes256Siv::name(), None).unwrap();
let mut ctx = openssl::cipher_ctx::CipherCtx::new().unwrap();
ctx.encrypt_init(Some(cipher), None, None).unwrap();
ctx.rand_key(key_data.as_mut()).unwrap();
key_data
};
Self { key }
}
}
impl ZeroizeOnDrop for AesSivCmac512 {}
impl Drop for AesSivCmac512 {
fn drop(&mut self) {
AsMut::<[u8]>::as_mut(&mut self.key).zeroize();
}
}
impl Cipher for AesSivCmac512 {
#[cfg(feature = "rustcrypto")]
fn encrypt(
&self,
buffer: &mut [u8],
plaintext_length: usize,
associated_data: &[u8],
) -> std::io::Result<EncryptResult> {
let mut siv = Aes256Siv::new(&self.key);
let nonce: [u8; 16] = rand::thread_rng().r#gen();
let buffer = prepend_slice(buffer, plaintext_length, &nonce)?;
let mut buffer_wrap = Buffer::new(buffer, plaintext_length);
siv.encrypt_in_place([associated_data, &nonce], &mut buffer_wrap)
.map_err(|_| std::io::ErrorKind::Other)?;
Ok(EncryptResult {
nonce_length: nonce.len(),
ciphertext_length: buffer_wrap.valid(),
})
}
#[cfg(feature = "rustcrypto")]
fn decrypt(
&self,
nonce: &[u8],
ciphertext: &[u8],
associated_data: &[u8],
) -> Result<Vec<u8>, DecryptError> {
let mut siv = Aes256Siv::new(&self.key);
siv.decrypt([associated_data, nonce], ciphertext)
.map_err(|_| DecryptError)
}
#[cfg(all(feature = "openssl", not(feature = "rustcrypto")))]
fn encrypt(
&self,
buffer: &mut [u8],
plaintext_length: usize,
associated_data: &[u8],
) -> std::io::Result<EncryptResult> {
let nonce: [u8; 16] = rand::thread_rng().r#gen();
let buffer = prepend_slice(buffer, plaintext_length, &nonce)?;
let ciphertext_length = openssl_defs::encrypt_in_place(
&self.key,
buffer,
plaintext_length,
[associated_data, nonce.as_slice()],
)?;
Ok(EncryptResult {
nonce_length: nonce.len(),
ciphertext_length,
})
}
#[cfg(all(feature = "openssl", not(feature = "rustcrypto")))]
fn decrypt(
&self,
nonce: &[u8],
ciphertext: &[u8],
associated_data: &[u8],
) -> Result<Vec<u8>, DecryptError> {
openssl_defs::decrypt_vec(&self.key, ciphertext, [associated_data, nonce])
.map_err(|_| DecryptError)
}
fn key_bytes(&self) -> &[u8] {
self.key.as_ref()
}
}
impl std::fmt::Debug for AesSivCmac512 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AesSivCmac512").finish()
}
}
#[cfg(test)]
pub struct IdentityCipher {
nonce_length: usize,
}
#[cfg(test)]
impl IdentityCipher {
pub fn new(nonce_length: usize) -> Self {
Self { nonce_length }
}
}
#[cfg(test)]
impl ZeroizeOnDrop for IdentityCipher {}
#[cfg(test)]
impl Cipher for IdentityCipher {
fn encrypt(
&self,
buffer: &mut [u8],
plaintext_length: usize,
associated_data: &[u8],
) -> std::io::Result<EncryptResult> {
debug_assert!(associated_data.is_empty());
let nonce: Vec<u8> = (0..self.nonce_length as u8).collect();
if buffer.len() < nonce.len() + plaintext_length {
return Err(std::io::ErrorKind::WriteZero.into());
}
buffer.copy_within(..plaintext_length, nonce.len());
buffer[..nonce.len()].copy_from_slice(&nonce);
Ok(EncryptResult {
nonce_length: nonce.len(),
ciphertext_length: plaintext_length,
})
}
fn decrypt(
&self,
nonce: &[u8],
ciphertext: &[u8],
associated_data: &[u8],
) -> Result<Vec<u8>, DecryptError> {
debug_assert!(associated_data.is_empty());
debug_assert_eq!(nonce.len(), self.nonce_length);
Ok(ciphertext.to_vec())
}
fn key_bytes(&self) -> &[u8] {
unimplemented!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aes_siv_cmac_256() {
let mut testvec: Vec<u8> = (0..16).collect();
testvec.resize(testvec.len() + 32, 0);
let key = AesSivCmac256::new([0u8; 32].into());
let EncryptResult {
nonce_length,
ciphertext_length,
} = key.encrypt(&mut testvec, 16, &[]).unwrap();
let result = key
.decrypt(
&testvec[..nonce_length],
&testvec[nonce_length..(nonce_length + ciphertext_length)],
&[],
)
.unwrap();
assert_eq!(result, (0..16).collect::<Vec<u8>>());
}
#[test]
fn test_aes_siv_cmac_256_with_assoc_data() {
let mut testvec: Vec<u8> = (0..16).collect();
testvec.resize(testvec.len() + 32, 0);
let key = AesSivCmac256::new([0u8; 32].into());
let EncryptResult {
nonce_length,
ciphertext_length,
} = key.encrypt(&mut testvec, 16, &[1]).unwrap();
assert!(
key.decrypt(
&testvec[..nonce_length],
&testvec[nonce_length..(nonce_length + ciphertext_length)],
&[2]
)
.is_err()
);
let result = key
.decrypt(
&testvec[..nonce_length],
&testvec[nonce_length..(nonce_length + ciphertext_length)],
&[1],
)
.unwrap();
assert_eq!(result, (0..16).collect::<Vec<u8>>());
}
#[test]
fn test_aes_siv_cmac_512() {
let mut testvec: Vec<u8> = (0..16).collect();
testvec.resize(testvec.len() + 32, 0);
let key = AesSivCmac512::new([0u8; 64].into());
let EncryptResult {
nonce_length,
ciphertext_length,
} = key.encrypt(&mut testvec, 16, &[]).unwrap();
let result = key
.decrypt(
&testvec[..nonce_length],
&testvec[nonce_length..(nonce_length + ciphertext_length)],
&[],
)
.unwrap();
assert_eq!(result, (0..16).collect::<Vec<u8>>());
}
#[test]
fn test_aes_siv_cmac_512_with_assoc_data() {
let mut testvec: Vec<u8> = (0..16).collect();
testvec.resize(testvec.len() + 32, 0);
let key = AesSivCmac512::new([0u8; 64].into());
let EncryptResult {
nonce_length,
ciphertext_length,
} = key.encrypt(&mut testvec, 16, &[1]).unwrap();
assert!(
key.decrypt(
&testvec[..nonce_length],
&testvec[nonce_length..(nonce_length + ciphertext_length)],
&[2]
)
.is_err()
);
let result = key
.decrypt(
&testvec[..nonce_length],
&testvec[nonce_length..(nonce_length + ciphertext_length)],
&[1],
)
.unwrap();
assert_eq!(result, (0..16).collect::<Vec<u8>>());
}
#[test]
fn key_functions_correctness() {
assert_eq!(
std::mem::size_of::<Key<Aes128Siv>>(),
AesSivCmac256::key_size()
);
assert_eq!(
std::mem::size_of::<Key<Aes256Siv>>(),
AesSivCmac512::key_size()
);
let key_bytes = (1..=64).collect::<Vec<u8>>();
assert!(AesSivCmac256::try_from(&key_bytes).is_err());
let slice = &key_bytes[..AesSivCmac256::key_size()];
assert_eq!(AesSivCmac256::try_from(slice).unwrap().key_bytes(), slice);
let slice = &key_bytes[..AesSivCmac512::key_size()];
assert_eq!(AesSivCmac512::try_from(slice).unwrap().key_bytes(), slice);
}
}