use crate::pk_encryption::PKEncryptionScheme;
use qfall_math::{
integer::{MatPolyOverZ, PolyOverZ, Z},
integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq, PolynomialRingZq},
};
use qfall_tools::{
compression::LossyCompressionFIPS203,
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 KPKE {
q: ModulusPolynomialRingZq, k: i64, eta_1: i64, eta_2: i64, d_u: i64, d_v: i64, }
impl KPKE {
pub fn ml_kem_512() -> Self {
let q = new_anticyclic(256, 3329).unwrap();
Self {
q,
k: 2,
eta_1: 3,
eta_2: 2,
d_u: 10,
d_v: 4,
}
}
pub fn ml_kem_768() -> Self {
let q = new_anticyclic(256, 3329).unwrap();
Self {
q,
k: 3,
eta_1: 2,
eta_2: 2,
d_u: 10,
d_v: 4,
}
}
pub fn ml_kem_1024() -> Self {
let q = new_anticyclic(256, 3329).unwrap();
Self {
q,
k: 4,
eta_1: 2,
eta_2: 2,
d_u: 11,
d_v: 5,
}
}
}
impl PKEncryptionScheme for KPKE {
type PublicKey = (MatPolynomialRingZq, MatPolynomialRingZq);
type SecretKey = MatPolynomialRingZq;
type Cipher = (MatPolyOverZ, PolyOverZ);
fn key_gen(&self) -> (Self::PublicKey, Self::SecretKey) {
let mat_a = MatPolynomialRingZq::sample_uniform(self.k, self.k, &self.q);
let vec_s = MatPolynomialRingZq::sample_binomial_with_offset(
self.k,
1,
&self.q,
-self.eta_1,
2 * self.eta_1,
0.5,
)
.unwrap();
let vec_e = MatPolynomialRingZq::sample_binomial_with_offset(
self.k,
1,
&self.q,
-self.eta_1,
2 * self.eta_1,
0.5,
)
.unwrap();
let vec_t = &mat_a * &vec_s + vec_e;
let pk = (mat_a.transpose(), vec_t);
let sk = vec_s;
(pk, sk)
}
fn enc(&self, pk: &Self::PublicKey, message: impl Into<Z>) -> Self::Cipher {
let vec_y = MatPolynomialRingZq::sample_binomial_with_offset(
self.k,
1,
&self.q,
-self.eta_1,
2 * self.eta_1,
0.5,
)
.unwrap();
let vec_e_1 = MatPolynomialRingZq::sample_binomial_with_offset(
self.k,
1,
&self.q,
-self.eta_2,
2 * self.eta_2,
0.5,
)
.unwrap();
let e_2 = PolynomialRingZq::sample_binomial_with_offset(
&self.q,
-self.eta_2,
2 * self.eta_2,
0.5,
)
.unwrap();
let vec_u = &pk.0 * &vec_y + vec_e_1;
let mu = encode_value_in_polynomialringzq(message, 2, &self.q).unwrap();
let v = pk.1.dot_product(&vec_y).unwrap() + e_2 + mu;
let vec_u = vec_u.lossy_compress(self.d_u);
let v = v.lossy_compress(self.d_v);
(vec_u, v)
}
fn dec(&self, sk: &Self::SecretKey, (u, v): &Self::Cipher) -> Z {
let u = MatPolynomialRingZq::lossy_decompress(u, self.d_u, &self.q);
let v = PolynomialRingZq::lossy_decompress(v, self.d_v, &self.q);
let w = v - sk.dot_product(&u).unwrap();
decode_value_from_polynomialringzq(&w, 2).unwrap()
}
}
#[cfg(test)]
mod test_kpke {
use crate::pk_encryption::{PKEncryptionScheme, k_pke::KPKE};
#[test]
fn correctness() {
let k_pkes = [KPKE::ml_kem_512(), KPKE::ml_kem_768(), KPKE::ml_kem_1024()];
for k_pke in k_pkes {
let messages = [0, 1, 13, 255, 2047, 4294967295_u32];
for message in messages {
let (pk, sk) = k_pke.key_gen();
let c = k_pke.enc(&pk, message);
let m = k_pke.dec(&sk, &c);
assert_eq!(message, m);
}
}
}
}