252 lines
6.0 KiB
Python
252 lines
6.0 KiB
Python
import time
|
|
|
|
import pytest
|
|
|
|
from lightphe import LightPHE
|
|
from lightphe.commons.logger import Logger
|
|
|
|
logger = Logger(module="tests/test_bonehgodnissim.py")
|
|
|
|
cs = LightPHE(
|
|
algorithm_name="Boneh-Goh-Nissim",
|
|
key_size=50,
|
|
max_tries=10000,
|
|
)
|
|
security_level = cs.cs.ec.n.bit_length()
|
|
|
|
|
|
def test_api():
|
|
tic = time.time()
|
|
|
|
m1 = 83
|
|
m2 = 31
|
|
|
|
c1 = cs.encrypt(plaintext=m1)
|
|
c2 = cs.encrypt(plaintext=m2)
|
|
|
|
# proof of decryption
|
|
assert cs.decrypt(c1) == m1
|
|
assert cs.decrypt(c2) == m2
|
|
|
|
# homomorphic addition
|
|
assert cs.decrypt(c1 + c2) == (m1 + m2) % cs.cs.plaintext_modulo
|
|
|
|
# homomorphic multiplication (once)
|
|
c1_times_c2 = c1 * c2
|
|
assert cs.decrypt(c1_times_c2) == (m1 * m2) % cs.cs.plaintext_modulo
|
|
|
|
# scalar multiplication
|
|
k = 2
|
|
assert cs.decrypt(c1 * k) == (m1 * k) % cs.cs.plaintext_modulo
|
|
|
|
# unsupported homomorphic operations
|
|
with pytest.raises(
|
|
ValueError,
|
|
match="Boneh-Goh-Nissim only supports multiplication ciphertexts once!",
|
|
):
|
|
_ = c1_times_c2 * c2
|
|
|
|
with pytest.raises(ValueError):
|
|
_ = c1 & c2
|
|
|
|
with pytest.raises(ValueError):
|
|
_ = c1 ^ c2
|
|
|
|
# re-randomization
|
|
c1_prime = cs.cs.reencrypt(c1.value)
|
|
c2_prime = cs.cs.reencrypt(c2.value)
|
|
# assert c1.value != c1_prime
|
|
# assert c2.value != c2_prime
|
|
assert cs.cs.decrypt(c1_prime) == m1
|
|
assert cs.cs.decrypt(c2_prime) == m2
|
|
|
|
logger.info(
|
|
f"✅ Boneh-God-Nissim test succeeded ({security_level} bit ECC security level)"
|
|
f" in {time.time() - tic:.2f} seconds"
|
|
)
|
|
|
|
|
|
def test_linear_regression():
|
|
tic = time.time()
|
|
|
|
x1 = 5
|
|
w1 = 7
|
|
|
|
x2 = 3
|
|
w2 = 4
|
|
|
|
x1_enc = cs.encrypt(plaintext=x1)
|
|
w1_enc = cs.encrypt(plaintext=w1)
|
|
|
|
x2_enc = cs.encrypt(plaintext=x2)
|
|
w2_enc = cs.encrypt(plaintext=w2)
|
|
|
|
x1w1_enc = x1_enc * w1_enc
|
|
x2w2_enc = x2_enc * w2_enc
|
|
|
|
sum_enc = x1w1_enc + x2w2_enc
|
|
|
|
sum_decrypted = cs.decrypt(sum_enc)
|
|
|
|
assert sum_decrypted == (x1 * w1 + x2 * w2)
|
|
|
|
# multiplication by constant
|
|
_5x1w1_enc = cs.decrypt(x1w1_enc * 5)
|
|
assert _5x1w1_enc == (5 * x1 * w1)
|
|
|
|
# unsupported homomorphic operations
|
|
with pytest.raises(
|
|
ValueError,
|
|
match="Boneh-Goh-Nissim only supports multiplication ciphertexts once!",
|
|
):
|
|
_ = x1w1_enc * w2_enc
|
|
|
|
logger.info(
|
|
f"✅ Boneh-God-Nissim linear regression test succeeded "
|
|
f"({security_level} bit ECC security level)"
|
|
f" in {time.time() - tic:.2f} seconds"
|
|
)
|
|
|
|
|
|
def test_cosine_similarity():
|
|
tic = time.time()
|
|
i1 = 5
|
|
i2 = 2
|
|
i3 = 8
|
|
|
|
# v2 = [1, 1, 2]
|
|
j1 = 1
|
|
j2 = 1
|
|
j3 = 2
|
|
|
|
i1_enc = cs.encrypt(i1)
|
|
i2_enc = cs.encrypt(i2)
|
|
i3_enc = cs.encrypt(i3)
|
|
|
|
j1_enc = cs.encrypt(j1)
|
|
j2_enc = cs.encrypt(j2)
|
|
j3_enc = cs.encrypt(j3)
|
|
|
|
# homomorphic operation
|
|
cosine_sim_enc = i1_enc * j1_enc + i2_enc * j2_enc + i3_enc * j3_enc
|
|
|
|
# proof of work
|
|
assert cs.decrypt(cosine_sim_enc) == i1 * j1 + i2 * j2 + i3 * j3
|
|
|
|
logger.info(
|
|
f"✅ Boneh-God-Nissim cosine similarity test succeeded "
|
|
f"({security_level} bit ECC security level)"
|
|
f" in {time.time() - tic:.2f} seconds"
|
|
)
|
|
|
|
|
|
def test_euclidean_distance():
|
|
tic = time.time()
|
|
|
|
# v1 = [5, 2, 3]
|
|
i1 = 5
|
|
i2 = 2
|
|
i3 = 8
|
|
|
|
# v2 = [1, 1, 2]
|
|
j1 = 1
|
|
j2 = 1
|
|
j3 = 2
|
|
|
|
i1_enc = cs.encrypt(i1)
|
|
i2_enc = cs.encrypt(i2)
|
|
i3_enc = cs.encrypt(i3)
|
|
|
|
j1_enc = cs.encrypt(-j1)
|
|
j2_enc = cs.encrypt(-j2)
|
|
j3_enc = cs.encrypt(-j3)
|
|
|
|
euclidean_sqrt_enc = (
|
|
(i1_enc + j1_enc) * (i1_enc + j1_enc)
|
|
+ (i2_enc + j2_enc) * (i2_enc + j2_enc)
|
|
+ (i3_enc + j3_enc) * (i3_enc + j3_enc)
|
|
)
|
|
|
|
assert (
|
|
cs.decrypt(euclidean_sqrt_enc)
|
|
== (i1 - j1) ** 2 + (i2 - j2) ** 2 + (i3 - j3) ** 2
|
|
)
|
|
|
|
logger.info(
|
|
f"✅ Boneh-God-Nissim euclidean distance test succeeded "
|
|
f"({security_level} bit ECC security level)"
|
|
f" in {time.time() - tic:.2f} seconds"
|
|
)
|
|
|
|
|
|
def test_api_with_predefined_keys():
|
|
keys = {
|
|
"private_key": {"q1": 1260048562698661, "q2": 1495813357973021},
|
|
"public_key": {
|
|
"curve": {
|
|
"a": 1,
|
|
"b": 0,
|
|
"p": 1477681217875020437035023206706703,
|
|
"G": (
|
|
404050258967758769169929337984186,
|
|
548422986186523785500578682099939,
|
|
),
|
|
"n": 1884797471779362802340590824881,
|
|
},
|
|
"G": (404050258967758769169929337984186, 548422986186523785500578682099939),
|
|
"u": (
|
|
496699107331579016081758964967239,
|
|
1209427529257066171815901459972102,
|
|
),
|
|
"h": (557434130026147587760748115479880, 109859910481881270030204038710204),
|
|
"l": 784,
|
|
},
|
|
}
|
|
|
|
bgn_cs = LightPHE(
|
|
algorithm_name="Boneh-Goh-Nissim",
|
|
keys=keys,
|
|
)
|
|
security_level = bgn_cs.cs.ec.n.bit_length()
|
|
|
|
m1 = 3
|
|
m2 = 2
|
|
|
|
c1 = bgn_cs.encrypt(plaintext=m1)
|
|
c2 = bgn_cs.encrypt(plaintext=m2)
|
|
|
|
# proof of decryption
|
|
assert bgn_cs.decrypt(c1) == m1
|
|
assert bgn_cs.decrypt(c2) == m2
|
|
|
|
# homomorphic addition
|
|
assert bgn_cs.decrypt(c1 + c2) == (m1 + m2) % bgn_cs.cs.plaintext_modulo
|
|
|
|
# homomorphic multiplication
|
|
c1_times_c2 = c1 * c2
|
|
assert bgn_cs.decrypt(c1_times_c2) == (m1 * m2) % bgn_cs.cs.plaintext_modulo
|
|
|
|
# scalar multiplication
|
|
k = 2
|
|
assert bgn_cs.decrypt(c1 * k) == (m1 * k) % bgn_cs.cs.plaintext_modulo
|
|
|
|
# unsupported homomorphic operations
|
|
|
|
with pytest.raises(ValueError):
|
|
_ = c1 & c2
|
|
|
|
with pytest.raises(ValueError):
|
|
_ = c1 ^ c2
|
|
|
|
# re-randomization
|
|
c1_prime = bgn_cs.cs.reencrypt(c1.value)
|
|
c2_prime = bgn_cs.cs.reencrypt(c2.value)
|
|
# assert c1.value != c1_prime
|
|
# assert c2.value != c2_prime
|
|
assert bgn_cs.cs.decrypt(c1_prime) == m1
|
|
assert bgn_cs.cs.decrypt(c2_prime) == m2
|
|
|
|
logger.info(
|
|
f"✅ Boneh-God-Nissim test succeeded ({security_level} bit ECC security level)"
|
|
)
|