mod domain;
mod error;
mod fiestal;
mod prf;
pub use error::Error as FieldEncryptionError;
const ROUNDS: usize = 7;
pub struct FieldEncryption {
input: domain::RegexDomain,
output: domain::RegexDomain,
fiestal: fiestal::Fiestal,
trim_bytes: usize,
top_bits: u32,
}
impl FieldEncryption {
pub fn new(input_regex: &str, output_regex: &str, key: &[u8]) -> Result<Self, error::Error> {
let input = domain::RegexDomain::new(input_regex)?;
let output = domain::RegexDomain::new(output_regex)?;
if input.len() > output.len() {
return Err(error::Error::OutputDomainTooSmall);
}
let prfs = prf::prfs(key, ROUNDS)?;
let fiestal = fiestal::Fiestal::new(prfs)?;
let output_max = output.len();
let zero_bits = output_max.leading_zeros();
let trim_bytes = (zero_bits / 8) as usize;
let mut top_bits = 8 - (zero_bits % 8);
if top_bits % 2 == 1 {
top_bits += 1;
}
Ok(Self {
input,
output,
fiestal,
trim_bytes,
top_bits,
})
}
pub fn encrypt(&self, input: &str) -> Result<String, error::Error> {
self.execute(input, &self.input, &self.output, |data, bits| {
self.fiestal.encrypt(data, bits);
})
}
pub fn decrypt(&self, input: &str) -> Result<String, error::Error> {
self.execute(input, &self.output, &self.input, |data, bits| {
self.fiestal.decrypt(data, bits);
})
}
fn execute(
&self,
input: &str,
from: &domain::RegexDomain,
to: &domain::RegexDomain,
func: impl Fn(&mut [u8], u32),
) -> Result<String, error::Error> {
let input_offset = from
.offset(input.as_bytes())
.ok_or_else(|| error::Error::InvalidInput(input.to_owned()))?;
let mut input_bytes = input_offset.to_be_bytes();
let mut output_offset = self.output.len();
while output_offset >= self.output.len() {
func(&mut input_bytes[self.trim_bytes..], self.top_bits);
output_offset = u128::from_be_bytes(input_bytes);
}
let output_bytes = to
.nth(output_offset)
.ok_or_else(|| error::Error::InvalidOutputOffset(output_offset))?;
Ok(String::from_utf8(output_bytes)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip() {
let input = "11211";
let fe = FieldEncryption::new(r"[0-9]{1,9}", r"[a-z]{1,17}", &[23; 32]).unwrap();
let cipher = fe.encrypt(input).unwrap();
println!("cipher = {}", cipher);
let plain = fe.decrypt(&cipher).unwrap();
println!("plain = {}", plain);
assert_eq!(input, plain.as_str());
}
#[test]
fn readme() {
let fe = FieldEncryption::new(
r"[A-Z][a-z]{1,4} [A-Z][a-z]{1,4}!",
r"[a-z]{5} [a-z]{7}",
&[0; 32],
)
.unwrap();
let cipher_text = fe.encrypt("Hello World!").unwrap();
println!("{}", cipher_text);
let plain_text = fe.decrypt(&cipher_text).unwrap();
println!("{}", plain_text);
}
}