From 7ad15fce760303336fe27597bb20b3720d7dca77 Mon Sep 17 00:00:00 2001 From: K Shiva Kiran Date: Wed, 28 Feb 2024 23:54:39 +0530 Subject: [PATCH] crypt: Switch to using large unsigned integers - The crate now uses crypto_bigint U128 ints instead of i32. - OsRng instead of thread_rng(), (cryptographically more secure?). - Modular arithmetic methods from crypto_bigint, no more overflows. - Error code for invalid parameters. - Simpler naming. --- .gitignore | 1 + Cargo.lock | 109 ---------------------------------------- Cargo.toml | 4 +- src/errors.rs | 3 +- src/lib.rs | 135 +++++++++++++++++++++++++++----------------------- 5 files changed, 79 insertions(+), 173 deletions(-) delete mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index ea8c4bf..96ef6c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 7a18bd2..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,109 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "false-bottom" -version = "0.1.0" -dependencies = [ - "modinverse", - "rand", -] - -[[package]] -name = "getrandom" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "modinverse" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f62f577f148cc1a9466e7065a22e59466a7d537cceba5e77e57181d0f706633" -dependencies = [ - "num-integer", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/Cargo.toml b/Cargo.toml index 2700d76..72a0388 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = "^0.8.5" -modinverse = "0.1.1" \ No newline at end of file +rand = "0.8.5" +crypto-bigint = "0.5.5" diff --git a/src/errors.rs b/src/errors.rs index 8f9126a..225e879 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,6 +1,7 @@ #[derive(Debug)] -pub enum FalseBottomError { +pub enum FBError { IntegerTooLarge, NoModInverse, InvalidKey, + InvalidParams, } diff --git a/src/lib.rs b/src/lib.rs index 0bd85a6..ba68871 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,80 +1,85 @@ pub mod errors; -use rand::Rng; -use rand::seq::IteratorRandom; -use crate::errors::FalseBottomError; -use modinverse::modinverse; +use rand::{Rng, seq::IteratorRandom}; +use crate::errors::FBError; +use crypto_bigint::{U128, RandomMod, NonZero, rand_core::OsRng, Limb}; #[derive(Debug)] -pub struct FalseBottom { - pub k: i32, - pub r: Vec, - pub c: Vec, +pub struct FBCrypt { + k: usize, + r: Vec, + c: Vec, } #[derive(Debug)] -pub struct FalseBottomKey { - pub key_indices: Vec, - pub cipher_indices: Vec, +pub struct FBKey { + r_i: Vec, + c_i: Vec, } -const P: i32 = 9973; +// 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 FalseBottom { - pub fn init(k: i32) -> FalseBottom { - let mut rng = rand::thread_rng(); - let mut r: Vec = Vec::new(); - let mut c: Vec = Vec::new(); - for _ in 0..k { - r.push(rng.gen_range(1..k)); - c.push(rng.gen_range(1..P)); +impl FBCrypt { + + pub fn init(n: usize, k: usize) -> Result { + if n < k || k < 2 { + return Err(FBError::InvalidParams) } - FalseBottom {k, r, c} + 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: i32) -> - Result - { - if m > P as i32 { - return Err(FalseBottomError::IntegerTooLarge) + pub fn add(&mut self, m: &U128) -> Result { + if m.ge(&P) { + return Err(FBError::IntegerTooLarge); } - let mut rng = rand::thread_rng(); - let n = rng.gen_range(2..=self.k); + 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 cipher_indices: Vec = (0..self.c.len() as i32) - .choose_multiple(&mut rng, n as usize - 1); - - let key_indices: Vec = (0..self.k) - .choose_multiple(&mut rng, n as usize); - - let mut sum = 0; - for (j, rho) in cipher_indices.iter() - .zip(key_indices.iter()) + let mut sum: U128 = U128::ZERO; + for (&ci, &ri) in c_i.iter() + .zip(r_i.iter()) { - sum += self.c[*j as usize] * self.r[*rho as usize]; - sum %= P; + sum = sum.add_mod( + &c[ci].mul_mod_special(&r[ri], P_POS), + &P); } - - let key_last_el = *key_indices.last().unwrap() as usize; - let mod_inv = modinverse(self.r[key_last_el], P) - .ok_or(FalseBottomError::NoModInverse)?; - let cipher_new_element = ((m - sum) * mod_inv) - .rem_euclid(P); - self.c.push(cipher_new_element); - cipher_indices.push(self.c.len() as i32 - 1); - Ok(FalseBottomKey {key_indices, cipher_indices}) + 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: &FalseBottomKey) -> Result { - let keyi = &indices.key_indices; - let cipheri = &indices.cipher_indices; - if keyi.len() > self.r.len() || cipheri.len() > self.c.len() { - return Err(FalseBottomError::InvalidKey) + 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: i32 = 0; - for (ki, ci) in keyi.iter().zip(cipheri.iter()) { - m += self.c[*ci as usize] * self.r[*ki as usize]; - m %= P; + 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) } @@ -83,12 +88,20 @@ impl FalseBottom { #[cfg(test)] mod tests { use super::*; + #[test] fn encrypt_decrypt_100() { - let msg = 100; - let mut fb = FalseBottom::init(9); - let key = fb.add(msg).unwrap(); + 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!(decrypted, msg); + assert_eq!(msg, decrypted); + } + + #[test] + fn many_runs() { + for _ in 0..1000 { + encrypt_decrypt_100(); + } } }