use crate::biterator::Biterator;
use crate::impls::encrypt_ope_bits;
use crate::{CllwOpeEncrypt, Error, Key, OpeCllw8V1};
macro_rules! impl_cllw_ope_byte_array {
($n:literal) => {
impl CllwOpeEncrypt for [u8; $n] {
type Output = OpeCllw8V1<{ $n * 8 + 1 }>;
fn encrypt_ope_with_salt(
self,
key: &Key,
salt: Option<&[u8]>,
) -> Result<Self::Output, Error> {
let bits = self.into_iter().flat_map(Biterator::new);
let vec = encrypt_ope_bits(bits, $n * 8, key, salt);
let mut bytes = [0u8; { $n * 8 + 1 }];
bytes.copy_from_slice(&vec);
Ok(OpeCllw8V1::from_bytes(bytes))
}
}
};
}
impl_cllw_ope_byte_array!(4);
impl_cllw_ope_byte_array!(12);
impl_cllw_ope_byte_array!(14);
#[cfg(test)]
mod tests {
use super::*;
use hex::FromHex;
use quickcheck::{quickcheck, Arbitrary, Gen};
fn key() -> Key {
Key::from([7u8; 32])
}
#[derive(Debug, Clone, Copy)]
struct ArbBytes<const N: usize>([u8; N]);
impl<const N: usize> Arbitrary for ArbBytes<N> {
fn arbitrary(g: &mut Gen) -> Self {
let mut bytes = [0u8; N];
for b in &mut bytes {
*b = u8::arbitrary(g);
}
ArbBytes(bytes)
}
}
quickcheck! {
fn prop_byte_array_4_determinism(x: ArbBytes<4>) -> bool {
let a = key().encrypt_ope(x.0).unwrap();
let b = key().encrypt_ope(x.0).unwrap();
a == b
}
fn prop_byte_array_12_determinism(x: ArbBytes<12>) -> bool {
let a = key().encrypt_ope(x.0).unwrap();
let b = key().encrypt_ope(x.0).unwrap();
a == b
}
fn prop_byte_array_14_determinism(x: ArbBytes<14>) -> bool {
let a = key().encrypt_ope(x.0).unwrap();
let b = key().encrypt_ope(x.0).unwrap();
a == b
}
fn prop_byte_array_4_cmp(x: ArbBytes<4>, y: ArbBytes<4>) -> bool {
let a = key().encrypt_ope(x.0).unwrap();
let b = key().encrypt_ope(y.0).unwrap();
a.cmp(&b) == x.0.cmp(&y.0)
}
fn prop_byte_array_12_cmp(x: ArbBytes<12>, y: ArbBytes<12>) -> bool {
let a = key().encrypt_ope(x.0).unwrap();
let b = key().encrypt_ope(y.0).unwrap();
a.cmp(&b) == x.0.cmp(&y.0)
}
fn prop_byte_array_14_cmp(x: ArbBytes<14>, y: ArbBytes<14>) -> bool {
let a = key().encrypt_ope(x.0).unwrap();
let b = key().encrypt_ope(y.0).unwrap();
a.cmp(&b) == x.0.cmp(&y.0)
}
}
#[test]
fn ciphertext_widths() {
let k = key();
assert_eq!(k.encrypt_ope([0u8; 4]).unwrap().as_ref().len(), 4 * 8 + 1);
assert_eq!(k.encrypt_ope([0u8; 12]).unwrap().as_ref().len(), 12 * 8 + 1);
assert_eq!(k.encrypt_ope([0u8; 14]).unwrap().as_ref().len(), 14 * 8 + 1);
}
#[test]
fn hex_round_trip_14() {
let ct = key().encrypt_ope([42u8; 14]).unwrap();
let parsed = OpeCllw8V1::<{ 14 * 8 + 1 }>::from_hex(hex::encode(ct.as_ref())).unwrap();
assert_eq!(ct, parsed);
}
#[test]
fn distinct_salts_produce_distinct_ciphertexts() {
let k = key();
let x = [9u8; 14];
let a = x.encrypt_ope_with_salt(&k, Some(b"domain-a")).unwrap();
let b = x.encrypt_ope_with_salt(&k, Some(b"domain-b")).unwrap();
assert_ne!(a, b);
}
}