import unittest
from libpep.arithmetic.group_elements import GroupElement
from libpep.arithmetic.scalars import ScalarNonZero
from libpep.core.elgamal import encrypt, decrypt, ElGamal
from libpep.core.primitives import (
rekey,
rekey2,
rerandomize,
reshuffle,
reshuffle2,
rsk,
rsk2,
rrsk,
rrsk2,
)
class TestElGamal(unittest.TestCase):
def test_encryption_decryption(self):
G = GroupElement.generator()
y = ScalarNonZero.random()
Y = G.mul(y)
m = GroupElement.random()
encrypted = encrypt(m, Y)
decrypted = decrypt(encrypted, y)
self.assertEqual(m.to_hex(), decrypted.to_hex())
def test_multiple_encryptions(self):
G = GroupElement.generator()
y = ScalarNonZero.random()
Y = G.mul(y)
m = GroupElement.random()
enc1 = encrypt(m, Y)
enc2 = encrypt(m, Y)
self.assertNotEqual(enc1.to_base64(), enc2.to_base64())
dec1 = decrypt(enc1, y)
dec2 = decrypt(enc2, y)
self.assertEqual(m.to_hex(), dec1.to_hex())
self.assertEqual(m.to_hex(), dec2.to_hex())
self.assertEqual(dec1.to_hex(), dec2.to_hex())
def test_elgamal_encoding(self):
G = GroupElement.generator()
y = ScalarNonZero.random()
Y = G.mul(y)
m = GroupElement.random()
encrypted = encrypt(m, Y)
encoded_bytes = encrypted.to_bytes()
decoded = ElGamal.from_bytes(encoded_bytes)
self.assertIsNotNone(decoded)
decrypted_original = decrypt(encrypted, y)
decrypted_decoded = decrypt(decoded, y)
self.assertEqual(decrypted_original.to_hex(), decrypted_decoded.to_hex())
base64_str = encrypted.to_base64()
decoded_b64 = ElGamal.from_base64(base64_str)
self.assertIsNotNone(decoded_b64)
decrypted_b64 = decrypt(decoded_b64, y)
self.assertEqual(decrypted_original.to_hex(), decrypted_b64.to_hex())
def test_elgamal_representation(self):
G = GroupElement.generator()
y = ScalarNonZero.random()
Y = G.mul(y)
m = GroupElement.random()
encrypted = encrypt(m, Y)
str_repr = str(encrypted)
repr_repr = repr(encrypted)
self.assertIsInstance(str_repr, str)
self.assertIsInstance(repr_repr, str)
self.assertIn("ElGamal", repr_repr)
self.assertEqual(str_repr, encrypted.to_base64())
def test_deterministic_values(self):
y_hex = "044214715d782745a36ededee498b31d882f5e6239db9f9443f6bfef04944906"
y = ScalarNonZero.from_hex(y_hex)
self.assertIsNotNone(y)
generator = GroupElement.generator()
Y = generator.mul(y)
encrypted = encrypt(generator, Y)
decrypted = decrypt(encrypted, y)
self.assertEqual(generator.to_hex(), decrypted.to_hex())
class TestPrimitives(unittest.TestCase):
def setUp(self):
self.G = GroupElement.generator()
self.y = ScalarNonZero.random()
self.Y = self.G.mul(self.y)
self.m = GroupElement.random()
self.encrypted = encrypt(self.m, self.Y)
def test_rerandomize(self):
r = ScalarNonZero.random()
try:
rerandomized = rerandomize(self.encrypted, self.Y, r)
except TypeError:
rerandomized = rerandomize(self.encrypted, r)
dec_original = decrypt(self.encrypted, self.y)
dec_rerandomized = decrypt(rerandomized, self.y)
self.assertEqual(dec_original.to_hex(), dec_rerandomized.to_hex())
self.assertEqual(self.m.to_hex(), dec_rerandomized.to_hex())
self.assertNotEqual(self.encrypted.to_base64(), rerandomized.to_base64())
def test_rekey(self):
k = ScalarNonZero.random()
rekeyed = rekey(self.encrypted, k)
new_secret = self.y.mul(k)
decrypted = decrypt(rekeyed, new_secret)
self.assertEqual(self.m.to_hex(), decrypted.to_hex())
def test_reshuffle(self):
s = ScalarNonZero.random()
reshuffled = reshuffle(self.encrypted, s)
decrypted = decrypt(reshuffled, self.y)
expected = self.m.mul(s)
self.assertEqual(expected.to_hex(), decrypted.to_hex())
def test_rsk_combined(self):
s = ScalarNonZero.random()
k = ScalarNonZero.random()
rsk_result = rsk(self.encrypted, s, k)
reshuffled = reshuffle(self.encrypted, s)
rsk_separate = rekey(reshuffled, k)
new_secret = self.y.mul(k)
dec_combined = decrypt(rsk_result, new_secret)
dec_separate = decrypt(rsk_separate, new_secret)
self.assertEqual(dec_combined.to_hex(), dec_separate.to_hex())
expected = self.m.mul(s)
self.assertEqual(expected.to_hex(), dec_combined.to_hex())
def test_rekey2_transitivity(self):
k_from = ScalarNonZero.random()
k_to = ScalarNonZero.random()
rekeyed_from = rekey(self.encrypted, k_from)
rekeyed_to = rekey2(rekeyed_from, k_from, k_to)
direct_rekey = rekey(self.encrypted, k_to)
new_secret = self.y.mul(k_to)
dec_transitive = decrypt(rekeyed_to, new_secret)
dec_direct = decrypt(direct_rekey, new_secret)
self.assertEqual(dec_transitive.to_hex(), dec_direct.to_hex())
self.assertEqual(self.m.to_hex(), dec_transitive.to_hex())
def test_reshuffle2_transitivity(self):
n_from = ScalarNonZero.random()
n_to = ScalarNonZero.random()
reshuffled_from = reshuffle(self.encrypted, n_from)
reshuffled_to = reshuffle2(reshuffled_from, n_from, n_to)
direct_reshuffle = reshuffle(self.encrypted, n_to)
dec_transitive = decrypt(reshuffled_to, self.y)
dec_direct = decrypt(direct_reshuffle, self.y)
self.assertEqual(dec_transitive.to_hex(), dec_direct.to_hex())
expected = self.m.mul(n_to)
self.assertEqual(expected.to_hex(), dec_transitive.to_hex())
def test_rsk2_transitivity(self):
s_from = ScalarNonZero.random()
s_to = ScalarNonZero.random()
k_from = ScalarNonZero.random()
k_to = ScalarNonZero.random()
rsk_from = rsk(self.encrypted, s_from, k_from)
rsk_to = rsk2(rsk_from, s_from, s_to, k_from, k_to)
direct_rsk = rsk(self.encrypted, s_to, k_to)
new_secret = self.y.mul(k_to)
dec_transitive = decrypt(rsk_to, new_secret)
dec_direct = decrypt(direct_rsk, new_secret)
self.assertEqual(dec_transitive.to_hex(), dec_direct.to_hex())
expected = self.m.mul(s_to)
self.assertEqual(expected.to_hex(), dec_transitive.to_hex())
def test_identity_operations(self):
one = ScalarNonZero.one()
rekeyed_one = rekey(self.encrypted, one)
dec_rekeyed = decrypt(rekeyed_one, self.y)
self.assertEqual(self.m.to_hex(), dec_rekeyed.to_hex())
reshuffled_one = reshuffle(self.encrypted, one)
dec_reshuffled = decrypt(reshuffled_one, self.y)
self.assertEqual(self.m.to_hex(), dec_reshuffled.to_hex())
rsk_ones = rsk(self.encrypted, one, one)
dec_rsk = decrypt(rsk_ones, self.y)
self.assertEqual(self.m.to_hex(), dec_rsk.to_hex())
if __name__ == "__main__":
unittest.main()