use std::collections::HashMap;
use crate::engine;
use crate::error::Error;
use crate::validate::{self, Operand, MAX_LEN};
#[derive(Clone)]
pub struct FF1 {
key: Vec<u8>,
radix: u32,
min_length: usize,
default_tweak: Vec<u8>,
min_tweak_len: Option<usize>,
max_tweak_len: Option<usize>,
alphabet: Option<Alphabet>,
}
impl std::fmt::Debug for FF1 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FF1")
.field("radix", &self.radix)
.field("min_length", &self.min_length)
.field("min_tweak_len", &self.min_tweak_len)
.field("max_tweak_len", &self.max_tweak_len)
.field("has_alphabet", &self.alphabet.is_some())
.finish_non_exhaustive()
}
}
#[derive(Clone)]
struct Alphabet {
chars: Vec<char>,
index: HashMap<char, u16>,
}
#[derive(Clone)]
#[must_use = "a builder does nothing until .build() is called"]
pub struct Builder<'a> {
key: &'a [u8],
radix: u32,
alphabet: Option<&'a str>,
tweak: &'a [u8],
min_tweak_len: Option<usize>,
max_tweak_len: Option<usize>,
}
impl<'a> Builder<'a> {
pub fn alphabet(mut self, alphabet: &'a str) -> Self {
self.alphabet = Some(alphabet);
self
}
pub fn tweak(mut self, tweak: &'a [u8]) -> Self {
self.tweak = tweak;
self
}
pub fn min_tweak_len(mut self, len: usize) -> Self {
self.min_tweak_len = Some(len);
self
}
pub fn max_tweak_len(mut self, len: usize) -> Self {
self.max_tweak_len = Some(len);
self
}
pub fn build(self) -> Result<FF1, Error> {
validate::key(self.key)?;
validate::radix(self.radix)?;
validate::tweak_bounds(self.min_tweak_len, self.max_tweak_len)?;
validate::tweak(self.tweak.len(), self.min_tweak_len, self.max_tweak_len)?;
let alphabet = match self.alphabet {
None => None,
Some(text) => {
let chars = validate::alphabet(text, self.radix)?;
let index = chars
.iter()
.enumerate()
.map(|(i, &c)| (c, u16::try_from(i).expect("position < radix < 2**16")))
.collect();
Some(Alphabet { chars, index })
}
};
Ok(FF1 {
key: self.key.to_vec(),
radix: self.radix,
min_length: validate::min_length(self.radix),
default_tweak: self.tweak.to_vec(),
min_tweak_len: self.min_tweak_len,
max_tweak_len: self.max_tweak_len,
alphabet,
})
}
}
impl FF1 {
pub fn new(key: &[u8], radix: u32) -> Result<FF1, Error> {
FF1::builder(key, radix).build()
}
pub fn builder(key: &[u8], radix: u32) -> Builder<'_> {
Builder {
key,
radix,
alphabet: None,
tweak: &[],
min_tweak_len: None,
max_tweak_len: None,
}
}
pub fn encrypt_numerals(&self, x: &[u16], tweak: Option<&[u8]>) -> Result<Vec<u16>, Error> {
self.run(x, tweak, Operand::Plaintext)
}
pub fn decrypt_numerals(&self, x: &[u16], tweak: Option<&[u8]>) -> Result<Vec<u16>, Error> {
self.run(x, tweak, Operand::Ciphertext)
}
pub fn encrypt(&self, s: &str, tweak: Option<&[u8]>) -> Result<String, Error> {
let alphabet = self
.alphabet
.as_ref()
.ok_or_else(|| validate::alphabet_required("encrypt_numerals"))?;
let numerals = validate::decode(s, &alphabet.index)?;
let out = self.encrypt_numerals(&numerals, tweak)?;
Ok(alphabet.encode(&out))
}
pub fn decrypt(&self, s: &str, tweak: Option<&[u8]>) -> Result<String, Error> {
let alphabet = self
.alphabet
.as_ref()
.ok_or_else(|| validate::alphabet_required("decrypt_numerals"))?;
let numerals = validate::decode(s, &alphabet.index)?;
let out = self.decrypt_numerals(&numerals, tweak)?;
Ok(alphabet.encode(&out))
}
pub fn min_length(&self) -> usize {
self.min_length
}
pub fn max_length(&self) -> u64 {
MAX_LEN
}
fn run(&self, x: &[u16], tweak: Option<&[u8]>, operand: Operand) -> Result<Vec<u16>, Error> {
let tweak = tweak.unwrap_or(&self.default_tweak);
validate::length(x.len(), self.min_length, self.radix, operand)?;
validate::tweak(tweak.len(), self.min_tweak_len, self.max_tweak_len)?;
validate::numerals(x, self.radix, operand)?;
let encrypt = operand == Operand::Plaintext;
Ok(engine::ff1(&self.key, self.radix, x, tweak, encrypt)
.expect("inputs were validated; the FF1 core cannot fail on them"))
}
}
impl Alphabet {
fn encode(&self, numerals: &[u16]) -> String {
numerals
.iter()
.map(|&v| self.chars[usize::from(v)])
.collect()
}
}