Huge Refactor
Traits have been decoupled, simplifications have been made for generics.
This commit is contained in:
parent
67ab21cdbe
commit
340c439c5c
18
Cargo.toml
18
Cargo.toml
|
@ -1,23 +1,19 @@
|
|||
[package]
|
||||
name = "false-bottom"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
description = "A deniable encryption scheme"
|
||||
repository = "https://codeberg.org/skran/false-bottom"
|
||||
authors = ["K Shiva Kiran <shiva_kr@riseup.net>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.5"
|
||||
crypto-bigint = {version = "0.5.5", features = ["generic-array"]}
|
||||
base64 = "0.21.7"
|
||||
crypto-bigint = { version = "0.5.5", features = ["generic-array"] }
|
||||
base64 = { version = "0.21.7", optional = true }
|
||||
bincode = "1.3.3"
|
||||
typenum = "1.17.0"
|
||||
rand = "0.8.5"
|
||||
rayon = "1.10.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
serde = { version = "1.0.199", features = ["derive"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5.1"
|
||||
|
@ -25,3 +21,7 @@ criterion = "0.5.1"
|
|||
[[bench]]
|
||||
name = "bench"
|
||||
harness = false
|
||||
|
||||
[[example]]
|
||||
name = "export"
|
||||
required-features = ["base64"]
|
||||
|
|
|
@ -1,26 +1,67 @@
|
|||
use criterion::*;
|
||||
use false_bottom::{FB128, FBAlgo};
|
||||
use false_bottom::{FalseBottom, Fb128, Fb256, Fb512};
|
||||
|
||||
pub fn bench(c: &mut Criterion) {
|
||||
let inp = vec![0_u8; 4096000];
|
||||
let mut fb = FB128::init(2, 2).unwrap();
|
||||
let mut group = c.benchmark_group("bench-group");
|
||||
let key = fb.add(&inp);
|
||||
pub fn bench_encryption(c: &mut Criterion) {
|
||||
let inp = vec![0_u8; 1_024_000];
|
||||
let mut fb128 = Fb128::init(2, 2);
|
||||
let mut fb256 = Fb256::init(2, 2);
|
||||
let mut fb512 = Fb512::init(2, 2);
|
||||
let mut group = c.benchmark_group("Encryption with 1MiB data");
|
||||
group.sample_size(21);
|
||||
group.throughput(Throughput::Bytes(inp.len() as u64));
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("4MB null bytes encryption", 4), &inp,
|
||||
BenchmarkId::new("128-bit encryption", 128), &inp,
|
||||
|b, inp| b.iter(|| {
|
||||
fb.add(&inp)
|
||||
fb128.add(&inp)
|
||||
}),
|
||||
);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("4MB null bytes decryption", 4), &key,
|
||||
|b, key| b.iter(|| {
|
||||
fb.decrypt(&key)
|
||||
BenchmarkId::new("256-bit encryption", 256), &inp,
|
||||
|b, inp| b.iter(|| {
|
||||
fb256.add(&inp)
|
||||
}),
|
||||
);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("512-bit encryption", 512), &inp,
|
||||
|b, inp| b.iter(|| {
|
||||
fb512.add(&inp)
|
||||
}),
|
||||
);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench);
|
||||
criterion_main!(benches);
|
||||
pub fn bench_decryption(c: &mut Criterion) {
|
||||
let inp = vec![0_u8; 4_096_000];
|
||||
let mut fb128 = Fb128::init(2, 2);
|
||||
let mut fb256 = Fb256::init(2, 2);
|
||||
let mut fb512 = Fb512::init(2, 2);
|
||||
let key128 = fb128.add(&inp);
|
||||
let key256 = fb256.add(&inp);
|
||||
let key512 = fb512.add(&inp);
|
||||
let mut group = c.benchmark_group("Decryption with 4MiB data");
|
||||
group.sample_size(21);
|
||||
group.throughput(Throughput::Bytes(inp.len() as u64));
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("128-bit decryption", 128), &key128,
|
||||
|b, key128| b.iter(|| {
|
||||
fb128.decrypt(&key128)
|
||||
}),
|
||||
);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("256-bit decryption", 256), &key256,
|
||||
|b, key256| b.iter(|| {
|
||||
fb256.decrypt(&key256)
|
||||
}),
|
||||
);
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new("512-bit decryption", 512), &key512,
|
||||
|b, key512| b.iter(|| {
|
||||
fb512.decrypt(&key512)
|
||||
}),
|
||||
);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(encryption, bench_encryption);
|
||||
criterion_group!(decryption, bench_decryption);
|
||||
criterion_main!(encryption, decryption);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use false_bottom::{FB128, FBAlgo};
|
||||
use false_bottom::{FalseBottom, Fb128};
|
||||
|
||||
fn main() {
|
||||
// Input messages
|
||||
|
@ -6,7 +6,7 @@ fn main() {
|
|||
let real_msg = "I have gathered intel regarding the government's illegal spying";
|
||||
|
||||
// Cipher initialization
|
||||
let mut fb = FB128::init(10, 10);
|
||||
let mut fb = Fb128::init(10, 10);
|
||||
|
||||
// Encryption (Adding messages is not limited to 2)
|
||||
let fake_key = fb.add(&fake_msg.as_bytes());
|
||||
|
@ -19,9 +19,6 @@ fn main() {
|
|||
let fake_str = String::from_utf8(fake_decr).unwrap();
|
||||
let real_str = String::from_utf8(real_decr).unwrap();
|
||||
|
||||
println!("Decrypted Contents:");
|
||||
println!("Fake Message: {fake_str}");
|
||||
println!("Real Message: {real_str}");
|
||||
assert_eq!(fake_msg, fake_str);
|
||||
assert_eq!(real_msg, real_str);
|
||||
}
|
||||
|
|
|
@ -1,39 +1,25 @@
|
|||
use false_bottom::{FB128, FBKey, FBAlgo, Encode};
|
||||
use false_bottom::{Encode, FalseBottom, Fb128, FbKey};
|
||||
|
||||
fn main() {
|
||||
// Cipher Initialization
|
||||
let mut fb = FB128::init(18, 9);
|
||||
let mut fb = Fb128::init(18, 9);
|
||||
|
||||
// Encryption
|
||||
let msg1 = "This is a message";
|
||||
let key1 = fb.add(msg1.as_bytes());
|
||||
let msg2 = "This is another message";
|
||||
let key2 = fb.add(msg2.as_bytes());
|
||||
let msg = "This is a message";
|
||||
let key = fb.add(msg.as_bytes());
|
||||
|
||||
// Export as base64
|
||||
let (cipher, keybase) = fb.as_base64(); // Careful with the order
|
||||
let key1_exp = key1.as_base64();
|
||||
// Export as base64 (Note the order!)
|
||||
let (cipher, keybase) = fb.to_base64();
|
||||
// Or as raw bytes
|
||||
let key2_exp = key2.as_bytes();
|
||||
let key_exp = key.to_bytes();
|
||||
|
||||
// Import from base64
|
||||
let fb_new = FB128::from_base64(&cipher, &keybase).unwrap();
|
||||
let key1_imp = FBKey::from_base64(&key1_exp).unwrap();
|
||||
let fb_imp = Fb128::from_base64(&cipher, &keybase).unwrap();
|
||||
// Or as raw bytes
|
||||
let key2_imp = FBKey::from_bytes(&key2_exp).unwrap();
|
||||
let key_imp = FbKey::from_bytes(&key_exp).unwrap();
|
||||
|
||||
// Decryption
|
||||
let decr1 = fb_new.decrypt(&key1_imp).unwrap();
|
||||
let decr2 = fb_new.decrypt(&key2_imp).unwrap();
|
||||
let decr = fb_imp.decrypt(&key_imp).unwrap();
|
||||
|
||||
// Display
|
||||
println!("
|
||||
CipherText: \n{cipher}\n
|
||||
KeyBase: \n{keybase}\n
|
||||
Key 1: {key1_exp}
|
||||
Key 2: {key2_exp:?}
|
||||
");
|
||||
|
||||
assert_eq!(msg1.as_bytes(), decr1);
|
||||
assert_eq!(msg2.as_bytes(), decr2);
|
||||
assert_eq!(msg.as_bytes(), decr);
|
||||
}
|
||||
|
|
137
src/algo.rs
137
src/algo.rs
|
@ -1,137 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use crate::{FBError, FBKey, FBObj, FBObjTrait, FieldOps, Packing};
|
||||
use crypto_bigint::{NonZero, RandomMod};
|
||||
use rand::{rngs::ThreadRng, seq::index, Rng};
|
||||
use rayon::iter::*;
|
||||
use std::marker::Send;
|
||||
use std::sync::RwLock;
|
||||
|
||||
pub trait FBAlgo<T>
|
||||
where
|
||||
Self: BlockOps<T> + Sync + Send,
|
||||
T: FieldOps + Packing + RandomMod + Send + Sync,
|
||||
{
|
||||
const MODULUS: NonZero<T>;
|
||||
|
||||
/// Creates a new [`FBObj`].
|
||||
/// The keybase and ciphertext are initialized from random values.
|
||||
/// Bounds: `2 <= keybase_len <= cipher_len`
|
||||
/// # Errors
|
||||
/// [InvalidParams](FBError::InvalidParams)
|
||||
fn init(cipher_len: usize, keybase_len: usize) -> FBObj<T> {
|
||||
if cipher_len < keybase_len || keybase_len < 2 {
|
||||
panic!("{}", FBError::InvalidParams);
|
||||
}
|
||||
let mut rng = rand::thread_rng();
|
||||
let r = (0..keybase_len)
|
||||
.map(|_| T::random_mod(&mut rng, &Self::MODULUS))
|
||||
.collect();
|
||||
let c_vec = (0..cipher_len)
|
||||
.map(|_| T::random_mod(&mut rng, &Self::MODULUS))
|
||||
.collect();
|
||||
let c = RwLock::new(c_vec);
|
||||
|
||||
FBObj {c, r}
|
||||
}
|
||||
|
||||
/// Adds the provided message to the ciphertext.
|
||||
fn add(&mut self, msg: &[u8]) -> FBKey {
|
||||
let indices = T::pack(msg)
|
||||
.into_par_iter()
|
||||
.map_init(
|
||||
|| rand::thread_rng(),
|
||||
|rng, index_row| self.add_block(rng, &index_row),
|
||||
)
|
||||
.collect();
|
||||
|
||||
FBKey { indices }
|
||||
}
|
||||
|
||||
/// Decrypts the message that corresponds to the provided key.
|
||||
/// # Errors
|
||||
/// [InvalidKey](FBError::InvalidKey)
|
||||
fn decrypt(&self, key: &FBKey) -> Result<Vec<u8>, FBError> {
|
||||
let decr = key.indices.iter()
|
||||
.map(|index_row| self.decrypt_block(&index_row))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mut msg = T::unpack(decr)?;
|
||||
msg.shrink_to_fit();
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BlockOps<T>
|
||||
where
|
||||
Self: FBObjTrait<T>,
|
||||
T: FieldOps + RandomMod + Send + Sync,
|
||||
{
|
||||
fn add_block(&self, rng: &mut ThreadRng, msg_uint: &T) -> Vec<(usize, usize)> {
|
||||
let r = self.keybase();
|
||||
let n = rng.gen_range(2..=r.len());
|
||||
let r_i = index::sample(rng, r.len(), n);
|
||||
let ri_last = r_i.iter().last()
|
||||
.expect("r_i will contain at least 2 elements");
|
||||
let ri_last_inv = r[ri_last].field_inv();
|
||||
let c_i;
|
||||
let c_len;
|
||||
{
|
||||
let mut c = self.cipher().write().unwrap();
|
||||
c_i = index::sample(rng, c.len(), n - 1);
|
||||
let sum = c_i.iter()
|
||||
.zip(r_i.iter())
|
||||
.map(|(ci, ri)| c[ci].field_mul(&r[ri]))
|
||||
.reduce(|acc, i| acc.field_add(&i))
|
||||
.unwrap();
|
||||
let c_new_el = msg_uint.field_sub(&sum).field_mul(&ri_last_inv);
|
||||
c.push(c_new_el);
|
||||
c_len = c.len();
|
||||
}
|
||||
let indices = c_i.iter()
|
||||
.chain([c_len - 1].into_iter())
|
||||
.zip(r_i.iter())
|
||||
.collect();
|
||||
|
||||
indices
|
||||
}
|
||||
|
||||
fn decrypt_block(&self, indices: &[(usize, usize)]) -> Result<T, FBError> {
|
||||
let (c, r) = (self.cipher().read().unwrap(), self.keybase());
|
||||
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.field_add(&c_el.field_mul(&r_el));
|
||||
}
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypt_u128() {
|
||||
use crypto_bigint::U128;
|
||||
let msg = U128::from_u32(100);
|
||||
let fb = FBObj::<U128>::init(18, 12);
|
||||
let rng = &mut rand::thread_rng();
|
||||
let key = fb.add_block(rng, &msg);
|
||||
let decrypted = fb.decrypt_block(&key).unwrap();
|
||||
assert_eq!(msg, decrypted);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encrypt_bytes() {
|
||||
use crypto_bigint::U128;
|
||||
let input1 = vec![255_u8; 33];
|
||||
let input2 = vec![0_u8; 102];
|
||||
let mut fb = FBObj::<U128>::init(21, 9);
|
||||
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);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pub trait FieldOps {
|
||||
fn field_add(&self, rhs: &Self) -> Self;
|
||||
fn field_sub(&self, rhs: &Self) -> Self;
|
||||
fn field_mul(&self, rhs: &Self) -> Self;
|
||||
fn field_inv(&self) -> Self;
|
||||
}
|
||||
|
||||
pub trait WrappingOps {
|
||||
fn wrapping_add(&self, rhs: &Self) -> Self;
|
||||
fn wrapping_sub(&self, rhs: &Self) -> Self;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use crate::{FbError, FbObj};
|
||||
use crypto_bigint::{ArrayEncoding, generic_array::GenericArray, Uint};
|
||||
use std::sync::RwLock;
|
||||
|
||||
#[cfg(feature = "base64")]
|
||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||
|
||||
/// Provides methods to encode and decode data to and from several formats.
|
||||
pub trait Encode {
|
||||
/// Returns the byte representation of the ciphertext and keybase.
|
||||
fn to_bytes(&self) -> (Vec<u8>, Vec<u8>);
|
||||
|
||||
/// Constructs the `FbObj` from the provided byte representations of
|
||||
/// ciphertext and keybase.
|
||||
/// # Errors
|
||||
/// - [InvalidParams](FbError::InvalidParams) - Are the parameters in the wrong order?
|
||||
fn from_bytes(cipher: &[u8], keybase: &[u8]) -> Result<Self, FbError>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Returns the base64 encoded representation of the ciphertext and keybase.
|
||||
/// Requires `base64` feature to be enabled.
|
||||
#[cfg(feature = "base64")]
|
||||
fn to_base64(&self) -> (String, String);
|
||||
|
||||
/// Constructs the `FbObj` from the provided base64 encoded forms of
|
||||
/// ciphertext and keybase.
|
||||
/// Requires `base64` feature to be enabled.
|
||||
/// # Errors
|
||||
/// - [DecodeError](FbError::DecodeError)
|
||||
/// - [InvalidParams](FbError::InvalidParams) - Are the parameters in the wrong order?
|
||||
#[cfg(feature = "base64")]
|
||||
fn from_base64(cipher: &str, keybase: &str) -> Result<Self, FbError>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> Encode for FbObj<Uint<LIMBS>>
|
||||
where
|
||||
Uint<LIMBS>: ArrayEncoding,
|
||||
{
|
||||
fn to_bytes(&self) -> (Vec<u8>, Vec<u8>) {
|
||||
let c = self.c.read().unwrap()
|
||||
.iter()
|
||||
.flat_map(|bigint| bigint.to_le_byte_array())
|
||||
.collect();
|
||||
let r = self.r.iter()
|
||||
.flat_map(|bigint| bigint.to_le_byte_array())
|
||||
.collect();
|
||||
|
||||
(c, r)
|
||||
}
|
||||
|
||||
fn from_bytes(cipher: &[u8], keybase: &[u8]) -> Result<Self, FbError> {
|
||||
let to_uint = |chunk| Uint::<LIMBS>::from_le_byte_array(GenericArray::clone_from_slice(chunk));
|
||||
let c_vec: Vec<_> = cipher.chunks_exact(Uint::<LIMBS>::BYTES)
|
||||
.map(to_uint)
|
||||
.collect();
|
||||
let r: Vec<_> = keybase.chunks_exact(Uint::<LIMBS>::BYTES)
|
||||
.map(to_uint)
|
||||
.collect();
|
||||
if r.len() > c_vec.len() || r.len() < 2 {
|
||||
return Err(FbError::InvalidParams);
|
||||
}
|
||||
let c = RwLock::new(c_vec);
|
||||
|
||||
Ok(FbObj {c, r})
|
||||
}
|
||||
|
||||
#[cfg(feature = "base64")]
|
||||
fn to_base64(&self) -> (String, String) {
|
||||
let (c, r) = self.to_bytes();
|
||||
|
||||
(BASE64_STANDARD.encode(c), BASE64_STANDARD.encode(r))
|
||||
}
|
||||
|
||||
#[cfg(feature = "base64")]
|
||||
fn from_base64(cipher: &str, keybase: &str) -> Result<Self, FbError> {
|
||||
let c_bytes = BASE64_STANDARD.decode(cipher)
|
||||
.map_err(|_| FbError::DecodeError)?;
|
||||
let r_bytes = BASE64_STANDARD.decode(keybase)
|
||||
.map_err(|_| FbError::DecodeError)?;
|
||||
|
||||
Self::from_bytes(&c_bytes, &r_bytes)
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use crate::{FBError, FBObj, FBObjTrait};
|
||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||
use crypto_bigint::{generic_array::GenericArray, ArrayEncoding, Bounded};
|
||||
use std::sync::RwLock;
|
||||
|
||||
pub trait Encode<T>
|
||||
where
|
||||
Self: FBObjTrait<T>,
|
||||
T: ArrayEncoding + Bounded,
|
||||
{
|
||||
/// Returns the byte representation of the ciphertext and keybase.
|
||||
fn as_bytes(&self) -> (Vec<u8>, Vec<u8>) {
|
||||
let c = self.cipher().read().unwrap()
|
||||
.iter()
|
||||
.flat_map(|bigint| bigint.to_le_byte_array())
|
||||
.collect();
|
||||
let r = self.keybase().iter()
|
||||
.flat_map(|bigint| bigint.to_le_byte_array())
|
||||
.collect();
|
||||
|
||||
(c, r)
|
||||
}
|
||||
|
||||
/// Returns the base64 encoded representation of the ciphertext and keybase.
|
||||
fn as_base64(&self) -> (String, String) {
|
||||
let (c, r) = self.as_bytes();
|
||||
|
||||
(BASE64_STANDARD.encode(c), BASE64_STANDARD.encode(r))
|
||||
}
|
||||
|
||||
/// Constructs the [`FBObj`] from the provided byte representations of
|
||||
/// ciphertext and keybase.
|
||||
/// # Errors
|
||||
/// - [InvalidParams](FBError::InvalidParams) - Are the parameters in the wrong order?
|
||||
fn from_bytes(cipher: &[u8], keybase: &[u8]) -> Result<FBObj<T>, FBError> {
|
||||
let chunk_to_uint = |chunk| T::from_le_byte_array(GenericArray::clone_from_slice(chunk));
|
||||
let c_vec: Vec<T> = cipher.chunks_exact(T::BYTES)
|
||||
.map(chunk_to_uint)
|
||||
.collect();
|
||||
let r: Vec<T> = keybase.chunks_exact(T::BYTES)
|
||||
.map(chunk_to_uint)
|
||||
.collect();
|
||||
if r.len() > c_vec.len() || r.len() < 2 {
|
||||
return Err(FBError::InvalidParams);
|
||||
}
|
||||
let c = RwLock::new(c_vec);
|
||||
|
||||
Ok(FBObj {c, r})
|
||||
}
|
||||
|
||||
/// Constructs the [`FBObj`] from the provided base64 encoded forms of
|
||||
/// ciphertext and keybase.
|
||||
/// # Errors
|
||||
/// - [DecodeError](FBError::DecodeError)
|
||||
/// - [InvalidParams](FBError::InvalidParams) - Are the parameters in the wrong order?
|
||||
fn from_base64(cipher: &str, keybase: &str) -> Result<FBObj<T>, FBError> {
|
||||
let c_bytes = BASE64_STANDARD.decode(cipher)
|
||||
.map_err(|_| FBError::DecodeError)?;
|
||||
let r_bytes = BASE64_STANDARD.decode(keybase)
|
||||
.map_err(|_| FBError::DecodeError)?;
|
||||
|
||||
Self::from_bytes(&c_bytes, &r_bytes)
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FBError {
|
||||
/// Unable to decode the given data.
|
||||
DecodeError,
|
||||
/// The provided key is invalid w.r.t this False Bottom Object.
|
||||
InvalidKey,
|
||||
/// Keybase length (k) or Cipher length (n) out of bounds.
|
||||
/// Valid bounds: (2 <= k <= n).
|
||||
InvalidParams,
|
||||
}
|
||||
|
||||
impl fmt::Display for FBError {
|
||||
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let message = match self {
|
||||
FBError::DecodeError => "Invalid input",
|
||||
FBError::InvalidKey => "Invalid Key used",
|
||||
FBError::InvalidParams => "Keybase len(k) or Cipher len(n) out of bounds. Valid bounds: (2 <= k <= n)",
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("{message}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for FBError {}
|
|
@ -0,0 +1,66 @@
|
|||
use crate::{FbError, FbKey, FbObj, PrimeField, Packing};
|
||||
use crypto_bigint::{ArrayEncoding, NonZero, RandomMod, Uint};
|
||||
use std::sync::RwLock;
|
||||
use rayon::iter::*;
|
||||
|
||||
/// The main interface to the False Bottom algorithm.
|
||||
pub trait FalseBottom {
|
||||
/// Creates a new `FbObj`.
|
||||
/// The keybase and ciphertext are initialized from random values.
|
||||
/// Bounds: `2 <= keybase_len <= cipher_len`
|
||||
/// # Panics
|
||||
/// If Parameters are out of bounds.
|
||||
fn init(cipher_len: usize, keybase_len: usize) -> Self;
|
||||
|
||||
/// Adds the provided message to the ciphertext.
|
||||
fn add(&mut self, msg: &[u8]) -> FbKey;
|
||||
|
||||
/// Decrypts the message that corresponds to the provided key.
|
||||
/// # Errors
|
||||
/// [InvalidKey](FbError::InvalidKey)
|
||||
fn decrypt(&self, key: &FbKey) -> Result<Vec<u8>, FbError>;
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> FalseBottom for FbObj<Uint<LIMBS>>
|
||||
where
|
||||
Uint<LIMBS>: ArrayEncoding + PrimeField
|
||||
{
|
||||
fn init(cipher_len: usize, keybase_len: usize) -> FbObj<Uint<LIMBS>> {
|
||||
let modulus = NonZero::<Uint<LIMBS>>::new(Uint::<LIMBS>::PRIME).unwrap();
|
||||
if cipher_len < keybase_len || keybase_len < 2 {
|
||||
panic!("Valid bounds are: 2 <= keybase_len <= cipher_len");
|
||||
}
|
||||
let mut rng = rand::thread_rng();
|
||||
let r = (0..keybase_len)
|
||||
.map(|_| Uint::<LIMBS>::random_mod(&mut rng, &modulus))
|
||||
.collect();
|
||||
let c_vec = (0..cipher_len)
|
||||
.map(|_| Uint::<LIMBS>::random_mod(&mut rng, &modulus))
|
||||
.collect();
|
||||
let c = RwLock::new(c_vec);
|
||||
|
||||
FbObj {c, r}
|
||||
}
|
||||
|
||||
fn add(&mut self, msg: &[u8]) -> FbKey {
|
||||
let indices = Uint::<LIMBS>::pack(msg)
|
||||
.into_par_iter()
|
||||
//.map(|index_row| self.add_block(&mut rand::thread_rng(), &index_row))
|
||||
.map_init(
|
||||
|| rand::thread_rng(),
|
||||
|rng, index_row| self.add_block(rng, &index_row),
|
||||
)
|
||||
.collect();
|
||||
|
||||
FbKey(indices)
|
||||
}
|
||||
|
||||
fn decrypt(&self, key: &FbKey) -> Result<Vec<u8>, FbError> {
|
||||
let decr = key.0.iter()
|
||||
.map(|index_row| self.decrypt_block(&index_row))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let msg = Uint::<LIMBS>::unpack(decr)?;
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use std::fmt;
|
||||
|
||||
/// Enum representing all the possible errors in this crate.
|
||||
#[derive(Debug)]
|
||||
pub enum FbError {
|
||||
/// Unable to decode the given data.
|
||||
DecodeError,
|
||||
|
||||
/// The provided key is invalid w.r.t this False Bottom Object.
|
||||
InvalidKey,
|
||||
|
||||
/// One or more of the parameters are invalid.
|
||||
/// Valid bounds: (2 <= keybase_len <= cipher_len).
|
||||
InvalidParams,
|
||||
}
|
||||
|
||||
impl fmt::Display for FbError {
|
||||
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let message = match self {
|
||||
FbError::DecodeError => "Invalid input",
|
||||
FbError::InvalidKey => "Invalid Key used",
|
||||
FbError::InvalidParams => "One or more of the parameters are invalid or out of bounds. Valid bounds: (2 <= keybase_len <= cipher_len)",
|
||||
};
|
||||
|
||||
f.write_fmt(format_args!("{message}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for FbError {}
|
58
src/fbkey.rs
58
src/fbkey.rs
|
@ -1,47 +1,55 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use crate::FBError;
|
||||
use base64::prelude::{BASE64_STANDARD, Engine};
|
||||
use crate::FbError;
|
||||
use bincode::{Options, DefaultOptions};
|
||||
|
||||
/// A key object that is specific to a message.
|
||||
pub struct FBKey {
|
||||
pub(crate) indices: Vec<Vec<(usize, usize)>>,
|
||||
}
|
||||
#[cfg(feature = "base64")]
|
||||
use base64::prelude::{BASE64_STANDARD, Engine};
|
||||
|
||||
impl FBKey {
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Returns the byte representation of the key.
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
/// An object that represents a message specific key.
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct FbKey(pub(crate) Vec<Vec<(usize, usize)>>);
|
||||
|
||||
|
||||
impl FbKey {
|
||||
|
||||
/// Returns the byte representation of the message specific key.
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let binc = DefaultOptions::new();
|
||||
binc.serialize(&self.indices)
|
||||
|
||||
binc.serialize(&self.0)
|
||||
.expect("Should be fine")
|
||||
}
|
||||
|
||||
/// Returns the base64 encoded representation of the key.
|
||||
pub fn as_base64(&self) -> String {
|
||||
BASE64_STANDARD.encode(&self.as_bytes())
|
||||
}
|
||||
|
||||
/// Constructs the key from the provided bytes.
|
||||
/// Constructs the message specific key from the provided bytes.
|
||||
/// # Errors
|
||||
/// [DecodeError](FBError::DecodeError)
|
||||
pub fn from_bytes(fbkey: &[u8]) -> Result<FBKey, FBError> {
|
||||
/// [DecodeError](FbError::DecodeError)
|
||||
pub fn from_bytes(fbkey: &[u8]) -> Result<FbKey, FbError> {
|
||||
let binc = DefaultOptions::new();
|
||||
let indices: Vec<_> = binc.deserialize(&fbkey)
|
||||
.map_err(|_| FBError::DecodeError)?;
|
||||
.map_err(|_| FbError::DecodeError)?;
|
||||
if indices.len() < 2 {
|
||||
return Err(FBError::DecodeError);
|
||||
return Err(FbError::DecodeError);
|
||||
}
|
||||
|
||||
Ok (FBKey {indices})
|
||||
Ok (FbKey(indices))
|
||||
}
|
||||
|
||||
/// Constructs the key from the provided base64 encoded form.
|
||||
/// Returns the base64 encoded representation of the message specific key.
|
||||
#[cfg(feature = "base64")]
|
||||
pub fn to_base64(&self) -> String {
|
||||
BASE64_STANDARD.encode(&self.to_bytes())
|
||||
}
|
||||
|
||||
/// Constructs the message specific key from the provided base64 encoded form.
|
||||
/// # Errors
|
||||
/// [DecodeError](FBError::DecodeError)
|
||||
pub fn from_base64(key_str: &str) -> Result<FBKey, FBError> {
|
||||
/// [DecodeError](FbError::DecodeError)
|
||||
#[cfg(feature = "base64")]
|
||||
pub fn from_base64(key_str: &str) -> Result<FbKey, FbError> {
|
||||
let indice_bytes = BASE64_STANDARD.decode(key_str)
|
||||
.map_err(|_| FBError::DecodeError)?;
|
||||
.map_err(|_| FbError::DecodeError)?;
|
||||
|
||||
Self::from_bytes(&indice_bytes)
|
||||
}
|
||||
|
|
100
src/fbobj.rs
100
src/fbobj.rs
|
@ -1,37 +1,89 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pub mod fb128;
|
||||
|
||||
use crate::{BlockOps, Encode, FieldOps};
|
||||
use crypto_bigint::{ArrayEncoding, Bounded, RandomMod};
|
||||
use crate::{FbError, PrimeField};
|
||||
use crypto_bigint::{U128, U256, U512, Uint};
|
||||
use rand::{Rng, rngs::ThreadRng, seq::index};
|
||||
use std::sync::RwLock;
|
||||
use std::marker::Sync;
|
||||
|
||||
/// The False Bottom Object holds the ciphertext and the keybase. The provided type aliases can be used to pick a block size.
|
||||
pub struct FBObj<T> {
|
||||
// The False bottom object. It holds the ciphertext(c) and the keybase(r).
|
||||
pub struct FbObj<T> {
|
||||
pub(crate) c: RwLock<Vec<T>>,
|
||||
pub(crate) r: Vec<T>,
|
||||
}
|
||||
|
||||
pub trait FBObjTrait<T> {
|
||||
fn cipher(&self) -> &RwLock<Vec<T>>;
|
||||
fn keybase(&self) -> &Vec<T>;
|
||||
}
|
||||
/// An FbObj with block size of 128 bits.
|
||||
pub type Fb128 = FbObj<U128>;
|
||||
/// An FbObj with a block size of 256 bits.
|
||||
pub type Fb256 = FbObj<U256>;
|
||||
/// An FbObj with a block size of 512 bits.
|
||||
pub type Fb512 = FbObj<U512>;
|
||||
|
||||
impl<T> FBObjTrait<T> for FBObj<T> {
|
||||
fn cipher(&self) -> &RwLock<Vec<T>> {
|
||||
&self.c
|
||||
impl<const LIMBS: usize> FbObj<Uint<LIMBS>>
|
||||
where
|
||||
Uint<LIMBS>: PrimeField
|
||||
{
|
||||
pub(crate) fn add_block(
|
||||
&self,
|
||||
rng: &mut ThreadRng,
|
||||
msg_uint: &Uint<LIMBS>
|
||||
) -> Vec<(usize, usize)> {
|
||||
let r = &self.r;
|
||||
let n = rng.gen_range(2..=r.len());
|
||||
let r_i = index::sample(rng, r.len(), n);
|
||||
let ri_last = r_i.iter().last()
|
||||
.expect("r_i will contain at least 2 elements");
|
||||
let ri_last_inv = r[ri_last].field_inv();
|
||||
let c_i;
|
||||
let c_len;
|
||||
{
|
||||
let mut c = self.c.write().unwrap();
|
||||
c_i = index::sample(rng, c.len(), n - 1);
|
||||
let sum = c_i.iter()
|
||||
.zip(r_i.iter())
|
||||
.map(|(ci, ri)| c[ci].field_mul(&r[ri]))
|
||||
.reduce(|acc, i| acc.field_add(&i))
|
||||
.unwrap();
|
||||
let c_new_el = msg_uint.field_sub(&sum).field_mul(&ri_last_inv);
|
||||
c.push(c_new_el);
|
||||
c_len = c.len();
|
||||
}
|
||||
let indices = c_i.into_iter()
|
||||
.chain([c_len - 1].into_iter())
|
||||
.zip(r_i.into_iter())
|
||||
.collect();
|
||||
|
||||
indices
|
||||
}
|
||||
fn keybase(&self) -> &Vec<T> {
|
||||
&self.r
|
||||
|
||||
pub(crate) fn decrypt_block(
|
||||
&self,
|
||||
indices: &[(usize, usize)]
|
||||
) -> Result<Uint<LIMBS>, FbError> {
|
||||
let (c, r) = (self.c.read().unwrap(), &self.r);
|
||||
if indices.len() > r.len() {
|
||||
return Err(FbError::InvalidKey);
|
||||
}
|
||||
let mut msg = Uint::<LIMBS>::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.field_add(&c_el.field_mul(&r_el));
|
||||
}
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> BlockOps<T> for FBObj<T>
|
||||
where
|
||||
T: FieldOps + RandomMod + Send + Sync
|
||||
{}
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::FalseBottom;
|
||||
|
||||
impl<T> Encode<T> for FBObj<T>
|
||||
where
|
||||
T: ArrayEncoding + Bounded
|
||||
{}
|
||||
#[test]
|
||||
fn test_block_operations() {
|
||||
let msg = U512::from_u16(369);
|
||||
let fb = Fb512::init(12, 12);
|
||||
let key = fb.add_block(&mut rand::thread_rng(), &msg);
|
||||
let decrypted = fb.decrypt_block(&key).unwrap();
|
||||
assert_eq!(msg, decrypted);
|
||||
}
|
||||
}
|
||||
|
|
102
src/lib.rs
102
src/lib.rs
|
@ -1,65 +1,95 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//! ## Usage
|
||||
//! Unlike traditional encryption algorithms that output ciphertext to a given plaintext,
|
||||
//! False Bottom is a [deniable encryption](https://en.wikipedia.org/wiki/Deniable_encryption) scheme.
|
||||
//! Unlike traditional encryption algorithms that map a given plaintext to ciphertext,
|
||||
//! False Bottom works by "adding" messages to an existing ciphertext.
|
||||
//! As such, the initial ciphertext and the keybase are generated from random data.
|
||||
//! The ciphertext then grows as messages are added whereas the keybase is fixed
|
||||
//! after initialization and is used to generate the keys for the ciphertext.
|
||||
//! The parameters for the [`init()`](FBObj::init()) function determine the size
|
||||
//! after initialization and is used to generate message specific keys for the ciphertext.
|
||||
//! The parameters for the [`init()`](FalseBottom::init()) function determine the size
|
||||
//! (in number of blocks) of the initial ciphertext and keybase respectively.
|
||||
//! Type aliases over [`FBObj`] are provided to pick a block size.
|
||||
//! Ex: [`FB128`] corresponds to a block size of 128 bits.
|
||||
//! Type aliases [`Fb128`], [`Fb256`] and [`Fb512`] are provided to pick a block size.
|
||||
//!
|
||||
//! ### Cipher Initialization:
|
||||
//! ```rust
|
||||
//! use false_bottom::{FB128, FBAlgo};
|
||||
//! // 15 blocks of ciphertext and 12 blocks of keybase with a block size of 128 bits.
|
||||
//! let fb = FB128::init(15, 12);
|
||||
//! ```
|
||||
//! use false_bottom::{FalseBottom, Fb128};
|
||||
//! // Initialize 15 blocks of ciphertext and 12 blocks of keybase with a block size of 128 bits.
|
||||
//! let fb = Fb128::init(15, 12);
|
||||
//! ```
|
||||
//!
|
||||
//! ### Adding Messages:
|
||||
//! Messages can be added using the [`add()`](FBObj::add()) method.
|
||||
//! This method returns an object [`FBKey`] that represents the corresponding key for this message.
|
||||
//! This key can only be used to decrypt this message.
|
||||
//! ```rust
|
||||
//! let msg = b"Hello World!";
|
||||
//! let key = fb.add(&msg);
|
||||
//! Multiple messages can be added using the [`add()`](FalseBottom::add()) method.
|
||||
//! This method returns an object [`FbKey`] that represents the message specific key for this message.
|
||||
//! Only this key can decrypt the added message.
|
||||
//! ```
|
||||
//! # use false_bottom::{FalseBottom, Fb128};
|
||||
//! # let mut fb = Fb128::init(15, 12);
|
||||
//! let msg = "Hello World!";
|
||||
//! let key = fb.add(msg.as_bytes());
|
||||
//! ```
|
||||
//!
|
||||
//! ### Decryption:
|
||||
//! The [`decrypt()`](FBObj::decrypt()) method returns the message that corresponds
|
||||
//! to the provided [`FBKey`].
|
||||
//! ```rust
|
||||
//! The [`decrypt()`](FalseBottom::decrypt()) method returns the message that corresponds
|
||||
//! to the provided [`FbKey`].
|
||||
//! ```
|
||||
//! # use false_bottom::{FalseBottom, Fb128};
|
||||
//! # let mut fb = Fb128::init(15, 12);
|
||||
//! # let msg = "Hello World!";
|
||||
//! # let key = fb.add(msg.as_bytes());
|
||||
//! let decrypted = fb.decrypt(&key).unwrap();
|
||||
//! ```
|
||||
//! There is also an example [here](https://codeberg.org/skran/false-bottom/src/branch/main/examples/encryption.rs).
|
||||
//! An example has been provided [here](https://codeberg.org/skran/false-bottom/src/branch/main/examples/encryption.rs).
|
||||
//!
|
||||
//! ### Import and Export
|
||||
//! Available formats: Raw bytes and Base64 encoded.
|
||||
//! Refer to the [example](https://codeberg.org/skran/false-bottom/src/branch/main/examples/export.rs)
|
||||
//! Available formats: Raw bytes and Base64 encoded.
|
||||
//! #### Raw Bytes:
|
||||
//! The [`Encode`] trait provides methods for export and import of data.
|
||||
//! ```
|
||||
//! use false_bottom::{Encode, FalseBottom, Fb128, FbError, FbKey};
|
||||
//! # let mut fb = Fb128::init(15, 12);
|
||||
//! # let key = fb.add(b"Hello");
|
||||
//! // Exporting
|
||||
//! let (ciphertext_bytes, keybase_bytes) = fb.to_bytes();
|
||||
//! let key_bytes = key.to_bytes();
|
||||
//! // Importing
|
||||
//! let fb_imported = Fb128::from_bytes(&ciphertext_bytes, &keybase_bytes)?;
|
||||
//! let key_imported = FbKey::from_bytes(&key_bytes)?;
|
||||
//! # Ok::<(), FbError>(())
|
||||
//! ```
|
||||
//! #### Base64 Encoded:
|
||||
//! The feature `base64` needs to be enabled in your `Cargo.toml`.
|
||||
//! ```
|
||||
//! use false_bottom::{Encode, FalseBottom, Fb128, FbError, FbKey};
|
||||
//! # let mut fb = Fb128::init(15, 12);
|
||||
//! # let key = fb.add(b"Hello");
|
||||
//! // Exporting
|
||||
//! let (ciphertext_base64, keybase_base64) = fb.to_base64();
|
||||
//! let key_base64 = key.to_base64();
|
||||
//! // Importing
|
||||
//! let fb_imported = Fb128::from_base64(&ciphertext_base64, &keybase_base64)?;
|
||||
//! let key_imported = FbKey::from_base64(&key_base64)?;
|
||||
//! # Ok::<(), FbError>(())
|
||||
//! ```
|
||||
//! An example has been provided [here](https://codeberg.org/skran/false-bottom/src/branch/main/examples/export.rs).
|
||||
|
||||
mod algo;
|
||||
mod arithmetic;
|
||||
mod encoding;
|
||||
mod errors;
|
||||
mod encode;
|
||||
mod falsebottom;
|
||||
mod fberror;
|
||||
mod fbkey;
|
||||
mod fbobj;
|
||||
mod primefield;
|
||||
mod packing;
|
||||
|
||||
pub use crate::{
|
||||
errors::FBError,
|
||||
fbobj::{
|
||||
FBObj,
|
||||
fb128::FB128,
|
||||
},
|
||||
algo::FBAlgo,
|
||||
fbkey::FBKey,
|
||||
encoding::Encode,
|
||||
encode::Encode,
|
||||
falsebottom::FalseBottom,
|
||||
fbobj::{Fb128, Fb256, Fb512},
|
||||
fbkey::FbKey,
|
||||
fberror::FbError,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
algo::BlockOps,
|
||||
arithmetic::{FieldOps, WrappingOps},
|
||||
fbobj::FBObjTrait,
|
||||
fbobj::FbObj,
|
||||
packing::Packing,
|
||||
primefield::PrimeField,
|
||||
};
|
||||
|
|
|
@ -1,64 +1,68 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use crypto_bigint::{ArrayEncoding, Bounded, generic_array::GenericArray};
|
||||
use std::cmp::PartialOrd;
|
||||
use crate::{FBError, WrappingOps};
|
||||
use crypto_bigint::{ArrayEncoding, Uint, generic_array::GenericArray};
|
||||
use crate::{FbError, PrimeField};
|
||||
|
||||
/* PACKING SCHEME
|
||||
* This is used to transform message byte chunks into field elements.
|
||||
* Every x byte chunks of a message are combined to form a field element n.
|
||||
* In case a number n >= P (out of field) is formed,
|
||||
* the values P-1 (i.e R_BOUND) followed by n-R_BOUND are appended to the output vec.
|
||||
* While unpacking, P-1 is used as a signaling element to extract n
|
||||
* from the successive element after adding back R_BOUND.
|
||||
* This scheme transforms byte chunks into field elements and vice versa.
|
||||
* Every x byte chunks are combined to form a field element n.
|
||||
* In case the number formed is n >= P where P is the right bound of the prime field,
|
||||
* the values P-1 (i.e LIMIT) followed by n-LIMIT are appended to the output vec.
|
||||
* While unpacking, LIMIT is used as a signaling element to extract n
|
||||
* from the successive element after adding back LIMIT.
|
||||
*/
|
||||
|
||||
pub trait Packing
|
||||
pub(crate) trait Packing
|
||||
where
|
||||
Self: ArrayEncoding + Bounded + PartialOrd + WrappingOps
|
||||
Self: Sized
|
||||
{
|
||||
const R_BOUND: Self;
|
||||
const LIMIT: Self;
|
||||
fn pack(inp: &[u8]) -> Vec<Self>;
|
||||
fn unpack(inp: Vec<Self>) -> Result<Vec<u8>, FbError>;
|
||||
}
|
||||
|
||||
fn pack(inp: &[u8]) -> Vec<Self> {
|
||||
let mut out: Vec<Self> = inp.chunks(Self::BYTES)
|
||||
impl<const LIMBS: usize> Packing for Uint<LIMBS>
|
||||
where
|
||||
Uint<LIMBS>: ArrayEncoding + PrimeField,
|
||||
{
|
||||
const LIMIT: Uint<LIMBS> = Uint::<LIMBS>::PRIME.wrapping_sub(&Uint::<LIMBS>::ONE);
|
||||
fn pack(inp: &[u8]) -> Vec<Uint<LIMBS>> {
|
||||
let mut out: Vec<_> = inp.chunks(Uint::<LIMBS>::BYTES)
|
||||
.flat_map(|inp_chunk| {
|
||||
let mut out_chunk = GenericArray::<u8, Self::ByteSize>::default();
|
||||
let mut out_chunk = GenericArray::<u8, <Uint<LIMBS> as ArrayEncoding>::ByteSize>::default();
|
||||
out_chunk[..inp_chunk.len()].copy_from_slice(inp_chunk);
|
||||
let mut out_uint = Self::from_le_byte_array(out_chunk);
|
||||
if out_uint >= Self::R_BOUND {
|
||||
out_uint = out_uint.wrapping_sub(&Self::R_BOUND);
|
||||
vec![Self::R_BOUND, out_uint]
|
||||
let mut out_uint = Uint::from_le_byte_array(out_chunk);
|
||||
if out_uint >= Self::LIMIT {
|
||||
out_uint = out_uint.wrapping_sub(&Self::LIMIT);
|
||||
vec![Self::LIMIT, out_uint]
|
||||
} else {
|
||||
vec![out_uint]
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let inp_chunk_last = inp.chunks_exact(Self::BYTES)
|
||||
let inp_chunk_last = inp.chunks_exact(Uint::<LIMBS>::BYTES)
|
||||
.remainder();
|
||||
let mut pad_chunk = GenericArray::<u8, Self::ByteSize>::default();
|
||||
pad_chunk[Self::BYTES-1] = Self::BYTES as u8;
|
||||
if inp_chunk_last.len() != 0 {
|
||||
pad_chunk[Self::BYTES - 1] += (Self::BYTES - inp_chunk_last.len()) as u8;
|
||||
}
|
||||
out.push(Self::from_le_byte_array(pad_chunk));
|
||||
let pad_uint = Uint::from_u8((Uint::<LIMBS>::BYTES - inp_chunk_last.len()) as u8);
|
||||
out.push(pad_uint);
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn unpack(inp: Vec<Self>) -> Result<Vec<u8>, FBError> {
|
||||
let pad_len: usize = inp.last()
|
||||
.ok_or(FBError::InvalidKey)?
|
||||
.to_le_byte_array()[Self::BYTES-1] as usize;
|
||||
if pad_len > 2 * Self::BYTES - 1 || pad_len < Self::BYTES {
|
||||
return Err(FBError::InvalidKey);
|
||||
fn unpack(inp: Vec<Uint<LIMBS>>) -> Result<Vec<u8>, FbError> {
|
||||
let mut pad_len: usize = inp.last()
|
||||
.ok_or(FbError::InvalidKey)?
|
||||
.to_be_byte_array()[Uint::<LIMBS>::BYTES - 1] as usize;
|
||||
if pad_len >= Uint::<LIMBS>::BYTES || pad_len < 1 {
|
||||
return Err(FbError::InvalidKey);
|
||||
}
|
||||
pad_len += Uint::<LIMBS>::BYTES;
|
||||
let mut extract = false;
|
||||
let mut out: Vec<u8> = inp.into_iter()
|
||||
.filter_map(|el| {
|
||||
if extract {
|
||||
extract = false;
|
||||
let el = el.wrapping_add(&Self::R_BOUND);
|
||||
let el = el.wrapping_add(&Self::LIMIT);
|
||||
Some(el.to_le_byte_array())
|
||||
} else if el == Self::R_BOUND {
|
||||
} else if el == Self::LIMIT {
|
||||
extract = true;
|
||||
None
|
||||
} else {
|
||||
|
@ -67,9 +71,8 @@ where
|
|||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
let trunc_len = out.len().checked_sub(pad_len)
|
||||
.ok_or(FBError::InvalidKey)?;
|
||||
.ok_or(FbError::InvalidKey)?;
|
||||
out.truncate(trunc_len);
|
||||
|
||||
Ok(out)
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
use crypto_bigint::{Limb, U128, U256, U512, Uint};
|
||||
|
||||
pub trait PrimeField {
|
||||
const PRIME: Self;
|
||||
fn field_add(&self, rhs: &Self) -> Self;
|
||||
fn field_sub(&self, rhs: &Self) -> Self;
|
||||
fn field_mul(&self, rhs: &Self) -> Self;
|
||||
fn field_inv(&self) -> Self;
|
||||
}
|
||||
|
||||
// The prime number's position from the right end of the field.
|
||||
trait PrimeValue {
|
||||
const PRIME_RPOS: u16;
|
||||
const RPOS_LIMB: Limb = Limb::from_u16(Self::PRIME_RPOS);
|
||||
}
|
||||
|
||||
impl<const LIMBS: usize> PrimeField for Uint<LIMBS>
|
||||
where
|
||||
Uint<LIMBS>: PrimeValue
|
||||
{
|
||||
const PRIME: Self = Self::MAX.wrapping_sub(&Self::from_u16(Self::PRIME_RPOS - 1));
|
||||
fn field_add(&self, rhs: &Self) -> Self {
|
||||
self.add_mod_special(rhs, Self::RPOS_LIMB)
|
||||
}
|
||||
fn field_sub(&self, rhs: &Self) -> Self {
|
||||
self.sub_mod_special(rhs, Self::RPOS_LIMB)
|
||||
}
|
||||
fn field_mul(&self, rhs: &Self) -> Self {
|
||||
self.mul_mod_special(rhs, Self::RPOS_LIMB)
|
||||
}
|
||||
fn field_inv(&self) -> Self {
|
||||
//self.inv_odd_mod(&Self::PRIME).0
|
||||
self.inv_odd_mod_bounded(&Self::PRIME, self.bits_vartime(), Uint::<LIMBS>::BITS).0
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeValue for U128 {
|
||||
const PRIME_RPOS: u16 = 159;
|
||||
}
|
||||
|
||||
impl PrimeValue for U256 {
|
||||
const PRIME_RPOS: u16 = 189;
|
||||
}
|
||||
|
||||
impl PrimeValue for U512 {
|
||||
const PRIME_RPOS: u16 = 569;
|
||||
}
|
Loading…
Reference in New Issue