use super::{Cipher, Encrypt};
use crate::{
cipher::{MapCipher, SeqCipher},
IntoAad,
};
use std::borrow::Cow;
use std::collections::HashMap;
use std::hash::Hash;
use vitaminc_protected::{Controlled, Equatable, Protected};
use zeroize::Zeroize;
impl Encrypt for u32 {
fn encrypt_with_aad<'a, C, A>(self, cipher: C, aad: A) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
cipher.encrypt_bytes_array(Protected::new(self.to_le_bytes()), aad)
}
}
impl Encrypt for String {
fn encrypt_with_aad<'a, C, A>(self, cipher: C, aad: A) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
Self::encrypt_protected(Protected::new(self), cipher, aad)
}
fn encrypt_protected<'a, C, A>(
this: Protected<Self>,
cipher: C,
aad: A,
) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
cipher.encrypt_bytes_vec(this.map(String::into_bytes), aad)
}
}
impl Encrypt for &str {
fn encrypt_with_aad<'a, C, A>(self, cipher: C, aad: A) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
cipher.encrypt_bytes_vec(Protected::new(self.as_bytes().to_vec()), aad)
}
}
impl<T> Encrypt for Vec<T>
where
T: Encrypt,
{
fn encrypt_with_aad<'a, C: Cipher, A: IntoAad<'a>>(
self,
cipher: C,
aad: A,
) -> Result<C::Ok, C::Error> {
let len = self.len();
self.into_iter()
.try_fold(cipher.encrypt_seq(Some(len), aad), |c, item| {
c.encrypt_next(item)
})?
.end()
}
}
impl<const N: usize> Encrypt for [u8; N] {
fn encrypt_with_aad<'a, C, A>(self, cipher: C, aad: A) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
Self::encrypt_protected(Protected::new(self), cipher, aad)
}
fn encrypt_protected<'a, C, A>(
this: Protected<Self>,
cipher: C,
aad: A,
) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
cipher.encrypt_bytes_array(this, aad)
}
}
impl Encrypt for Vec<u8> {
fn encrypt_with_aad<'a, C, A>(self, cipher: C, aad: A) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
Self::encrypt_protected(Protected::new(self), cipher, aad)
}
fn encrypt_protected<'a, C, A>(
this: Protected<Self>,
cipher: C,
aad: A,
) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
cipher.encrypt_bytes_vec(this, aad)
}
}
impl<K, T> Encrypt for HashMap<K, T>
where
K: Into<Cow<'static, str>> + Eq + Hash,
T: Encrypt,
{
fn encrypt_with_aad<'a, C: Cipher, A: IntoAad<'a>>(
self,
cipher: C,
aad: A,
) -> Result<C::Ok, C::Error> {
self.into_iter()
.try_fold(cipher.encrypt_map(aad), |c, (k, v)| {
c.encrypt_key(k).and_then(|c| c.encrypt_value(v))
})?
.end()
}
}
impl<T> Encrypt for Protected<T>
where
T: Encrypt + Zeroize,
{
fn encrypt_with_aad<'a, C, A>(self, cipher: C, aad: A) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
T::encrypt_protected(self, cipher, aad)
}
}
impl<T> Encrypt for Equatable<T>
where
T: Controlled,
T::Inner: Encrypt,
{
fn encrypt_with_aad<'a, C, A>(self, cipher: C, aad: A) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
self.risky_unwrap().encrypt_with_aad(cipher, aad)
}
}
impl<T> Encrypt for Option<T>
where
T: Encrypt,
{
fn encrypt_with_aad<'a, C, A>(self, cipher: C, aad: A) -> Result<C::Ok, C::Error>
where
C: Cipher,
A: IntoAad<'a>,
{
match self {
Some(v) => cipher.encrypt_some(v, aad),
None => cipher.encrypt_none(aad),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::MockCipher;
#[test]
fn vec_u8_seals_as_a_byte_leaf_not_a_sequence() {
let cipher = MockCipher::new();
let ct = vec![1u8, 2, 3]
.encrypt(&cipher)
.expect("byte leaf should encrypt");
assert_eq!(ct, vec![1, 2, 3]);
assert_eq!(cipher.array_entry_hits(), 0);
}
#[test]
fn protected_vec_u8_seals_as_a_byte_leaf() {
let cipher = MockCipher::new();
let ct = Protected::new(vec![1u8, 2, 3])
.encrypt(&cipher)
.expect("byte leaf should encrypt");
assert_eq!(ct, vec![1, 2, 3]);
}
#[test]
fn protected_array_reaches_the_array_entry_point() {
let cipher = MockCipher::new();
let ct = Protected::new([9u8, 8, 7, 6])
.encrypt(&cipher)
.expect("byte leaf should encrypt");
assert_eq!(ct, vec![9, 8, 7, 6]);
assert_eq!(cipher.array_entry_hits(), 1);
}
#[test]
fn protected_array_and_bare_array_share_a_wire_shape() {
let cipher = MockCipher::new();
let wrapped = Protected::new([1u8, 2])
.encrypt_with_aad(&cipher, "ctx")
.expect("encrypt");
let wrapped_aad = cipher.captured_aad();
let bare = [1u8, 2].encrypt_with_aad(&cipher, "ctx").expect("encrypt");
assert_eq!(wrapped, bare);
assert_eq!(wrapped_aad, cipher.captured_aad());
}
#[test]
fn protected_string_seals_as_a_byte_leaf() {
let cipher = MockCipher::new();
let ct = Protected::new(String::from("hi"))
.encrypt(&cipher)
.expect("encrypt");
assert_eq!(ct, b"hi".to_vec());
assert_eq!(cipher.array_entry_hits(), 0);
}
#[test]
fn protected_value_without_an_override_takes_the_default_path() {
let cipher = MockCipher::new();
let ct = Protected::new(0x04030201u32)
.encrypt(&cipher)
.expect("encrypt");
assert_eq!(ct, vec![1, 2, 3, 4]);
assert_eq!(cipher.array_entry_hits(), 1);
}
}