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.
This commit is contained in:
K Shiva Kiran 2024-03-04 23:53:40 +05:30
parent 7ad15fce76
commit d33b8f534d
8 changed files with 219 additions and 112 deletions

View File

@ -8,3 +8,5 @@ edition = "2021"
[dependencies]
rand = "0.8.5"
crypto-bigint = "0.5.5"
base64 = "0.21.7"
bincode = "1.3.3"

23
examples/encryption.rs Normal file
View File

@ -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);
}

42
src/convert.rs Normal file
View File

@ -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<Vec<U128>, FBError> {
if inp.len() == 0 {
return Err(FBError::EmptyInput);
}
let mut out: Vec<U128> = 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<Vec<u8>, FBError> {
if inp.len() == 0 {
return Err(FBError::EmptyInput)
}
let mut out: Vec<u8> = 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)
}
}

23
src/encoding.rs Normal file
View File

@ -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<FBCrypt, FBError> {
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})
}
}

View File

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

118
src/false_bottom.rs Normal file
View File

@ -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<U128>,
pub(crate) c: Vec<U128>,
}
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<FBCrypt, FBError> {
if n < k || k < 2 {
return Err(FBError::InvalidParams)
}
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 {r, c})
}
pub fn add(&mut self, m: &[u8]) -> Result<Vec<FBKey>, FBError> {
let msg: Vec<U128> = FBCrypt::to_u128_blocks(m)?;
let mut keys: Vec<FBKey> = Vec::new();
for m in msg {
keys.push(self.add_u128(&m)?)
}
Ok(keys)
}
pub fn decrypt(&mut self, keys: &[FBKey]) -> Result<Vec<u8>, FBError> {
let mut decrypted: Vec<U128> = 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<FBKey, FBError> {
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<usize> = (0..c.len())
.choose_multiple(&mut OsRng, n-1);
let r_i: Vec<usize> = (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<U128, FBError> {
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);
}

View File

@ -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<U128>,
c: Vec<U128>,
}
#[derive(Debug)]
pub struct FBKey {
r_i: Vec<usize>,
c_i: Vec<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<FBCrypt, FBError> {
if n < k || k < 2 {
return Err(FBError::InvalidParams)
}
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: &U128) -> Result<FBKey, FBError> {
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<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 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<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: 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},
};

View File

@ -1,5 +0,0 @@
//use false_bottom::FalseBottom;
fn main() {
}