Skip to content

Commit

Permalink
ElGamal für prime Restklassengruppen implementiert.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tristan-H11 committed Feb 29, 2024
1 parent 9ecbe46 commit 4b9f532
Showing 1 changed file with 189 additions and 2 deletions.
191 changes: 189 additions & 2 deletions src/encryption/el_gamal/el_gamal_scheme.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use crate::encryption::asymmetric_encryption_types::{
AsymmetricEncryptionScheme, KeyGenWithPrimeConfig, KeyGenerator,
AsymmetricDecryptor, AsymmetricEncryptionScheme, AsymmetricEncryptor, KeyGenWithPrimeConfig,
KeyGenerator,
};
use crate::encryption::el_gamal::keys::{ElGamalKeyPair, ElGamalPrivateKey, ElGamalPublicKey};
use crate::encryption::encryption_types::EncryptionScheme;
use crate::encryption::encryption_types::{Decryptor, EncryptionScheme, Encryptor};
use crate::math_core::number_theory::number_theory_service::{
NumberTheoryService, NumberTheoryServiceTrait,
};
use crate::math_core::pseudo_random_number_generator::PseudoRandomNumberGenerator;
use crate::math_core::traits::increment::Increment;
use atomic_counter::RelaxedCounter;
use bigdecimal::num_bigint::BigInt;
use log::debug;
use std::time::SystemTime;

pub struct ElGamalScheme;

Expand Down Expand Up @@ -92,3 +95,187 @@ impl KeyGenerator<ElGamalPublicKey, ElGamalPrivateKey, ElGamalScheme> for ElGama
}
}
}

impl Encryptor<'_, ElGamalScheme> for ElGamalScheme {
type Input = BigInt;
type Output = (BigInt, BigInt);
type Key = ElGamalPublicKey;
}

impl AsymmetricEncryptor<'_, ElGamalScheme> for ElGamalScheme {
/// Verschlüsselt eine Nachricht mit dem öffentlichen Schlüssel des ElGamal-Kryptosystems in primen Restklassengruppen.
///
/// # Argumente
/// * `key` - Der öffentliche Schlüssel.
/// * `plaintext` - Die zu verschlüsselnde Nachricht.
/// * `service` - Der Service für die Zahlentheorie.
///
/// # Rückgabe
/// Ein Tupel aus dem verschlüsselten Nachrichtenteil `a` und dem zweiten verschlüsselten Nachrichtenteil `b`.
fn encrypt(
key: &Self::Key,
plaintext: &Self::Input,
service: NumberTheoryService,
) -> Self::Output {
let p = &key.p;
let g = &key.g;
let y = &key.y;

// TODO Der Seed für die Generierung der Zufallszahl für das Verschlüsseln der Nachricht
// wird vorerst aus der aktuellen Systemzeit generiert und auf 2^16 begrenzt.
let random_seed = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs() as u16;
let random_generator = PseudoRandomNumberGenerator::new(random_seed as u32, service);
let counter = RelaxedCounter::new(1);

// Generieren des Zufallszahl k (Zufallszahl zwischen 1 und p-2)
let p_minus_two = p.decrement().decrement();
let k = random_generator.take(&1.into(), &p_minus_two, &counter);

// Berechnen des ersten verschlüsselten Nachrichtenteils c1
let a = service.fast_exponentiation(g, &k, p);

// Berechnen des zweiten verschlüsselten Nachrichtenteils c2
let b = (service.fast_exponentiation(y, &k, p) * plaintext) % p;

(a, b)
}
}

impl Decryptor<'_, ElGamalScheme> for ElGamalScheme {
type Input = (BigInt, BigInt);
type Output = BigInt;
type Key = ElGamalPrivateKey;
}

impl AsymmetricDecryptor<'_, ElGamalScheme> for ElGamalScheme {
/// Entschlüsselt eine Nachricht mit dem privaten Schlüssel des ElGamal-Kryptosystems in primen Restklassengruppen.
///
/// # Argumente
/// * `key` - Der private Schlüssel.
/// * `ciphertext` - Das zu entschlüsselnde Nachrichtentupel.
/// * `service` - Der Service für die Zahlentheorie.
///
/// # Rückgabe
/// Die entschlüsselte Nachricht.
fn decrypt(
key: &Self::Key,
ciphertext: &Self::Input,
service: NumberTheoryService,
) -> Self::Output {
let p = &key.p;
let x = &key.x;
let (a, b) = ciphertext;

// Berechnen von z = a^x mod p = a^(p-1-x) mod p
let z = service.fast_exponentiation(a, &(p.decrement() - x), p);

// Berechnen des Klartextes m = b * z mod p
let plaintext = (b * z) % p;

plaintext
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::math_core::number_theory::number_theory_service::NumberTheoryServiceSpeed::Fast;
use bigdecimal::FromPrimitive;

#[test]
fn test_el_gamal_key_generation_happy_flow() {
let service = NumberTheoryService::new(Fast);
let config = ElGamalKeyGenConfig {
modulus_width: 32,
miller_rabin_iterations: 100,
// Pseudozufälliger Wert, weil dieser Test mit jedem Input erfolgreich sein muss
random_seed: SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs() as u32,
number_theory_service: service,
};

let keypair = ElGamalScheme::generate_keypair(&config);
let calculated_y = service.fast_exponentiation(
&keypair.public_key.g,
&keypair.private_key.x,
&keypair.public_key.p,
);

// Das berechnete y muss mit dem y-Wert des öffentlichen Schlüssels übereinstimmen.
// Besser lässt sich der Schlüssel nicht verifizieren, da die Generierung von p und g
// zufällig ist und somit nicht vorhersehbar ist.
assert_eq!(keypair.public_key.y, calculated_y);
}

#[test]
fn test_el_gamal_encryption_decryption_happy_flow() {
let service = NumberTheoryService::new(Fast);
let config = ElGamalKeyGenConfig {
modulus_width: 32,
miller_rabin_iterations: 100,
random_seed: 42,
number_theory_service: service,
};

let keypair = ElGamalScheme::generate_keypair(&config);
let public_key = keypair.public_key;
let private_key = keypair.private_key;

let plaintext = BigInt::from_i32(42).unwrap();
let ciphertext = ElGamalScheme::encrypt(&public_key, &plaintext, service);
let decrypted_plaintext = ElGamalScheme::decrypt(&private_key, &ciphertext, service);

assert_eq!(plaintext, decrypted_plaintext);
}

#[test]
fn test_el_gamal_encryption_decryption_big_keys() {
let service = NumberTheoryService::new(Fast);
let config = ElGamalKeyGenConfig {
modulus_width: 512,
miller_rabin_iterations: 20,
random_seed: 94,
number_theory_service: service,
};

let keypair = ElGamalScheme::generate_keypair(&config);
let public_key = keypair.public_key;
let private_key = keypair.private_key;

let plaintext = BigInt::from_i32(156776).unwrap();
let ciphertext = ElGamalScheme::encrypt(&public_key, &plaintext, service);
println!("ciphertext: {:?}", ciphertext);
let decrypted_plaintext = ElGamalScheme::decrypt(&private_key, &ciphertext, service);

assert_eq!(plaintext, decrypted_plaintext);
}

#[test]
fn test_trivial_with_zero_input() {
let service = NumberTheoryService::new(Fast);
let config = ElGamalKeyGenConfig {
modulus_width: 32,
miller_rabin_iterations: 100,
random_seed: 77,
number_theory_service: service,
};

let keypair = ElGamalScheme::generate_keypair(&config);
let public_key = keypair.public_key;
let private_key = keypair.private_key;

let plaintext = BigInt::from_i32(0).unwrap();
let ciphertext = ElGamalScheme::encrypt(&public_key, &plaintext, service);
// Der zweite Teil muss offensichtlich 0 sein, weil er ein Produkt mit 0 (plaintext) ist.
assert_eq!(ciphertext.1, 0.into());
println!("ciphertext: {:?}", ciphertext);
let decrypted_plaintext = ElGamalScheme::decrypt(&private_key, &ciphertext, service);

assert_eq!(plaintext, decrypted_plaintext);
}
}

0 comments on commit 4b9f532

Please sign in to comment.