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.
This commit is contained in:
K Shiva Kiran 2024-02-28 23:54:39 +05:30
parent b10874e5d6
commit 7ad15fce76
5 changed files with 79 additions and 173 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target /target
Cargo.lock

109
Cargo.lock generated
View File

@ -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"

View File

@ -6,5 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rand = "^0.8.5" rand = "0.8.5"
modinverse = "0.1.1" crypto-bigint = "0.5.5"

View File

@ -1,6 +1,7 @@
#[derive(Debug)] #[derive(Debug)]
pub enum FalseBottomError { pub enum FBError {
IntegerTooLarge, IntegerTooLarge,
NoModInverse, NoModInverse,
InvalidKey, InvalidKey,
InvalidParams,
} }

View File

@ -1,80 +1,85 @@
pub mod errors; pub mod errors;
use rand::Rng; use rand::{Rng, seq::IteratorRandom};
use rand::seq::IteratorRandom; use crate::errors::FBError;
use crate::errors::FalseBottomError; use crypto_bigint::{U128, RandomMod, NonZero, rand_core::OsRng, Limb};
use modinverse::modinverse;
#[derive(Debug)] #[derive(Debug)]
pub struct FalseBottom { pub struct FBCrypt {
pub k: i32, k: usize,
pub r: Vec<i32>, r: Vec<U128>,
pub c: Vec<i32>, c: Vec<U128>,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct FalseBottomKey { pub struct FBKey {
pub key_indices: Vec<i32>, r_i: Vec<usize>,
pub cipher_indices: Vec<i32>, c_i: Vec<usize>,
} }
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 { impl FBCrypt {
pub fn init(k: i32) -> FalseBottom {
let mut rng = rand::thread_rng(); pub fn init(n: usize, k: usize) -> Result<FBCrypt, FBError> {
let mut r: Vec<i32> = Vec::new(); if n < k || k < 2 {
let mut c: Vec<i32> = Vec::new(); return Err(FBError::InvalidParams)
for _ in 0..k {
r.push(rng.gen_range(1..k));
c.push(rng.gen_range(1..P));
} }
FalseBottom {k, r, c} const MODULUS: NonZero<U128> = NonZero::<U128>::const_new(P).0;
let r: Vec<U128> = (0..k)
.map(|_| U128::random_mod(&mut OsRng, &MODULUS))
.collect();
let c: Vec<U128> = (0..n)
.map(|_| U128::random_mod(&mut OsRng, &MODULUS))
.collect();
Ok(FBCrypt {k, r, c})
} }
pub fn add(&mut self, m: i32) -> pub fn add(&mut self, m: &U128) -> Result<FBKey, FBError> {
Result<FalseBottomKey, FalseBottomError> if m.ge(&P) {
{ return Err(FBError::IntegerTooLarge);
if m > P as i32 {
return Err(FalseBottomError::IntegerTooLarge)
} }
let mut rng = rand::thread_rng(); let (r, c) = (&mut self.r, &mut self.c);
let n = rng.gen_range(2..=self.k); let n = OsRng.gen_range(2..=self.k);
let mut c_i: Vec<usize> = (0..c.len())
.choose_multiple(&mut OsRng, n-1);
let r_i: Vec<usize> = (0..self.k)
.choose_multiple(&mut OsRng, n);
let mut cipher_indices: Vec<i32> = (0..self.c.len() as i32) let mut sum: U128 = U128::ZERO;
.choose_multiple(&mut rng, n as usize - 1); for (&ci, &ri) in c_i.iter()
.zip(r_i.iter())
let key_indices: Vec<i32> = (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())
{ {
sum += self.c[*j as usize] * self.r[*rho as usize]; sum = sum.add_mod(
sum %= P; &c[ci].mul_mod_special(&r[ri], P_POS),
&P);
} }
let r_last = *r_i.last().unwrap();
let key_last_el = *key_indices.last().unwrap() as usize; let (mod_inv, inv_exists) = r[r_last].inv_mod(&P);
let mod_inv = modinverse(self.r[key_last_el], P) let inv_exists: bool = inv_exists.into();
.ok_or(FalseBottomError::NoModInverse)?; if !inv_exists {
let cipher_new_element = ((m - sum) * mod_inv) return Err(FBError::NoModInverse);
.rem_euclid(P); }
self.c.push(cipher_new_element); let c_new = m.sub_mod(&sum, &P)
cipher_indices.push(self.c.len() as i32 - 1); .mul_mod_special(&mod_inv, P_POS);
Ok(FalseBottomKey {key_indices, cipher_indices}) c.push(c_new);
c_i.push(c.len()-1);
Ok(FBKey {r_i, c_i})
} }
pub fn decrypt(&self, indices: &FalseBottomKey) -> Result<i32, FalseBottomError> { pub fn decrypt(&self, indices: &FBKey) -> Result<U128, FBError> {
let keyi = &indices.key_indices; let (r_i, c_i) = (&indices.r_i, &indices.c_i);
let cipheri = &indices.cipher_indices; if r_i.len() > self.r.len() || c_i.len() > self.c.len() {
if keyi.len() > self.r.len() || cipheri.len() > self.c.len() { return Err(FBError::InvalidKey)
return Err(FalseBottomError::InvalidKey)
} }
let mut m: i32 = 0; let mut m: U128 = U128::ZERO;
for (ki, ci) in keyi.iter().zip(cipheri.iter()) { for (&ri, &ci) in r_i.iter().zip(c_i.iter()) {
m += self.c[*ci as usize] * self.r[*ki as usize]; m = m.add_mod(
m %= P; &self.c[ci].mul_mod_special(&self.r[ri], P_POS),
&P);
} }
Ok(m) Ok(m)
} }
@ -83,12 +88,20 @@ impl FalseBottom {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn encrypt_decrypt_100() { fn encrypt_decrypt_100() {
let msg = 100; let msg: U128 = U128::from_u32(100);
let mut fb = FalseBottom::init(9); let mut fb = FBCrypt::init(20, 12).unwrap();
let key = fb.add(msg).unwrap(); let key = fb.add(&msg).unwrap();
let decrypted = fb.decrypt(&key).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();
}
} }
} }