From 182832bc6ecfb3c116900469051f2a91212fcf75 Mon Sep 17 00:00:00 2001 From: K Shiva Kiran Date: Tue, 26 Mar 2024 07:17:51 +0530 Subject: [PATCH] Adding Trait generics --- src/crypt.rs | 130 +++++++++++++++++++++++++++++++++++++++++++++ src/crypt/fb128.rs | 53 ++++++++++++++++++ src/lib.rs | 2 + src/pack.rs | 71 +++++++++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 src/crypt.rs create mode 100644 src/crypt/fb128.rs create mode 100644 src/pack.rs diff --git a/src/crypt.rs b/src/crypt.rs new file mode 100644 index 0000000..adc360e --- /dev/null +++ b/src/crypt.rs @@ -0,0 +1,130 @@ +use crypto_bigint::{U128, Limb, RandomMod, NonZero, Encoding}; +use crate::{FBError, FBKey, pack::Packing}; +use rand::{Rng, prelude::IteratorRandom}; +mod fb128; + +pub trait FB: FBBlock +where + T: RandomMod + SpecialMod + InvMod + Encoding + std::cmp::PartialOrd + Packing +{ + const MODULUS: NonZero; + + fn init(n: usize, k: usize) -> Result, FBError> + where + T: RandomMod + { + if n < k || k < 2 { + return Err(FBError::InvalidParams); + } + let mut rng = rand::thread_rng(); + let r = (0..k) + .map(|_| T::random_mod(&mut rng, &Self::MODULUS)) + .collect(); + let c = (0..n) + .map(|_| T::random_mod(&mut rng, &Self::MODULUS)) + .collect(); + + Ok(FBStruct { c, r }) + } + + fn add(&mut self, msg: &[u8]) -> FBKey { + let indices = T::pack(msg).into_iter() + .map(|msg_uint| self.add_u128(&msg_uint)) + .collect(); + + FBKey { indices } + } + + fn decrypt(&self, key: &FBKey) -> Result, FBError> { + let decr = key.indices.iter() + .map(|index_row| self.decrypt_u128(&index_row)) + .collect::, _>>()?; + let msg = T::unpack(&decr)?; + + Ok(msg) + } +} + +trait SpecialMod { + fn mul_mod_special(&self, rhs: &Self, c: Limb) -> Self; + fn add_mod_special(&self, rhs: &Self, c: Limb) -> Self; + fn sub_mod_special(&self, rhs: &Self, c: Limb) -> Self; +} + +use crypto_bigint::CtChoice; +trait InvMod { + fn inv_mod(&self, modulus: &Self) -> (Self, CtChoice) + where Self: Sized; +} + +impl SpecialMod for U128 { + fn mul_mod_special(&self, rhs: &Self, c: Limb) -> Self {self.mul_mod_special(rhs, c)} + fn add_mod_special(&self, rhs: &Self, c: Limb) -> Self {self.add_mod_special(rhs, c)} + fn sub_mod_special(&self, rhs: &Self, c: Limb) -> Self {self.sub_mod_special(rhs, c)} +} + +impl InvMod for U128 { + fn inv_mod(&self, modulus: &Self) -> (Self, CtChoice) { + self.inv_mod(modulus) + } +} + +trait FBBlock +where + T: RandomMod + SpecialMod + InvMod +{ + const P: T; + const P_POS: Limb; + + fn cipher(&self) -> &Vec; + fn cipher_mut(&mut self) -> &mut Vec; + fn keybase(&self) -> &Vec; + + fn add_u128(&mut self, msg_uint: &T) -> Vec<(usize, usize)> { + let c = self.cipher(); + let r = self.keybase(); + let mut rng = rand::thread_rng(); + let n = rng.gen_range(2..=r.len()); + let mut c_i = (0..c.len()).choose_multiple(&mut rng, n - 1); + let r_i = (0..r.len()).choose_multiple(&mut rng, n); + let mut sum = T::ZERO; + for (&ci, &ri) in c_i.iter().zip(r_i.iter()) { + sum = sum.add_mod_special( + &c[ci].mul_mod_special(&r[ri], Self::P_POS), + Self::P_POS); + } + let ri_last = *r_i.last().expect("r_i will contain at least 2 elements"); + let mod_inv = r[ri_last].inv_mod(&Self::P).0; + let c_new_el = msg_uint.sub_mod_special(&sum, Self::P_POS) + .mul_mod_special(&mod_inv, Self::P_POS); + let c = self.cipher_mut(); + c.push(c_new_el); + c_i.push(c.len() - 1); + let indices = c_i.into_iter() + .zip(r_i.into_iter()) + .collect(); + + indices + } + + fn decrypt_u128(&self, indices: &[(usize, usize)]) -> Result { + let (r, c) = (self.keybase(), self.cipher()); + if indices.len() > r.len() { + return Err(FBError::InvalidKey); + } + let mut msg = T::ZERO; + for &(ci, ri) in indices { + let c_el = c.get(ci).ok_or(FBError::InvalidKey)?; + let r_el = r.get(ri).ok_or(FBError::InvalidKey)?; + msg = msg.add_mod_special( + &c_el.mul_mod_special(&r_el, Self::P_POS), + Self::P_POS); + } + + Ok(msg) + } +} +pub struct FBStruct { + pub(crate) c: Vec, + pub(crate) r: Vec, +} diff --git a/src/crypt/fb128.rs b/src/crypt/fb128.rs new file mode 100644 index 0000000..fcdb45f --- /dev/null +++ b/src/crypt/fb128.rs @@ -0,0 +1,53 @@ +use crypto_bigint::{U128, Limb, NonZero}; +use crate::{ + crypt::{FB, FBStruct, FBBlock}, + pack::Packing, +}; + +impl FB for FBStruct { + const MODULUS: NonZero = NonZero::::const_new(Self::P).0; +} + +impl FBBlock for FBStruct { + const P: U128 = U128::MAX.wrapping_sub(&U128::from_u8(159 - 1)); + const P_POS: Limb = Limb::from_u8(159); + + fn cipher(&self) -> &Vec { + &self.c + } + + fn cipher_mut(&mut self) -> &mut Vec { + &mut self.c + } + + fn keybase(&self) -> &Vec { + &self.r + } +} + +impl Packing for U128 { + const P_MINUS_ONE: U128 = U128::MAX.wrapping_sub(&U128::from_u8(159)); + const BYTES: usize = 16; +} + +#[test] +fn encrypt_u128() { + let msg = U128::from_u32(100); + let mut fb = FBStruct::init(20, 12).unwrap(); + let key = fb.add_u128(&msg); + let decrypted = fb.decrypt_u128(&key).unwrap(); + assert_eq!(msg, decrypted); +} + +#[test] +fn encrypt_bytes() { + let input1 = vec![255_u8; 150]; + let input2 = vec![0_u8; 102]; + let mut fb = FBStruct::init(21, 12).unwrap(); + let key1 = fb.add(&input1); + let key2 = fb.add(&input2); + let decr1 = fb.decrypt(&key1).unwrap(); + let decr2 = fb.decrypt(&key2).unwrap(); + assert_eq!(input1, decr1); + assert_eq!(input2, decr2); +} diff --git a/src/lib.rs b/src/lib.rs index c68bee2..1148ad9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ mod encoding; mod errors; mod false_bottom; mod packing; +mod pack; +mod crypt; pub use crate::{ errors::FBError, diff --git a/src/pack.rs b/src/pack.rs new file mode 100644 index 0000000..7575f98 --- /dev/null +++ b/src/pack.rs @@ -0,0 +1,71 @@ +use crypto_bigint::{Encoding}; +use crate::FBError; + +/* PACKING SCHEME + * When a number n >= P (out of field) is formed from the byte chunks, + * the values P-1 followed by n-3000 are appended to the output vec. + * Subtraction is done to bring n within the field (i.e less than P). + * While unpacking, P-1 is used as a signaling element to extract n + * from the successive element by adding back 3000. + */ + +pub(crate) trait Packing +where + T: Encoding + std::cmp::PartialOrd +{ + const P_MINUS_ONE: T; + const BYTES: usize; + + fn pack(inp: &[u8]) -> Vec { + let mut out = Vec::with_capacity(inp.len()/Self::BYTES + 2); + inp.chunks(Self::BYTES) + .for_each(|inp_chunk| { + let mut out_chunk = [0_u8; Self::BYTES]; + out_chunk[..inp_chunk.len()].copy_from_slice(inp_chunk); + let mut out_uint = T::from_le_bytes(out_chunk); + if out_uint >= Self::P_MINUS_ONE { + out.push(Self::P_MINUS_ONE); + out_uint = out_uint.wrapping_sub(&T::from_u16(3000)); + } + out.push(out_uint); + }); + let inp_chunk_last = inp.chunks_exact(Self::BYTES) + .remainder(); + let mut pad_chunk = [Self::BYTES as u8; Self::BYTES]; + if inp_chunk_last.len() > 0 { + pad_chunk[Self::BYTES as u8 - 1] += Self::BYTES as u8 - inp_chunk_last.len() as u8; + } + out.push(T::from_le_bytes(pad_chunk)); + out.shrink_to_fit(); + + out + } + + fn unpack(inp: &[T]) -> Result, FBError> { + let pad_len = inp.last() + .ok_or(FBError::InvalidKey)? + .to_le_bytes()[15] as usize; + if pad_len > 31 { + return Err(FBError::InvalidKey); + } + let mut out = Vec::with_capacity(inp.len()*16); + let mut add_3k = false; + for i in inp { + if add_3k { + let orig = i.wrapping_add(&T::from_u16(3000)); + out.extend(orig.to_le_bytes().into_iter()); + add_3k = false; + } else if *i == Self::P_MINUS_ONE { + add_3k = true; + } else { + out.extend(i.to_le_bytes().into_iter()); + } + } + let trunc_len = out.len().checked_sub(pad_len) + .ok_or(FBError::InvalidKey)?; + out.truncate(trunc_len); + out.shrink_to_fit(); + + Ok(out) + } +}