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
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
[dependencies]
rand = "^0.8.5"
modinverse = "0.1.1"
rand = "0.8.5"
crypto-bigint = "0.5.5"

View File

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

View File

@ -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<i32>,
pub c: Vec<i32>,
pub struct FBCrypt {
k: usize,
r: Vec<U128>,
c: Vec<U128>,
}
#[derive(Debug)]
pub struct FalseBottomKey {
pub key_indices: Vec<i32>,
pub cipher_indices: Vec<i32>,
pub struct FBKey {
r_i: Vec<usize>,
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 {
pub fn init(k: i32) -> FalseBottom {
let mut rng = rand::thread_rng();
let mut r: Vec<i32> = Vec::new();
let mut c: Vec<i32> = 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<FBCrypt, FBError> {
if n < k || k < 2 {
return Err(FBError::InvalidParams)
}
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) ->
Result<FalseBottomKey, FalseBottomError>
{
if m > P as i32 {
return Err(FalseBottomError::IntegerTooLarge)
pub fn add(&mut self, m: &U128) -> Result<FBKey, FBError> {
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<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)
.choose_multiple(&mut rng, n as usize - 1);
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())
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<i32, FalseBottomError> {
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<U128, FBError> {
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();
}
}
}