use rand_core::{CryptoRng, RngCore};
use rug::{Complete, Integer};
use crate::{utils, Ciphertext, EncryptionKey, Nonce, Plaintext};
use crate::{Error, Reason};
#[derive(Clone)]
pub struct DecryptionKey {
ek: EncryptionKey,
lambda: Integer,
mu: Integer,
p: Integer,
q: Integer,
crt_mod_nn: utils::CrtExp,
exp_n: utils::Exponent,
exp_lambda: utils::Exponent,
}
impl DecryptionKey {
pub fn generate(rng: &mut (impl RngCore + CryptoRng)) -> Result<Self, Error> {
let p = utils::generate_safe_prime(rng, 1536);
let q = utils::generate_safe_prime(rng, 1536);
Self::from_primes(p, q)
}
#[allow(clippy::many_single_char_names)]
pub fn from_primes(p: Integer, q: Integer) -> Result<Self, Error> {
if p == q {
return Err(Reason::InvalidPQ.into());
}
let pm1 = Integer::from(&p - 1);
let qm1 = Integer::from(&q - 1);
let ek = EncryptionKey::from_n((&p * &q).complete());
let lambda = pm1.clone().lcm(&qm1);
if lambda.cmp0().is_eq() {
return Err(Reason::InvalidPQ.into());
}
let u = lambda.invert_ref(ek.n()).ok_or(Reason::InvalidPQ)?.into();
let crt_mod_nn = utils::CrtExp::build_nn(&p, &q).ok_or(Reason::BuildFastExp)?;
let exp_n = crt_mod_nn.prepare_exponent(ek.n());
let exp_lambda = crt_mod_nn.prepare_exponent(&lambda);
Ok(Self {
ek,
lambda,
mu: u,
p,
q,
crt_mod_nn,
exp_n,
exp_lambda,
})
}
pub fn decrypt(&self, c: &Ciphertext) -> Result<Plaintext, Error> {
if !utils::in_mult_group(c, self.ek.nn()) {
return Err(Reason::Decrypt.into());
}
let a = self
.crt_mod_nn
.exp(c, &self.exp_lambda)
.ok_or(Reason::Decrypt)?;
let l = self.ek.l(&a).ok_or(Reason::Decrypt)?;
let plaintext = (l * &self.mu) % self.ek.n();
if Integer::from(&plaintext << 1) >= *self.n() {
Ok(plaintext - self.n())
} else {
Ok(plaintext)
}
}
pub fn encrypt_with(&self, x: &Plaintext, nonce: &Nonce) -> Result<Ciphertext, Error> {
if !self.ek.in_signed_group(x) || !utils::in_mult_group(nonce, self.n()) {
return Err(Reason::Encrypt.into());
}
let x = if x.cmp0().is_ge() {
x.clone()
} else {
(x + self.n()).complete()
};
let a = (Integer::ONE + x * self.ek.n()) % self.ek.nn();
let b = self
.crt_mod_nn
.exp(nonce, &self.exp_n)
.ok_or(Reason::Encrypt)?;
Ok((a * b) % self.ek.nn())
}
pub fn encrypt_with_random(
&self,
rng: &mut (impl RngCore + CryptoRng),
x: &Plaintext,
) -> Result<(Ciphertext, Nonce), Error> {
let nonce = utils::sample_in_mult_group(rng, self.ek.n());
let ciphertext = self.encrypt_with(x, &nonce)?;
Ok((ciphertext, nonce))
}
pub fn omul(&self, scalar: &Integer, ciphertext: &Ciphertext) -> Result<Ciphertext, Error> {
if !utils::in_mult_group_abs(scalar, self.n())
|| !utils::in_mult_group(ciphertext, self.ek.nn())
{
return Err(Reason::Ops.into());
}
let e = self.crt_mod_nn.prepare_exponent(scalar);
Ok(self.crt_mod_nn.exp(ciphertext, &e).ok_or(Reason::Ops)?)
}
pub fn encryption_key(&self) -> &EncryptionKey {
&self.ek
}
pub fn n(&self) -> &Integer {
self.ek.n()
}
pub fn lambda(&self) -> &Integer {
&self.lambda
}
pub fn mu(&self) -> &Integer {
&self.mu
}
pub fn p(&self) -> &Integer {
&self.p
}
pub fn q(&self) -> &Integer {
&self.q
}
pub fn bits_length(&self) -> u32 {
self.p.significant_bits().min(self.q.significant_bits())
}
}