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:
parent
b10874e5d6
commit
7ad15fce76
|
@ -1 +1,2 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
|
|
|
@ -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"
|
|
@ -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"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#[derive(Debug)]
|
||||
pub enum FalseBottomError {
|
||||
pub enum FBError {
|
||||
IntegerTooLarge,
|
||||
NoModInverse,
|
||||
InvalidKey,
|
||||
InvalidParams,
|
||||
}
|
||||
|
|
135
src/lib.rs
135
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<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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue