#![allow(clippy::type_complexity)]
use vitaminc_protected::Protected;
use crate::{Context, IntoAad};
use super::types::{Absent, Encrypted, Entry, Map, Passthrough};
use super::{HCons, HList, HNil};
pub trait StaticCipher: Sized + Copy {
type Error;
fn encrypt_bytes<'a, A>(
self,
data: Protected<Vec<u8>>,
aad: A,
) -> Result<Encrypted, Self::Error>
where
A: IntoAad<'a>;
fn encrypt_none<'a, A>(self, aad: A) -> Result<Absent, Self::Error>
where
A: IntoAad<'a>;
fn passthrough<T>(self, value: T) -> Passthrough<T> {
Passthrough(value)
}
fn encrypt_map(self) -> StaticMapBuilder<Self, HNil> {
StaticMapBuilder {
cipher: self,
list: HNil,
}
}
}
pub struct StaticMapBuilder<C, L> {
cipher: C,
list: L,
}
impl<C, L> StaticMapBuilder<C, L>
where
C: StaticCipher,
L: HList,
{
pub fn encrypt_entry<'a, A>(
self,
key: &'static str,
value: Protected<Vec<u8>>,
aad: A,
) -> Result<StaticMapBuilder<C, HCons<Entry<Encrypted>, L>>, C::Error>
where
A: IntoAad<'a>,
{
let entry_aad = aad.into_aad().for_map_entry(key);
let encrypted = self.cipher.encrypt_bytes(value, entry_aad)?;
Ok(StaticMapBuilder {
cipher: self.cipher,
list: HCons(
Entry {
key,
value: encrypted,
},
self.list,
),
})
}
pub fn passthrough_entry<T>(
self,
key: &'static str,
value: T,
) -> StaticMapBuilder<C, HCons<Entry<Passthrough<T>>, L>> {
StaticMapBuilder {
cipher: self.cipher,
list: HCons(
Entry {
key,
value: Passthrough(value),
},
self.list,
),
}
}
pub fn none_entry<'a, A>(
self,
key: &'static str,
aad: A,
) -> Result<StaticMapBuilder<C, HCons<Entry<Absent>, L>>, C::Error>
where
A: IntoAad<'a>,
{
let entry_aad = aad.into_aad().for_map_entry(key);
let absent = self.cipher.encrypt_none(entry_aad)?;
Ok(StaticMapBuilder {
cipher: self.cipher,
list: HCons(Entry { key, value: absent }, self.list),
})
}
pub fn nested_entry<'a, A, Inner, F>(
self,
key: &'static str,
aad: A,
build: F,
) -> Result<StaticMapBuilder<C, HCons<Entry<Map<Inner>>, L>>, C::Error>
where
A: IntoAad<'a>,
Inner: HList,
F: FnOnce(StaticMapBuilder<C, HNil>, Context<'static>) -> Result<Map<Inner>, C::Error>,
{
let nested_aad = aad.into_aad().for_map_entry(key);
let value = build(self.cipher.encrypt_map(), nested_aad)?;
Ok(StaticMapBuilder {
cipher: self.cipher,
list: HCons(Entry { key, value }, self.list),
})
}
pub fn end(self) -> Map<L> {
Map(self.list)
}
}