//! El-Gamal encryption on a prime-order cyclic group. dbg(GEN, ORDER); PublicKey = #{ encrypt: |self, message| { r = rand_scalar(); shared_secret = self ^ r; #{ R: GEN ^ r, B: message * shared_secret } }, }; SecretKey = #{ decrypt: |self, { R, B }| { shared_secret = R ^ self; B / shared_secret }, public_key: |self| GEN ^ self, }; gen = || { sk = rand_scalar(); #{ sk, pk: {SecretKey.public_key}(sk) } }; // Test! { sk, pk } = gen(); while(5, |i| i != 0, |i| { message = GEN ^ rand_scalar(); dbg(message); encrypted = pk.{PublicKey.encrypt}(message); dbg(encrypted); assert_eq(sk.{SecretKey.decrypt}(encrypted), message); i - 1 }); // Advanced testing making use of partial homomorphicity of encryption. ONE = GEN ^ 0; encrypt_and_combine = |pk, messages| { messages.map(|msg| pk.{PublicKey.encrypt}(msg)).fold( #{ R: ONE, B: ONE }, |enc_x, enc_y| enc_x * enc_y, ) }; messages = (1, 2, 3, 4, 5).map(|_| GEN ^ rand_scalar()); assert_eq( sk.{SecretKey.decrypt}(encrypt_and_combine(pk, messages)), messages.fold(ONE, |acc, msg| acc * msg) );