use super::PKEncryptionScheme;
use qfall_math::{
error::MathError,
integer::Z,
integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolynomialRingZq},
rational::Q,
traits::Pow,
};
use qfall_tools::utils::{
common_encodings::{decode_value_from_polynomialringzq, encode_value_in_polynomialringzq},
common_moduli::new_anticyclic,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct RingLPR {
n: Z, q: ModulusPolynomialRingZq, alpha: Q, }
impl RingLPR {
pub fn new(n: impl Into<Z>, q: impl Into<Modulus>, alpha: impl Into<Q>) -> Self {
let n: Z = n.into();
let q = new_anticyclic(&n, q).unwrap();
let alpha: Q = alpha.into();
Self { n, q, alpha }
}
pub fn new_from_n(n: impl Into<Z>) -> Self {
let n = n.into();
assert!(
n >= 10,
"Choose n >= 10 as this function does not return parameters ensuring proper correctness of the scheme otherwise."
);
let (mut q, mut alpha) = Self::gen_new_public_parameters(&n);
let mut out = Self {
n: n.clone(),
q,
alpha,
};
while out.check_correctness().is_err() || out.check_security().is_err() {
(q, alpha) = Self::gen_new_public_parameters(&n);
out = Self {
n: n.clone(),
q,
alpha,
};
}
out
}
fn gen_new_public_parameters(n: &Z) -> (ModulusPolynomialRingZq, Q) {
let n_i64 = i64::try_from(n).unwrap();
let upper_bound: Z = n.pow(3).unwrap();
let lower_bound = upper_bound.div_ceil(2);
let q = Z::sample_prime_uniform(&lower_bound, &upper_bound).unwrap();
let factor = match n_i64 {
1..=20 => 1,
21..=40 => 2,
41..=80 => 3,
81..=160 => 4,
_ => 5,
};
let alpha = 1 / (factor * n.sqrt() * n.log(2).unwrap().pow(3).unwrap());
let q = new_anticyclic(n, q).unwrap();
(q, alpha)
}
pub fn check_correctness(&self) -> Result<(), MathError> {
let n_i64 = i64::try_from(&self.n)?;
if self.n <= Z::ONE {
return Err(MathError::InvalidIntegerInput(String::from(
"n must be chosen bigger than 1.",
)));
}
let result = self.n.is_perfect_power();
let err_msg = String::from(
"n is not a perfect power of 2, \
which is required for the correctness of this scheme.",
);
if let Some((root, _)) = result {
if root != 2 {
return Err(MathError::InvalidIntegerInput(err_msg));
}
} else {
return Err(MathError::InvalidIntegerInput(err_msg));
}
let factor = match n_i64 {
1..=20 => 1,
21..=40 => 2,
41..=80 => 3,
81..=160 => 4,
_ => 5,
};
if self.alpha > 1 / (factor * self.n.sqrt() * self.n.log(2).unwrap().pow(3).unwrap()) {
return Err(MathError::InvalidIntegerInput(String::from(
"Correctness is not guaranteed as α >= 1 / (sqrt(n) * log^3 n), \
but α < 1 / (sqrt(n) * log^3 n) is required. Please check the documentation!",
)));
}
Ok(())
}
pub fn check_security(&self) -> Result<(), MathError> {
let q = Z::from(&self.q.get_q());
if &q * &self.alpha < 2 * self.n.sqrt() {
return Err(MathError::InvalidIntegerInput(String::from(
"Security is not guaranteed as q * α < 2 * sqrt(n), but q * α >= 2 * sqrt(n) is required.",
)));
}
Ok(())
}
}
impl Default for RingLPR {
fn default() -> Self {
Self::new(16, 2399, 0.0039)
}
}
impl PKEncryptionScheme for RingLPR {
type Cipher = (PolynomialRingZq, PolynomialRingZq);
type PublicKey = (PolynomialRingZq, PolynomialRingZq);
type SecretKey = PolynomialRingZq;
fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) {
let a = PolynomialRingZq::sample_uniform(&self.q);
let s = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q())
.unwrap();
let e = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q())
.unwrap();
let b = &a * &s + e;
((a, b), s)
}
fn enc(&self, pk: &Self::PublicKey, message: impl Into<Z>) -> Self::Cipher {
let message: Z = message.into().abs();
let mu = message % Z::from(2).pow(&self.n).unwrap();
let mu_q_half = encode_value_in_polynomialringzq(mu, 2, &self.q).unwrap();
let r = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q())
.unwrap();
let e1 = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q())
.unwrap();
let e2 = PolynomialRingZq::sample_discrete_gauss(&self.q, 0, &self.alpha * &self.q.get_q())
.unwrap();
let u = &pk.0 * &r + e1;
let v = &pk.1 * &r + e2 + mu_q_half;
(u, v)
}
fn dec(&self, sk: &Self::SecretKey, cipher: &Self::Cipher) -> Z {
let result = &cipher.1 - sk * &cipher.0;
decode_value_from_polynomialringzq(&result, 2).unwrap()
}
}
#[cfg(test)]
mod test_pp_generation {
use super::RingLPR;
use super::Z;
#[test]
fn new_availability() {
let _ = RingLPR::new(2u8, 2u32, 2u64);
let _ = RingLPR::new(2u16, 2i32, 2i64);
let _ = RingLPR::new(2i16, 2u32, 2u8);
let _ = RingLPR::new(Z::from(2), 2u8, 2i8);
}
#[test]
fn suitable_security_params() {
let n_choices = [16, 32, 64, 128, 256, 512, 1024];
for n in n_choices {
let _ = RingLPR::new_from_n(n);
}
}
#[test]
fn default_suitable() {
let scheme = RingLPR::default();
assert!(scheme.check_correctness().is_ok());
assert!(scheme.check_security().is_ok());
}
#[test]
fn choice_valid() {
let n_choices = [16, 32, 64, 128, 256, 512, 1024];
for n in n_choices {
let scheme = RingLPR::new_from_n(n);
assert!(scheme.check_correctness().is_ok());
assert!(scheme.check_security().is_ok());
}
}
#[test]
fn non_power_of_2_n() {
let scheme = RingLPR::new(7, 17, 0.01);
assert!(scheme.check_correctness().is_err())
}
#[test]
#[allow(clippy::needless_borrows_for_generic_args)]
fn availability() {
let _ = RingLPR::new_from_n(16u8);
let _ = RingLPR::new_from_n(16u16);
let _ = RingLPR::new_from_n(16u32);
let _ = RingLPR::new_from_n(16u64);
let _ = RingLPR::new_from_n(16i8);
let _ = RingLPR::new_from_n(16i16);
let _ = RingLPR::new_from_n(16i32);
let _ = RingLPR::new_from_n(16i64);
let _ = RingLPR::new_from_n(Z::from(16));
let _ = RingLPR::new_from_n(&Z::from(16));
}
#[test]
#[should_panic]
fn invalid_n() {
RingLPR::new_from_n(9);
}
}
#[cfg(test)]
mod test_ring_lpr {
use super::RingLPR;
use crate::pk_encryption::PKEncryptionScheme;
use qfall_math::integer::Z;
#[test]
fn cycle_small_n() {
let scheme = RingLPR::default();
let (pk, sk) = scheme.key_gen();
let messages = [0, 1, 2, 15, 70, 256, 580, 1000, 4000, 8000, 65535];
for message in messages {
let cipher = scheme.enc(&pk, message);
let m = scheme.dec(&sk, &cipher);
assert_eq!(Z::from(message), m);
}
}
#[test]
fn cycle_large_n() {
let scheme = RingLPR::new_from_n(64);
let (pk, sk) = scheme.key_gen();
let messages = [
0,
1,
2,
15,
70,
256,
580,
1_000,
4_000,
8_000,
20_000,
80_000,
240_000,
4_000_000,
100_000_000,
];
for message in messages {
let cipher = scheme.enc(&pk, message);
let m = scheme.dec(&sk, &cipher);
assert_eq!(Z::from(message), m);
}
}
#[test]
fn modulus_application() {
let messages = [65536];
let scheme = RingLPR::default();
let (pk, sk) = scheme.key_gen();
for msg in messages {
let cipher = scheme.enc(&pk, msg);
let m = scheme.dec(&sk, &cipher);
assert_eq!(Z::ZERO, m);
}
}
}