diff --git a/examples/encryption.rs b/examples/encryption.rs index 39c4789..dff3672 100644 --- a/examples/encryption.rs +++ b/examples/encryption.rs @@ -1,26 +1,34 @@ + use false_bottom::FBCrypt; fn main() { - let real_msg = "The troops are headed due north"; - let fake_msg = "The troops are headed due south"; + // Input messages + let fake_msg = "Weather department warns of heavy rains within the upcoming two days"; + let real_msg1 = "I have obtained intel regarding the government's illegal spying"; + let real_msg2 = "Please meet me at the Paradise hotel at 5:30 PM"; - let mut fb = FBCrypt::init(18, 12).unwrap(); + // Cipher initialization + let mut fb = FBCrypt::init(18, 12).unwrap(); - let real_key = fb.add(&real_msg.as_bytes()).unwrap(); - let fake_key = fb.add(&fake_msg.as_bytes()).unwrap(); + // Encryption + let real_key1 = fb.add(&real_msg1.as_bytes()); + let real_key2 = fb.add(&real_msg2.as_bytes()); + let fake_key = fb.add(&fake_msg.as_bytes()); - let cipher = fb.export().unwrap(); - let mut fb_new = FBCrypt::import(&cipher).unwrap(); + // Decryption + let fake_decr = fb.decrypt(&fake_key).unwrap(); + let real1_decr = fb.decrypt(&real_key1).unwrap(); + let real2_decr = fb.decrypt(&real_key2).unwrap(); - let real_decr = fb_new.decrypt(&real_key).unwrap(); - let fake_decr = fb_new.decrypt(&fake_key).unwrap(); - - let real_str = String::from_utf8(real_decr).unwrap(); - let fake_str = String::from_utf8(fake_decr).unwrap(); - - println!("Cipher: {cipher}"); - println!("Decrypted Contents:"); - println!("Real: {real_str}\nFake: {fake_str}"); - assert_eq!(real_msg, real_str); + let fake_str = String::from_utf8(fake_decr).unwrap(); + let real_str1 = String::from_utf8(real1_decr).unwrap(); + let real_str2 = String::from_utf8(real2_decr).unwrap(); + println!("Decrypted Contents:"); + println!("Fake Message: {fake_str}"); + println!("Real Message 1: {real_str1}"); + println!("Real Message 2: {real_str2}"); + assert_eq!(fake_msg, fake_str); + assert_eq!(real_msg1, real_str1); + assert_eq!(real_msg2, real_str2); } diff --git a/examples/export.rs b/examples/export.rs new file mode 100644 index 0000000..497b14c --- /dev/null +++ b/examples/export.rs @@ -0,0 +1,37 @@ +use false_bottom::{FBCrypt, FBKey}; + +fn main() { + // Cipher Initialization + let mut fb = FBCrypt::init(18, 9).unwrap(); + + // Encryption + let msg1 = "This is a message"; + let key1 = fb.add(msg1.as_bytes()); + let msg2 = "This is another message"; + let key2 = fb.add(msg2.as_bytes()); + + // Export + let (cipher, keybase) = fb.export(); + let key1_exp = key1.export(); + let key2_exp = key2.export(); + + // Import + let fb_new = FBCrypt::import(&cipher, &keybase).unwrap(); + let key1_imp = FBKey::import(&key1_exp).unwrap(); + let key2_imp = FBKey::import(&key2_exp).unwrap(); + + // Decryption + let decr1 = fb_new.decrypt(&key1_imp).unwrap(); + let decr2 = fb_new.decrypt(&key2_imp).unwrap(); + + // Display + println!(" +CipherText: \n{cipher}\n +KeyBase: \n{keybase}\n +Key 1: {key1_exp} +Key 2: {key2_exp} +"); + + assert_eq!(msg1.as_bytes(), decr1); + assert_eq!(msg2.as_bytes(), decr2); +} diff --git a/src/convert.rs b/src/convert.rs deleted file mode 100644 index d6e2677..0000000 --- a/src/convert.rs +++ /dev/null @@ -1,40 +0,0 @@ -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 - -pub(crate) fn msg_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[..15].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_msg_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 index 090ea38..2bdddef 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -1,41 +1,60 @@ -use base64::prelude::*; +use base64::prelude::{BASE64_STANDARD, Engine}; +use bincode::{Options, DefaultOptions}; use crypto_bigint::{U128, Encoding}; -use crate::{ - FBCrypt, - errors::FBError, -}; +use crate::{FBCrypt, FBKey, errors::FBError}; impl FBCrypt { - pub fn export(&self) -> Result { - let c_bytes: Vec = self.c.iter() - .flat_map(|bigint| bigint.to_le_bytes().to_vec()) - .collect(); - let r_bytes: Vec = self.r.iter() - .flat_map(|bigint| bigint.to_le_bytes().to_vec()) - .collect(); - Ok( - BASE64_STANDARD.encode(c_bytes) + ":" - + &BASE64_STANDARD.encode(r_bytes) - ) - } - pub fn import(string: &str) -> Result { - let (c_str, r_str) = string.split_once(':') - .ok_or_else(|| FBError::DecodeError)?; - let c_bytes = BASE64_STANDARD.decode(c_str) - .map_err(|_| FBError::DecodeError)?; - let c: Vec = c_bytes.chunks_exact(16) - .map(|chunk| U128::from_le_bytes(chunk.try_into().unwrap())) - .collect(); - let r_bytes = BASE64_STANDARD.decode(r_str) - .map_err(|_| FBError::DecodeError)?; - let r: Vec = r_bytes.chunks_exact(16) - .map(|chunk| U128::from_le_bytes(chunk.try_into().unwrap())) - .collect(); - Ok(FBCrypt {c, r}) - } + pub fn export(&self) -> (String, String) { + let c_bytes: Vec = self.c.iter() + .flat_map(|bigint| bigint.to_le_bytes()) + .collect(); + let r_bytes: Vec = self.r.iter() + .flat_map(|bigint| bigint.to_le_bytes()) + .collect(); + + (BASE64_STANDARD.encode(c_bytes), BASE64_STANDARD.encode(r_bytes)) + } + + pub fn import(cipher: &str, keybase: &str) -> Result { + let c_bytes = BASE64_STANDARD.decode(cipher) + .map_err(|_| FBError::DecodeError)?; + let c: Vec = c_bytes.chunks_exact(16) + .map(|chunk| U128::from_le_bytes(chunk.try_into().unwrap())) + .collect(); + let r_bytes = BASE64_STANDARD.decode(keybase) + .map_err(|_| FBError::DecodeError)?; + let r: Vec = r_bytes.chunks_exact(16) + .map(|chunk| U128::from_le_bytes(chunk.try_into().unwrap())) + .collect(); + if c.len() < r.len() || r.len() < 2 { + return Err(FBError::InvalidParams); + } + + Ok(FBCrypt {c, r}) + } } impl FBKey { - + + pub fn export(&self) -> String { + let binc = DefaultOptions::new(); + let indice_bytes = binc.serialize(&self.indices) + .unwrap(); + + BASE64_STANDARD.encode(&indice_bytes) + } + + pub fn import(key_str: &str) -> Result { + let binc = DefaultOptions::new(); + let indice_bytes = BASE64_STANDARD.decode(key_str) + .map_err(|_| FBError::DecodeError)?; + let indices: Vec<_> = binc.deserialize(&indice_bytes) + .map_err(|_| FBError::DecodeError)?; + if indices.len() < 2 { + return Err(FBError::DecodeError); + } + + Ok (FBKey {indices}) + } } diff --git a/src/errors.rs b/src/errors.rs index 6cd195f..a62059d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,9 +1,6 @@ #[derive(Debug)] pub enum FBError { - DecodeError, - EmptyInput, - IntegerTooLarge, - InvalidKey, - InvalidParams, - NoModInverse, + DecodeError, + InvalidKey, + InvalidParams, } diff --git a/src/false_bottom.rs b/src/false_bottom.rs index e949185..d808812 100644 --- a/src/false_bottom.rs +++ b/src/false_bottom.rs @@ -1,122 +1,115 @@ -use rand::{Rng, seq::IteratorRandom}; -use crate::{errors::FBError, convert::*}; -use crypto_bigint::{U128, RandomMod, NonZero, rand_core::OsRng, Limb}; +use crypto_bigint::{U128, Limb, RandomMod, NonZero}; +use rand::{seq::IteratorRandom, Rng}; +use crate::{errors::FBError, packing}; pub struct FBCrypt { - pub(crate) c: Vec, - pub(crate) r: Vec, + pub(crate) c: Vec, // Ciphertext + pub(crate) r: Vec, // Keybase } pub struct FBKey { - pub(crate) r: Vec, - pub(crate) indices: Vec>, + // 2D Vec containing (cipher_index, keybase_index) pairs + pub(crate) indices: 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); +// Value and position of the Prime used (2^128 - 159) +const P: U128 = U128::MAX.wrapping_sub(&U128::from_u8(159 - 1)); +const P_POS: Limb = Limb::from_u8(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 {c, r}) - } + 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 mut rng = rand::thread_rng(); + let r = (0..k) + .map(|_| U128::random_mod(&mut rng, &MODULUS)) + .collect(); + let c = (0..n) + .map(|_| U128::random_mod(&mut rng, &MODULUS)) + .collect(); - pub fn add(&mut self, m: &[u8]) -> Result { - let msg: Vec = msg_to_u128_blocks(m)?; - let mut key = FBKey { - r: self.r.clone(), - indices: Vec::new() - }; - for m in msg { - key.indices.push(self.add_u128(&m)?) - } - Ok(key) - } + Ok(FBCrypt { c, r }) + } - pub fn decrypt(&mut self, key: &FBKey) -> Result, FBError> { - let mut decrypted: Vec = Vec::new(); - for indices in &key.indices { - decrypted.push(self.decrypt_u128(&indices)?) - } - let msg = to_msg_bytes(&decrypted)?; - Ok(msg) - } + pub fn add(&mut self, msg: &[u8]) -> FBKey { + let indices = packing::pack(msg).into_iter() + .map(|msg_uint| self.add_u128(&msg_uint)) + .collect(); - fn add_u128(&mut self, m: &U128) -> Result, FBError> { - 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); + FBKey { indices } + } - 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); + pub fn decrypt(&self, key: &FBKey) -> Result, FBError> { + let decr = key.indices.iter() + .map(|index_row| self.decrypt_u128(&index_row)) + .collect::, _>>()?; + let msg = packing::unpack(&decr)?; - let indices: Vec<(usize, usize)> = c_i.into_iter() - .zip(r_i.into_iter()) - .collect(); - Ok(indices) - } + Ok(msg) + } - fn decrypt_u128(&self, indices: &[(usize, usize)]) -> Result { - if indices.len() > self.c.len() { - return Err(FBError::InvalidKey) - } - let mut m: U128 = U128::ZERO; - for &(ci, ri) in indices { - m = m.add_mod( - &self.c[ci].mul_mod_special(&self.r[ri], P_POS), - &P); - } - Ok(m) - } + fn add_u128(&mut self, msg_uint: &U128) -> Vec<(usize, usize)> { + let (r, c) = (&self.r, &mut self.c); + let mut rng = rand::thread_rng(); + let n = rng.gen_range(2..=r.len()); + let mut c_i = (0..c.len()).choose_multiple(&mut rng, n - 1); + let r_i = (0..r.len()).choose_multiple(&mut rng, n); + let mut sum = U128::ZERO; + for (&ci, &ri) in c_i.iter().zip(r_i.iter()) { + sum = sum.add_mod_special( + &c[ci].mul_mod_special(&r[ri], P_POS), + P_POS); + } + let ri_last = *r_i.last().expect("r_i will contain at least 2 elements"); + let mod_inv = r[ri_last].inv_mod(&P).0; + let c_new_el = msg_uint.sub_mod_special(&sum, P_POS) + .mul_mod_special(&mod_inv, P_POS); + c.push(c_new_el); + c_i.push(c.len() - 1); + let indices = c_i.into_iter() + .zip(r_i.into_iter()) + .collect(); + + indices + } + + fn decrypt_u128(&self, indices: &[(usize, usize)]) -> Result { + if indices.len() > self.r.len() { + return Err(FBError::InvalidKey); + } + let mut msg = U128::ZERO; + for &(ci, ri) in indices { + let c_el = self.c.get(ci).ok_or(FBError::InvalidKey)?; + let r_el = self.r.get(ri).ok_or(FBError::InvalidKey)?; + msg = msg.add_mod_special( + &c_el.mul_mod_special(&r_el, P_POS), + P_POS); + } + + Ok(msg) + } } #[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); + let msg = U128::from_u32(100); + let mut fb = FBCrypt::init(20, 12).unwrap(); + let key = fb.add_u128(&msg); + 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 key = fb.add(&input).unwrap(); - let decrypted = fb.decrypt(&key).unwrap(); - assert_eq!(input, decrypted); + let input1 = vec![255_u8; 150]; + let input2 = vec![0_u8; 102]; + let mut fb = FBCrypt::init(21, 12).unwrap(); + let key1 = fb.add(&input1); + let key2 = fb.add(&input2); + let decr1 = fb.decrypt(&key1).unwrap(); + let decr2 = fb.decrypt(&key2).unwrap(); + assert_eq!(input1, decr1); + assert_eq!(input2, decr2); } diff --git a/src/lib.rs b/src/lib.rs index bcca58e..c68bee2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ -mod errors; -mod convert; mod encoding; +mod errors; mod false_bottom; +mod packing; pub use crate::{ - errors::FBError, - false_bottom::{FBCrypt, FBKey}, + errors::FBError, + false_bottom::{FBCrypt, FBKey}, }; diff --git a/src/packing.rs b/src/packing.rs new file mode 100644 index 0000000..cd6b6ee --- /dev/null +++ b/src/packing.rs @@ -0,0 +1,64 @@ +use crypto_bigint::{U128, Encoding}; +use crate::FBError; + +/* PACKING SCHEME + * When a number n >= P (out of field) is formed from the byte chunks, + * the values P-1 followed by n-3000 are appended to the output vec. + * Subtraction is done to bring n within the field (i.e less than P). + * While unpacking, P-1 is used as a signaling element to extract n + * from the successive element by adding back 3000. + */ +const P_MINUS_ONE: U128 = U128::MAX.wrapping_sub(&U128::from_u8(159)); + +pub(crate) fn pack(inp: &[u8]) -> Vec { + let mut out = Vec::with_capacity(inp.len()/16 + 2); + inp.chunks(16) + .for_each(|inp_chunk| { + let mut out_chunk = [0_u8; 16]; + out_chunk[..inp_chunk.len()].copy_from_slice(inp_chunk); + let mut out_uint = U128::from_le_bytes(out_chunk); + if out_uint >= P_MINUS_ONE { + out.push(P_MINUS_ONE); + out_uint = out_uint.wrapping_sub(&U128::from_u16(3000)); + } + out.push(out_uint); + }); + let inp_chunk_last = inp.chunks_exact(16) + .remainder(); + let mut pad_chunk: [u8; 16] = [16; 16]; + if inp_chunk_last.len() > 0 { + pad_chunk[15] += 16 - inp_chunk_last.len() as u8; + } + out.push(U128::from_le_bytes(pad_chunk)); + out.shrink_to_fit(); + + out +} + +pub(crate) fn unpack(inp: &[U128]) -> Result, FBError> { + let pad_len = inp.last() + .ok_or(FBError::InvalidKey)? + .to_le_bytes()[15] as usize; + if pad_len > 31 { + return Err(FBError::InvalidKey); + } + let mut out = Vec::with_capacity(inp.len()*16); + let mut add_3k = false; + for i in inp { + if add_3k { + let orig = i.wrapping_add(&U128::from_u16(3000)); + out.extend(orig.to_le_bytes().into_iter()); + add_3k = false; + } else if *i == P_MINUS_ONE { + add_3k = true; + } else { + out.extend(i.to_le_bytes().into_iter()); + } + } + let trunc_len = out.len().checked_sub(pad_len) + .ok_or(FBError::InvalidKey)?; + out.truncate(trunc_len); + out.shrink_to_fit(); + + Ok(out) +}