diff --git a/Cargo.toml b/Cargo.toml index 72a0388..267be3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,5 @@ edition = "2021" [dependencies] rand = "0.8.5" crypto-bigint = "0.5.5" +base64 = "0.21.7" +bincode = "1.3.3" diff --git a/examples/encryption.rs b/examples/encryption.rs new file mode 100644 index 0000000..253aeef --- /dev/null +++ b/examples/encryption.rs @@ -0,0 +1,23 @@ +use false_bottom::FBCrypt; + +fn main() { + let real_msg = "The troops are headed due north"; + let fake_msg = "The troops are headed due south"; + + let mut fb = FBCrypt::init(12, 12).unwrap(); + + let real_key = fb.add(&real_msg.as_bytes()).unwrap(); + let fake_key = fb.add(&fake_msg.as_bytes()).unwrap(); + + let real_decr = fb.decrypt(&real_key).unwrap(); + let fake_decr = fb.decrypt(&fake_key).unwrap(); + + let real_str = String::from_utf8(real_decr).unwrap(); + let fake_str = String::from_utf8(fake_decr).unwrap(); + + let cipher = fb.export(); + println!("Cipher: {cipher}"); + println!("Decrypted Contents:"); + println!("Real: {real_str}\nFake: {fake_str}"); + assert_eq!(real_msg, real_str); +} diff --git a/src/convert.rs b/src/convert.rs new file mode 100644 index 0000000..7b04abf --- /dev/null +++ b/src/convert.rs @@ -0,0 +1,42 @@ +use crate::{errors::FBError, FBCrypt}; +use crypto_bigint::{U128, Encoding}; + +// This uses a custom conversion scheme to ensure that +// all 128 bit integers are within the prime field (i.e < 2^128 - 159) +// As of now, this is achieved by utilizing only 120 bits out of 128 bits + +impl FBCrypt { + pub(crate) fn to_u128_blocks(inp: &[u8]) -> Result, FBError> { + if inp.len() == 0 { + return Err(FBError::EmptyInput); + } + let mut out: Vec = inp.chunks_exact(15) + .map(|chunk| { + let mut padded_chunk = [0_u8; 16]; + padded_chunk[..chunk.len()].copy_from_slice(chunk); + U128::from_le_bytes(padded_chunk) + }) + .collect(); + let rem = inp.chunks_exact(15) + .remainder(); + if rem.len() > 0 { + let mut rem_chunk: [u8; 16] = [0; 16]; + rem_chunk[..rem.len()].copy_from_slice(rem); + rem_chunk[15] = 16 - rem.len() as u8; + out.push(U128::from_le_bytes(rem_chunk)); + } + Ok(out) + } + + pub(crate) fn to_bytes(inp: &[U128]) -> Result, FBError> { + if inp.len() == 0 { + return Err(FBError::EmptyInput) + } + let mut out: Vec = inp.iter() + .flat_map(|big_num| big_num.to_le_bytes()[..15].to_vec()) + .collect(); + let pad = inp.last().unwrap().to_le_bytes()[15]; + out.truncate(out.len()+1-pad as usize); + Ok(out) + } +} diff --git a/src/encoding.rs b/src/encoding.rs new file mode 100644 index 0000000..3024521 --- /dev/null +++ b/src/encoding.rs @@ -0,0 +1,23 @@ +use base64::prelude::*; +use crate::{ + FBCrypt, + errors::FBError, +}; + +impl FBCrypt { + pub fn export(&self) -> String { + BASE64_STANDARD.encode( + FBCrypt::to_bytes(&self.c).unwrap_or("".into()) + ) + } + + pub fn import(c_str: &str, r_str: &str) -> Result { + let c_bytes = BASE64_STANDARD.decode(c_str) + .map_err(|_| FBError::DecodeError)?; + let c = FBCrypt::to_u128_blocks(&c_bytes)?; + let r_bytes = BASE64_STANDARD.decode(r_str) + .map_err(|_| FBError::DecodeError)?; + let r = FBCrypt::to_u128_blocks(&r_bytes)?; + Ok(FBCrypt {c, r}) + } +} diff --git a/src/errors.rs b/src/errors.rs index 225e879..6cd195f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,7 +1,9 @@ #[derive(Debug)] pub enum FBError { + DecodeError, + EmptyInput, IntegerTooLarge, - NoModInverse, InvalidKey, InvalidParams, + NoModInverse, } diff --git a/src/false_bottom.rs b/src/false_bottom.rs new file mode 100644 index 0000000..e9ab76e --- /dev/null +++ b/src/false_bottom.rs @@ -0,0 +1,118 @@ +use rand::{Rng, seq::IteratorRandom}; +use crate::{errors::FBError}; +use crypto_bigint::{U128, RandomMod, NonZero, rand_core::OsRng, Limb}; + +pub struct FBCrypt { + pub(crate) r: Vec, + pub(crate) c: Vec, +} + +pub struct FBKey { + pub(crate) indices: Vec<(usize, usize)>, +} + +// Prime number value +const P: U128 = U128::MAX.wrapping_sub(&U128::from_u32(159 - 1)); +// Our prime is at 159th position to the left of 2^128 +const P_POS: Limb = Limb::from_u32(159); + +impl FBCrypt { + pub fn init(n: usize, k: usize) -> Result { + if n < k || k < 2 { + return Err(FBError::InvalidParams) + } + const MODULUS: NonZero = NonZero::::const_new(P).0; + let r: Vec = (0..k) + .map(|_| U128::random_mod(&mut OsRng, &MODULUS)) + .collect(); + let c: Vec = (0..n) + .map(|_| U128::random_mod(&mut OsRng, &MODULUS)) + .collect(); + Ok(FBCrypt {r, c}) + } + + pub fn add(&mut self, m: &[u8]) -> Result, FBError> { + let msg: Vec = FBCrypt::to_u128_blocks(m)?; + let mut keys: Vec = Vec::new(); + for m in msg { + keys.push(self.add_u128(&m)?) + } + Ok(keys) + } + + pub fn decrypt(&mut self, keys: &[FBKey]) -> Result, FBError> { + let mut decrypted: Vec = Vec::new(); + for key in keys { + decrypted.push(self.decrypt_u128(&key)?) + } + let msg = FBCrypt::to_bytes(&decrypted)?; + Ok(msg) + } + + fn add_u128(&mut self, m: &U128) -> Result { + if m.ge(&P) { + return Err(FBError::IntegerTooLarge); + } + let (r, c) = (&mut self.r, &mut self.c); + let n = OsRng.gen_range(2..=r.len()); + let mut c_i: Vec = (0..c.len()) + .choose_multiple(&mut OsRng, n-1); + let r_i: Vec = (0..r.len()) + .choose_multiple(&mut OsRng, n); + + let mut sum: U128 = U128::ZERO; + for (&ci, &ri) in c_i.iter() + .zip(r_i.iter()) + { + sum = sum.add_mod( + &c[ci].mul_mod_special(&r[ri], P_POS), + &P); + } + let r_last = *r_i.last().unwrap(); + let (mod_inv, inv_exists) = r[r_last].inv_mod(&P); + let inv_exists: bool = inv_exists.into(); + if !inv_exists { + return Err(FBError::NoModInverse); + } + let c_new = m.sub_mod(&sum, &P) + .mul_mod_special(&mod_inv, P_POS); + c.push(c_new); + c_i.push(c.len()-1); + + let indices: Vec<(usize, usize)> = c_i.into_iter() + .zip(r_i.into_iter()) + .collect(); + Ok(FBKey {indices}) + } + + fn decrypt_u128(&self, key: &FBKey) -> Result { + if key.indices.len() > self.c.len() { + return Err(FBError::InvalidKey) + } + let mut m: U128 = U128::ZERO; + for &(ci, ri) in &key.indices { + m = m.add_mod( + &self.c[ci].mul_mod_special(&self.r[ri], P_POS), + &P); + } + Ok(m) + } +} + +#[test] +fn encrypt_u128() { + let msg: U128 = U128::from_u32(100); + let mut fb = FBCrypt::init(20, 12).unwrap(); + let key = fb.add_u128(&msg).unwrap(); + let decrypted = fb.decrypt_u128(&key).unwrap(); + assert_eq!(msg, decrypted); +} + +#[test] +fn encrypt_bytes() { + let input = vec![0xff; 150]; + let mut fb = FBCrypt::init(21, 12).unwrap(); + let keys = fb.add(&input).unwrap(); + let decrypted = fb.decrypt(&keys).unwrap(); + assert_eq!(input, decrypted); +} diff --git a/src/lib.rs b/src/lib.rs index ba68871..bcca58e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,107 +1,9 @@ -pub mod errors; +mod errors; +mod convert; +mod encoding; +mod false_bottom; -use rand::{Rng, seq::IteratorRandom}; -use crate::errors::FBError; -use crypto_bigint::{U128, RandomMod, NonZero, rand_core::OsRng, Limb}; - -#[derive(Debug)] -pub struct FBCrypt { - k: usize, - r: Vec, - c: Vec, -} - -#[derive(Debug)] -pub struct FBKey { - r_i: Vec, - c_i: Vec, -} - -// Prime number value -const P: U128 = U128::MAX.wrapping_sub(&U128::from_u32(159 - 1)); -// Our prime is at 159th position to the left of 2^128 -const P_POS: Limb = Limb::from_u32(159); - -impl FBCrypt { - - pub fn init(n: usize, k: usize) -> Result { - if n < k || k < 2 { - return Err(FBError::InvalidParams) - } - const MODULUS: NonZero = NonZero::::const_new(P).0; - let r: Vec = (0..k) - .map(|_| U128::random_mod(&mut OsRng, &MODULUS)) - .collect(); - let c: Vec = (0..n) - .map(|_| U128::random_mod(&mut OsRng, &MODULUS)) - .collect(); - Ok(FBCrypt {k, r, c}) - } - - pub fn add(&mut self, m: &U128) -> Result { - if m.ge(&P) { - return Err(FBError::IntegerTooLarge); - } - let (r, c) = (&mut self.r, &mut self.c); - let n = OsRng.gen_range(2..=self.k); - let mut c_i: Vec = (0..c.len()) - .choose_multiple(&mut OsRng, n-1); - let r_i: Vec = (0..self.k) - .choose_multiple(&mut OsRng, n); - - let mut sum: U128 = U128::ZERO; - for (&ci, &ri) in c_i.iter() - .zip(r_i.iter()) - { - sum = sum.add_mod( - &c[ci].mul_mod_special(&r[ri], P_POS), - &P); - } - let r_last = *r_i.last().unwrap(); - let (mod_inv, inv_exists) = r[r_last].inv_mod(&P); - let inv_exists: bool = inv_exists.into(); - if !inv_exists { - return Err(FBError::NoModInverse); - } - let c_new = m.sub_mod(&sum, &P) - .mul_mod_special(&mod_inv, P_POS); - c.push(c_new); - c_i.push(c.len()-1); - Ok(FBKey {r_i, c_i}) - } - - pub fn decrypt(&self, indices: &FBKey) -> Result { - let (r_i, c_i) = (&indices.r_i, &indices.c_i); - if r_i.len() > self.r.len() || c_i.len() > self.c.len() { - return Err(FBError::InvalidKey) - } - let mut m: U128 = U128::ZERO; - for (&ri, &ci) in r_i.iter().zip(c_i.iter()) { - m = m.add_mod( - &self.c[ci].mul_mod_special(&self.r[ri], P_POS), - &P); - } - Ok(m) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn encrypt_decrypt_100() { - let msg: U128 = U128::from_u32(100); - let mut fb = FBCrypt::init(20, 12).unwrap(); - let key = fb.add(&msg).unwrap(); - let decrypted = fb.decrypt(&key).unwrap(); - assert_eq!(msg, decrypted); - } - - #[test] - fn many_runs() { - for _ in 0..1000 { - encrypt_decrypt_100(); - } - } -} +pub use crate::{ + errors::FBError, + false_bottom::{FBCrypt, FBKey}, +}; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e79cf53..0000000 --- a/src/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -//use false_bottom::FalseBottom; - -fn main() { - -}