use crate::constants::{SAFE_PRIME_1024, SAFE_PRIME_2048, SAFE_PRIME_3072};
use scicrypt_bigint::UnsignedInteger;
use scicrypt_traits::cryptosystems::{
Associable, AsymmetricCryptosystem, DecryptionKey, EncryptionKey,
};
use scicrypt_traits::homomorphic::HomomorphicMultiplication;
use scicrypt_traits::randomness::GeneralRng;
use scicrypt_traits::randomness::SecureRng;
use scicrypt_traits::security::BitsOfSecurity;
use serde::{Deserialize, Serialize};
#[derive(Clone)]
pub struct IntegerElGamal {
modulus: UnsignedInteger,
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct IntegerElGamalPK {
pub h: UnsignedInteger,
pub modulus: UnsignedInteger,
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
pub struct IntegerElGamalCiphertext {
pub c1: UnsignedInteger,
pub c2: UnsignedInteger,
}
impl Associable<IntegerElGamalPK> for IntegerElGamalCiphertext {}
pub struct IntegerElGamalSK {
pub(crate) key: UnsignedInteger,
}
impl AsymmetricCryptosystem for IntegerElGamal {
type PublicKey = IntegerElGamalPK;
type SecretKey = IntegerElGamalSK;
fn setup(security_param: &BitsOfSecurity) -> Self {
let public_key_len = security_param.to_public_key_bit_length();
IntegerElGamal {
modulus: UnsignedInteger::from_string_leaky(
match public_key_len {
1024 => SAFE_PRIME_1024.to_string(),
2048 => SAFE_PRIME_2048.to_string(),
3072 => SAFE_PRIME_3072.to_string(),
_ => panic!("No parameters available for this security parameter"),
},
16,
public_key_len,
),
}
}
fn generate_keys<R: SecureRng>(
&self,
rng: &mut GeneralRng<R>,
) -> (IntegerElGamalPK, IntegerElGamalSK) {
let q = &self.modulus >> 1;
let secret_key = UnsignedInteger::random_below(&q, rng);
let public_key = UnsignedInteger::from(4u64).pow_mod(&secret_key, &self.modulus);
(
IntegerElGamalPK {
h: public_key,
modulus: self.modulus.clone(),
},
IntegerElGamalSK { key: secret_key },
)
}
}
impl EncryptionKey for IntegerElGamalPK {
type Input = UnsignedInteger;
type Plaintext = UnsignedInteger;
type Ciphertext = IntegerElGamalCiphertext;
type Randomness = UnsignedInteger;
fn encrypt_without_randomness(&self, plaintext: &Self::Plaintext) -> Self::Ciphertext {
IntegerElGamalCiphertext {
c1: UnsignedInteger::new(1, 1),
c2: plaintext.clone() % &self.modulus,
}
}
fn randomize<R: SecureRng>(
&self,
ciphertext: Self::Ciphertext,
rng: &mut GeneralRng<R>,
) -> Self::Ciphertext {
let q = &self.modulus >> 1;
let y = UnsignedInteger::random_below(&q, rng);
self.randomize_with(ciphertext, &y)
}
fn randomize_with(
&self,
ciphertext: Self::Ciphertext,
randomness: &Self::Randomness,
) -> Self::Ciphertext {
IntegerElGamalCiphertext {
c1: &ciphertext.c1 * &UnsignedInteger::from(4u64).pow_mod(randomness, &self.modulus),
c2: (&ciphertext.c2 * &self.h.pow_mod(randomness, &self.modulus)) % &self.modulus,
}
}
}
impl DecryptionKey<IntegerElGamalPK> for IntegerElGamalSK {
fn decrypt_raw(
&self,
public_key: &IntegerElGamalPK,
ciphertext: &IntegerElGamalCiphertext,
) -> UnsignedInteger {
(&ciphertext.c2
* &ciphertext
.c1
.pow_mod(&self.key, &public_key.modulus)
.invert(&public_key.modulus)
.unwrap())
% &public_key.modulus
}
fn decrypt_identity_raw(
&self,
public_key: &IntegerElGamalPK,
ciphertext: &<IntegerElGamalPK as EncryptionKey>::Ciphertext,
) -> bool {
ciphertext.c2 == ciphertext.c1.pow_mod(&self.key, &public_key.modulus)
}
}
impl HomomorphicMultiplication for IntegerElGamalPK {
fn mul(
&self,
ciphertext_a: &Self::Ciphertext,
ciphertext_b: &Self::Ciphertext,
) -> Self::Ciphertext {
IntegerElGamalCiphertext {
c1: (&ciphertext_a.c1 * &ciphertext_b.c1) % &self.modulus,
c2: (&ciphertext_a.c2 * &ciphertext_b.c2) % &self.modulus,
}
}
fn pow(&self, ciphertext: &Self::Ciphertext, input: &Self::Input) -> Self::Ciphertext {
IntegerElGamalCiphertext {
c1: ciphertext.c1.pow_mod(input, &self.modulus),
c2: ciphertext.c2.pow_mod(input, &self.modulus),
}
}
}
#[cfg(test)]
mod tests {
use crate::cryptosystems::integer_el_gamal::IntegerElGamal;
use rand_core::OsRng;
use scicrypt_bigint::UnsignedInteger;
use scicrypt_traits::cryptosystems::{
Associable, AsymmetricCryptosystem, DecryptionKey, EncryptionKey,
};
use scicrypt_traits::randomness::GeneralRng;
#[test]
fn test_encrypt_decrypt_generator() {
let mut rng = GeneralRng::new(OsRng);
let el_gamal = IntegerElGamal::setup(&Default::default());
let (pk, sk) = el_gamal.generate_keys(&mut rng);
let ciphertext = pk.encrypt(&UnsignedInteger::from(19u64), &mut rng);
assert_eq!(UnsignedInteger::from(19u64), sk.decrypt(&ciphertext));
}
#[test]
fn test_encrypt_decrypt_identity() {
let mut rng = GeneralRng::new(OsRng);
let el_gamal = IntegerElGamal::setup(&Default::default());
let (pk, sk) = el_gamal.generate_keys(&mut rng);
let ciphertext = pk.encrypt(&UnsignedInteger::from(1), &mut rng);
assert!(sk.decrypt_identity(&ciphertext));
}
#[test]
fn test_homomorphic_mul() {
let mut rng = GeneralRng::new(OsRng);
let el_gamal = IntegerElGamal::setup(&Default::default());
let (pk, sk) = el_gamal.generate_keys(&mut rng);
let ciphertext_a = pk.encrypt(&UnsignedInteger::from(7u64), &mut rng);
let ciphertext_b = pk.encrypt(&UnsignedInteger::from(7u64), &mut rng);
let ciphertext_twice = &ciphertext_a * &ciphertext_b;
assert_eq!(UnsignedInteger::from(49u64), sk.decrypt(&ciphertext_twice));
}
#[test]
fn test_homomorphic_scalar_pow() {
let mut rng = GeneralRng::new(OsRng);
let el_gamal = IntegerElGamal::setup(&Default::default());
let (pk, sk) = el_gamal.generate_keys(&mut rng);
let ciphertext = pk.encrypt(&UnsignedInteger::from(9u64), &mut rng);
let ciphertext_twice = ciphertext.pow(&UnsignedInteger::from(4u64));
assert_eq!(
UnsignedInteger::from(6561u64),
sk.decrypt(&ciphertext_twice)
);
}
#[test]
fn randomize() {
let mut rng = GeneralRng::new(OsRng);
let el_gamal = IntegerElGamal::setup(&Default::default());
let (pk, sk) = el_gamal.generate_keys(&mut rng);
let ciphertext = pk.encrypt_raw(&UnsignedInteger::from(15u64), &mut rng);
let ciphertext_randomized = pk.randomize(ciphertext.clone(), &mut rng);
assert_ne!(ciphertext, ciphertext_randomized);
assert_eq!(
UnsignedInteger::from(15u64),
sk.decrypt(&ciphertext_randomized.associate(&pk))
);
}
}