use super::IBEScheme;
use crate::{
hash::sha256::hash_to_mat_zq_sha256,
pk_encryption::{DualRegev, PKEncryptionScheme},
};
use qfall_math::{
error::MathError,
integer::{MatZ, Z},
integer_mod_q::{MatZq, Modulus},
rational::{MatQ, Q},
traits::{Concatenate, MatrixDimensions, Pow},
};
use qfall_tools::{
primitive::psf::{PSF, PSFGPV},
sample::g_trapdoor::gadget_parameters::GadgetParameters,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize)]
pub struct DualRegevIBE {
pub dual_regev: DualRegev,
pub psf: PSFGPV,
storage: HashMap<String, MatZ>,
}
impl DualRegevIBE {
pub fn new(
n: impl Into<Z>, q: impl Into<Modulus>, r: impl Into<Q>, alpha: impl Into<Q>, ) -> Self {
let n = n.into();
let q = q.into();
let r = r.into();
let alpha = alpha.into();
let gadget = GadgetParameters::init_default(&n, &q);
let log_q = Z::from(&q).log_ceil(2).unwrap();
let n_log_q = &n * &log_q;
let m = &gadget.m_bar + n_log_q;
let psf = PSFGPV { gp: gadget, s: r };
Self {
psf,
dual_regev: DualRegev::new(n, m, q, alpha),
storage: HashMap::new(),
}
}
pub fn new_from_n(n: impl Into<Z>) -> Self {
let n: Z = n.into();
if n < 2 {
panic!("Security parameter n has to be larger than 1");
}
let n_i64 = i64::try_from(&n).unwrap();
let power = match n_i64 {
2..=3 => 10,
4 => 7,
5..=7 => 6,
_ => 5,
};
let upper_bound: Z = n.pow(power).unwrap();
let lower_bound = upper_bound.div_ceil(2);
let q = Modulus::from(Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap());
let gadget = GadgetParameters::init_default(&n, &q);
let log_q = Z::from(&q).log_ceil(2).unwrap();
let n_log_q = &n * &log_q;
let m = &gadget.m_bar + n_log_q;
let r: Q = m.sqrt();
let alpha = 1 / (&r * 2 * (&m + Z::ONE).sqrt() * (n).log(2).unwrap());
let psf = PSFGPV { gp: gadget, s: r };
Self {
psf,
dual_regev: DualRegev::new(n, m, q, alpha),
storage: HashMap::new(),
}
}
pub fn check_security(&self) -> Result<(), MathError> {
let q = Q::from(&self.dual_regev.q);
if q < (5 * &self.psf.s) * (&self.dual_regev.m + Q::ONE) {
return Err(MathError::InvalidIntegerInput(String::from(
"Security is not guaranteed as q < 5 * r * (m + 1), but q >= 5 * r * (m + 1) is required.",
)));
}
if self.psf.s < self.dual_regev.m.sqrt() {
return Err(MathError::InvalidIntegerInput(String::from(
"Security is not guaranteed as r < sqrt(m), but r >= sqrt(m) is required.",
)));
}
if self.dual_regev.m <= (&self.dual_regev.n + 1) * &q.log(2).unwrap() {
return Err(MathError::InvalidIntegerInput(String::from(
"Security is not guaranteed as m <= (n + 1) * log(q), \
but m > (n + 1) * log(q) is required.",
)));
}
Ok(())
}
pub fn check_correctness(&self) -> Result<(), MathError> {
if self.dual_regev.n <= Z::ONE {
return Err(MathError::InvalidIntegerInput(String::from(
"n must be chosen bigger than 1.",
)));
}
if self.dual_regev.alpha
> 1 / (2 * &self.psf.s * (&self.dual_regev.m + Z::ONE).sqrt())
* self.dual_regev.n.log(2).unwrap()
{
return Err(MathError::InvalidIntegerInput(String::from(
"Correctness is not guaranteed as α > 1/(r * sqrt(m) * log(n)), but α <= 1/(2 * r * sqrt(m) * log(n)) is required.",
)));
}
Ok(())
}
}
impl Default for DualRegevIBE {
fn default() -> Self {
DualRegevIBE::new_from_n(4)
}
}
impl IBEScheme for DualRegevIBE {
type Cipher = MatZq;
type MasterPublicKey = MatZq;
type MasterSecretKey = (MatZ, MatQ);
type SecretKey = MatZ;
type Identity = String;
fn setup(&self) -> (Self::MasterPublicKey, Self::MasterSecretKey) {
self.psf.trap_gen()
}
fn extract(
&mut self,
master_pk: &Self::MasterPublicKey,
master_sk: &Self::MasterSecretKey,
identity: &Self::Identity,
) -> Self::SecretKey {
if let Some(value) = self.storage.get(&format!(
"{master_pk} {} {} {identity}",
master_sk.0, master_sk.1
)) {
return value.clone();
}
let u = hash_to_mat_zq_sha256(identity, &self.dual_regev.n, 1, &self.dual_regev.q);
let secret_key = self.psf.samp_p(master_pk, master_sk, &u);
self.storage.insert(
format!("{master_pk} {} {} {identity}", master_sk.0, master_sk.1),
secret_key.clone(),
);
secret_key
}
fn enc(
&self,
master_pk: &Self::MasterPublicKey,
identity: &Self::Identity,
message: impl Into<Z>,
) -> Self::Cipher {
let identity_based_pk =
hash_to_mat_zq_sha256(identity, master_pk.get_num_rows(), 1, master_pk.get_mod());
self.dual_regev.enc(
&master_pk.concat_horizontal(&identity_based_pk).unwrap(),
message,
)
}
fn dec(&self, sk_id: &Self::SecretKey, cipher: &Self::Cipher) -> Z {
self.dual_regev.dec(sk_id, cipher)
}
}
#[cfg(test)]
mod test_dual_regev_ibe {
use super::DualRegevIBE;
use crate::identity_based_encryption::IBEScheme;
use qfall_math::integer::Z;
#[test]
fn new_availability() {
let _ = DualRegevIBE::new(2u8, 2u16, 2u32, 2u64);
let _ = DualRegevIBE::new(2u16, 2u64, 2i32, 2i64);
let _ = DualRegevIBE::new(2i16, 2i64, 2u32, 2u8);
let _ = DualRegevIBE::new(Z::from(2), Z::from(2), 2u8, 2i8);
}
#[test]
#[allow(clippy::needless_borrows_for_generic_args)]
fn availability() {
let _ = DualRegevIBE::new_from_n(4u8);
let _ = DualRegevIBE::new_from_n(4u16);
let _ = DualRegevIBE::new_from_n(4u32);
let _ = DualRegevIBE::new_from_n(4u64);
let _ = DualRegevIBE::new_from_n(4i8);
let _ = DualRegevIBE::new_from_n(4i16);
let _ = DualRegevIBE::new_from_n(4i32);
let _ = DualRegevIBE::new_from_n(4i64);
let _ = DualRegevIBE::new_from_n(Z::from(4));
let _ = DualRegevIBE::new_from_n(&Z::from(4));
}
#[test]
#[should_panic]
fn invalid_n() {
DualRegevIBE::new_from_n(1);
}
#[test]
fn cycle_zero_default() {
let msg = Z::ZERO;
let id = String::from("Hello World!");
let mut cryptosystem = DualRegevIBE::default();
let (pk, sk) = cryptosystem.setup();
let id_sk = cryptosystem.extract(&pk, &sk, &id);
let cipher = cryptosystem.enc(&pk, &id, &msg);
let m = cryptosystem.dec(&id_sk, &cipher);
assert_eq!(msg, m)
}
#[test]
fn cycle_one_default() {
let msg = Z::ONE;
let id = String::from("Hello World!");
let mut cryptosystem = DualRegevIBE::default();
let (pk, sk) = cryptosystem.setup();
let id_sk = cryptosystem.extract(&pk, &sk, &id);
let cipher = cryptosystem.enc(&pk, &id, &msg);
let m = cryptosystem.dec(&id_sk, &cipher);
assert_eq!(msg, m)
}
#[test]
fn cycle_zero_small_n() {
let msg = Z::ZERO;
let id = String::from("Hel213lo World!");
let mut cryptosystem = DualRegevIBE::new_from_n(5);
let (pk, sk) = cryptosystem.setup();
let id_sk = cryptosystem.extract(&pk, &sk, &id);
let cipher = cryptosystem.enc(&pk, &id, &msg);
let m = cryptosystem.dec(&id_sk, &cipher);
assert_eq!(msg, m);
}
#[test]
fn cycle_one_small_n() {
let msg = Z::ONE;
let id = String::from("Hel213lo World!");
let mut cryptosystem = DualRegevIBE::new_from_n(5);
let (pk, sk) = cryptosystem.setup();
let id_sk = cryptosystem.extract(&pk, &sk, &id);
let cipher = cryptosystem.enc(&pk, &id, &msg);
let m = cryptosystem.dec(&id_sk, &cipher);
assert_eq!(msg, m);
}
#[test]
fn new_from_n() {
for i in 1..=5 {
let msg = Z::ONE;
let id = format!("Hello World!{i}");
let mut cryptosystem = DualRegevIBE::default();
cryptosystem.check_security().unwrap();
cryptosystem.check_correctness().unwrap();
let (pk, sk) = cryptosystem.setup();
let id_sk = cryptosystem.extract(&pk, &sk, &id);
for _j in 1..=100 {
let cipher = cryptosystem.enc(&pk, &id, &msg);
let m = cryptosystem.dec(&id_sk, &cipher);
assert_eq!(msg, m);
}
}
}
#[test]
fn extract_storage_same_identity_mk_pk() {
let id = "Hello World!".to_string();
let mut cryptosystem = DualRegevIBE::default();
let (pk, sk) = cryptosystem.setup();
let id_sk_1 = cryptosystem.extract(&pk, &sk, &id);
let id_sk_2 = cryptosystem.extract(&pk, &sk, &id);
assert_eq!(id_sk_1, id_sk_2)
}
#[test]
fn extract_storage_same_identity_different_mk_pk() {
let id = "Hello World!".to_string();
let mut cryptosystem = DualRegevIBE::default();
let (pk_1, sk_1) = cryptosystem.setup();
let (pk_2, sk_2) = cryptosystem.setup();
let id_sk_1 = cryptosystem.extract(&pk_1, &sk_1, &id);
let id_sk_2 = cryptosystem.extract(&pk_2, &sk_2, &id);
assert_ne!(id_sk_1, id_sk_2)
}
}