From d33b8f534d0c7695e9df4fd9e1f0b20030f6ced8 Mon Sep 17 00:00:00 2001 From: K Shiva Kiran Date: Mon, 4 Mar 2024 23:53:40 +0530 Subject: [PATCH] crypt: Added block level operations Byte sequenced inputs are converted to 128 bit uints. To ensure that the formed u128s are within the prime field the current implementation considers only 15 bytes per 128 bit block instead of 16. A small example has been added. --- Cargo.toml | 2 + examples/encryption.rs | 23 ++++++++ src/convert.rs | 42 +++++++++++++++ src/encoding.rs | 23 ++++++++ src/errors.rs | 4 +- src/false_bottom.rs | 118 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 114 +++------------------------------------ src/main.rs | 5 -- 8 files changed, 219 insertions(+), 112 deletions(-) create mode 100644 examples/encryption.rs create mode 100644 src/convert.rs create mode 100644 src/encoding.rs create mode 100644 src/false_bottom.rs delete mode 100644 src/main.rs 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() { - -}