use crate::{mod_in, Ciphertext, EncryptionKey};
use serde::{Deserialize, Serialize};
use unknown_order::BigNumber;
use zeroize::Zeroize;
#[derive(Clone, Debug, Deserialize, Serialize, Zeroize)]
#[zeroize(drop)]
pub struct DecryptionKey {
pub(crate) pk: EncryptionKey,
pub(crate) lambda: BigNumber,
pub(crate) totient: BigNumber,
pub(crate) u: BigNumber,
}
#[cfg(feature = "wasm")]
wasm_slice_impl!(DecryptionKey);
impl DecryptionKey {
pub fn random() -> Option<Self> {
let mut p = BigNumber::prime(1024);
let mut q = BigNumber::prime(1024);
let res = Self::with_primes_unchecked(&p, &q);
p.zeroize();
q.zeroize();
res
}
pub fn with_primes(p: &BigNumber, q: &BigNumber) -> Option<Self> {
if !p.is_prime() || !q.is_prime() {
return None;
}
Self::with_primes_unchecked(p, q)
}
#[allow(clippy::many_single_char_names)]
pub fn with_primes_unchecked(p: &BigNumber, q: &BigNumber) -> Option<Self> {
if p == q {
return None;
}
let pm1: BigNumber = p - 1;
let qm1: BigNumber = q - 1;
let n = p * q;
let nn = &n * &n;
let pk = EncryptionKey {
n: n.clone(),
nn: nn.clone(),
};
let lambda = pm1.lcm(&qm1);
if lambda.is_zero() {
return None;
}
let totient = &pm1 * &qm1;
let t: BigNumber = &n + 1;
let tt = t.modpow(&lambda, &nn);
let uu = pk.l(&tt).map(|uu| uu.invert(&n));
match uu {
None => None,
Some(u_inv) => u_inv.map(|u| DecryptionKey {
pk,
lambda,
totient,
u,
}),
}
}
pub fn decrypt(&self, c: &Ciphertext) -> Option<Vec<u8>> {
if !mod_in(&c, &self.pk.nn) {
return None;
}
let a = c.modpow(&self.lambda, &self.pk.nn);
self.pk.l(&a).map(|l| {
let m = l.modmul(&self.u, &self.pk.n);
m.to_bytes()
})
}
pub fn to_bytes(&self) -> Vec<u8> {
let bytes = DecryptionKeyBytes {
n: self.pk.n.to_bytes(),
lambda: self.lambda.to_bytes(),
totient: self.totient.to_bytes(),
u: self.u.to_bytes(),
};
serde_bare::to_vec(&bytes).unwrap()
}
pub fn from_bytes<B: AsRef<[u8]>>(data: B) -> Result<Self, String> {
let data = data.as_ref();
let bytes =
serde_bare::from_slice::<DecryptionKeyBytes>(data).map_err(|e| e.to_string())?;
let pk = EncryptionKey::from_bytes(bytes.n.as_slice())?;
Ok(Self {
pk,
lambda: BigNumber::from_slice(bytes.lambda.as_slice()),
totient: BigNumber::from_slice(bytes.totient.as_slice()),
u: BigNumber::from_slice(bytes.u.as_slice()),
})
}
pub fn n(&self) -> &BigNumber {
self.pk.n()
}
pub fn lambda(&self) -> &BigNumber {
&self.lambda
}
pub fn totient(&self) -> &BigNumber {
&self.totient
}
pub fn u(&self) -> &BigNumber {
&self.u
}
}
#[derive(Serialize, Deserialize)]
struct DecryptionKeyBytes {
n: Vec<u8>,
lambda: Vec<u8>,
totient: Vec<u8>,
u: Vec<u8>,
}