2019-09-18 14:09:36 +00:00
|
|
|
|
# Concept: Create 8 PKs where each represent a bloodtype. Let 7 of them be created by OGen and 1 of them by KeyGen.
|
|
|
|
|
# The one represents our bloodtype. Bob will then encrypt 8 values using these PKs, where each value repredents
|
|
|
|
|
# A truth value, thus either true or false, s.t. each cipher is an entry in the bloodtype comptability matrix.
|
2019-09-21 23:41:39 +00:00
|
|
|
|
import math
|
|
|
|
|
import random
|
2019-09-18 15:28:45 +00:00
|
|
|
|
from secrets import SystemRandom
|
2019-09-21 23:41:39 +00:00
|
|
|
|
|
|
|
|
|
from .week1 import BloodType, blood_cell_compatibility_lookup
|
|
|
|
|
|
|
|
|
|
bloodtypes = {b: i for i, b in enumerate(BloodType, start=1)} # we can't encrypt 0, so we have to index from 1
|
2019-09-18 14:09:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ElGamal:
|
2019-09-18 15:39:54 +00:00
|
|
|
|
def __init__(self, g, q, p):
|
2019-09-18 14:09:36 +00:00
|
|
|
|
self.gen_ = g
|
|
|
|
|
self.order = q
|
2019-09-18 15:39:54 +00:00
|
|
|
|
self.p = p
|
2019-09-18 14:09:36 +00:00
|
|
|
|
self.pk = None
|
|
|
|
|
self.sk = None
|
|
|
|
|
|
2019-09-18 15:28:45 +00:00
|
|
|
|
def gen_key(self):
|
|
|
|
|
key = SystemRandom().randint(1, self.order)
|
2019-09-21 23:41:39 +00:00
|
|
|
|
while math.gcd(self.order, key) != 1:
|
2019-09-18 15:28:45 +00:00
|
|
|
|
key = SystemRandom().randint(1, self.order)
|
|
|
|
|
return key
|
|
|
|
|
|
2019-09-18 14:09:36 +00:00
|
|
|
|
def gen(self, sk):
|
2019-09-20 10:16:58 +00:00
|
|
|
|
h = pow(self.gen_, sk, self.order)
|
2019-09-18 14:09:36 +00:00
|
|
|
|
self.sk = sk
|
|
|
|
|
self.pk = (self.gen_, h)
|
|
|
|
|
return self.pk
|
|
|
|
|
|
|
|
|
|
def enc(self, m, pk):
|
|
|
|
|
# sample random r \in Zq
|
2019-09-18 16:14:19 +00:00
|
|
|
|
r = SystemRandom().randint(1, self.order)
|
2019-09-18 14:09:36 +00:00
|
|
|
|
g, h = pk
|
2019-09-18 15:39:54 +00:00
|
|
|
|
|
2019-09-20 10:16:58 +00:00
|
|
|
|
s = pow(h, r, self.order)
|
|
|
|
|
p = pow(g, r, self.order)
|
2019-09-18 15:28:45 +00:00
|
|
|
|
c = s * m
|
|
|
|
|
return c, p
|
2019-09-18 14:09:36 +00:00
|
|
|
|
|
|
|
|
|
def dec(self, c):
|
|
|
|
|
c1, c2 = c
|
2019-09-20 10:16:58 +00:00
|
|
|
|
h = pow(c2, self.sk, self.order)
|
2019-09-18 15:28:45 +00:00
|
|
|
|
m = c1 / h
|
2019-09-18 14:09:36 +00:00
|
|
|
|
return m
|
|
|
|
|
|
2019-09-18 15:39:54 +00:00
|
|
|
|
def ogen(self):
|
2019-09-18 15:28:45 +00:00
|
|
|
|
s = SystemRandom().randint(1, self.order)
|
2019-09-20 10:16:58 +00:00
|
|
|
|
h = pow(s, 2, self.order)
|
2019-09-18 14:09:36 +00:00
|
|
|
|
return self.gen_, h
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Alice:
|
|
|
|
|
def __init__(self, bloodtype, elgamal):
|
|
|
|
|
self.elgamal = elgamal
|
|
|
|
|
|
2019-09-18 15:39:54 +00:00
|
|
|
|
self.sk = elgamal.gen_key()
|
|
|
|
|
self.pk = elgamal.gen(self.sk)
|
2019-09-21 23:41:39 +00:00
|
|
|
|
self.b = bloodtypes[bloodtype]
|
|
|
|
|
self.fake_pks = [self.elgamal.ogen() for _ in range(7)]
|
2019-09-18 14:09:36 +00:00
|
|
|
|
|
|
|
|
|
def send_pks(self):
|
|
|
|
|
all_pks = self.fake_pks
|
2019-09-18 15:39:54 +00:00
|
|
|
|
all_pks.insert(self.b-1, self.pk)
|
2019-09-18 14:09:36 +00:00
|
|
|
|
return all_pks
|
|
|
|
|
|
|
|
|
|
def retrieve(self, ciphers):
|
2019-09-21 23:41:39 +00:00
|
|
|
|
#print(ciphers)
|
2019-09-18 15:39:54 +00:00
|
|
|
|
mb = self.elgamal.dec(ciphers[self.b-1])
|
2019-09-21 23:41:39 +00:00
|
|
|
|
# Bob sends 1 for false, 2 for true, so we have to subtract 1 here
|
|
|
|
|
return mb - 1
|
2019-09-18 14:09:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Bob:
|
|
|
|
|
def __init__(self, bloodtype, elgamal):
|
2019-09-21 23:41:39 +00:00
|
|
|
|
self.bloodtype = bloodtypes[bloodtype]
|
2019-09-18 14:09:36 +00:00
|
|
|
|
self.truth_vals = []
|
|
|
|
|
self.elgamal = elgamal
|
|
|
|
|
self.pks = None
|
2019-09-19 10:39:38 +00:00
|
|
|
|
# Bob needs his row of the truth table, to create the 8 messages
|
2019-09-18 14:09:36 +00:00
|
|
|
|
for donor in BloodType:
|
2019-09-18 16:14:19 +00:00
|
|
|
|
truth_val = blood_cell_compatibility_lookup(bloodtype, donor)
|
2019-09-18 14:09:36 +00:00
|
|
|
|
self.truth_vals.append(truth_val)
|
|
|
|
|
|
|
|
|
|
def receive_pks(self, pks):
|
|
|
|
|
self.pks = pks
|
|
|
|
|
|
|
|
|
|
def transfer_messages(self):
|
|
|
|
|
ciphers = []
|
|
|
|
|
for idx, truth_val in enumerate(self.truth_vals):
|
|
|
|
|
pk = self.pks[idx]
|
2019-09-21 23:41:39 +00:00
|
|
|
|
c = self.elgamal.enc(truth_val + 1, pk) # + 1 since Bob can't send 0, as it will encrypt to 0
|
2019-09-18 14:09:36 +00:00
|
|
|
|
ciphers.append(c)
|
|
|
|
|
return ciphers
|
|
|
|
|
|
|
|
|
|
|
2019-09-21 23:41:39 +00:00
|
|
|
|
def is_prime(n: int, k: int) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
Miller-Rabin Primality test.
|
|
|
|
|
Adapted from pseudo-code at https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test.
|
|
|
|
|
|
|
|
|
|
:param n: An odd integer to be tested for primality.
|
|
|
|
|
:param k: The number of rounds of testing to perform.
|
|
|
|
|
:return: True if n is 'probably prime', False otherwise if n is composite.
|
|
|
|
|
"""
|
|
|
|
|
# write n as 2r·d + 1 with d odd (by factoring out powers of 2 from n − 1)
|
|
|
|
|
d = n - 1
|
|
|
|
|
r = 0
|
|
|
|
|
while d % 2 == 0:
|
|
|
|
|
d >>= 1
|
|
|
|
|
r += 1
|
|
|
|
|
|
|
|
|
|
for i in range(k): # witnessLoop
|
|
|
|
|
continue_wl = False
|
|
|
|
|
a = random.randint(2, n - 2)
|
|
|
|
|
x = pow(a, d, n)
|
|
|
|
|
if x == 1 or x == n - 1:
|
|
|
|
|
continue
|
|
|
|
|
for j in range(r - 1):
|
|
|
|
|
x = pow(x, 2, n)
|
|
|
|
|
if x == n - 1:
|
|
|
|
|
continue_wl = True
|
|
|
|
|
break
|
|
|
|
|
if continue_wl:
|
|
|
|
|
continue
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
2019-10-02 18:07:34 +00:00
|
|
|
|
def gen_prime(b: int, k: int = 4) -> int:
|
2019-09-21 23:41:39 +00:00
|
|
|
|
"""
|
|
|
|
|
Generate strong probable prime by drawing integers at random until one passes the is_prime test.
|
|
|
|
|
Adapted from pseudo-code at https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test.
|
|
|
|
|
|
|
|
|
|
:param b: The number of bits of the result.
|
|
|
|
|
:param k: The number of rounds of testing to perform.
|
|
|
|
|
:return: a strong probable prime.
|
|
|
|
|
"""
|
|
|
|
|
while True:
|
|
|
|
|
n = random.randint(2**(b-1), (2**b)-1)
|
|
|
|
|
if n % 2 == 0:
|
|
|
|
|
continue
|
2019-10-02 18:07:34 +00:00
|
|
|
|
# Check that the future value of q is prime
|
|
|
|
|
if is_prime(n, k) and is_prime(2*n+1, k):
|
2019-09-21 23:41:39 +00:00
|
|
|
|
return n
|
|
|
|
|
|
|
|
|
|
|
2019-10-02 18:07:34 +00:00
|
|
|
|
def find_primitive_root(p):
|
|
|
|
|
if p == 2:
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
p1 = 2
|
|
|
|
|
p2 = (p - 1) // p1
|
|
|
|
|
|
|
|
|
|
# test random g's until one is found that is a primitive root mod p
|
|
|
|
|
while True:
|
|
|
|
|
g = SystemRandom().randint(2, p - 1)
|
|
|
|
|
|
|
|
|
|
if not (pow(g, (p - 1) // p1, p) == 1):
|
|
|
|
|
if not pow(g, (p - 1) // p2, p) == 1:
|
|
|
|
|
return g
|
|
|
|
|
|
|
|
|
|
|
2019-09-21 23:41:39 +00:00
|
|
|
|
def run(donor: BloodType, recipient: BloodType):
|
|
|
|
|
p = gen_prime(128)
|
2019-09-18 16:14:19 +00:00
|
|
|
|
q = 2 * p + 1
|
2019-10-02 18:07:34 +00:00
|
|
|
|
g = find_primitive_root(p)
|
2019-09-21 23:41:39 +00:00
|
|
|
|
#print("p:", p, "q:", q, "g:", g)
|
2019-09-18 15:28:45 +00:00
|
|
|
|
|
2019-09-18 15:39:54 +00:00
|
|
|
|
elgamal = ElGamal(g, q, p)
|
2019-09-18 16:14:19 +00:00
|
|
|
|
alice = Alice(donor, elgamal)
|
|
|
|
|
bob = Bob(recipient, elgamal)
|
2019-09-18 15:39:54 +00:00
|
|
|
|
|
|
|
|
|
bob.receive_pks(alice.send_pks())
|
|
|
|
|
pls = alice.retrieve(bob.transfer_messages())
|
2019-09-18 15:28:45 +00:00
|
|
|
|
|
2019-09-18 16:14:19 +00:00
|
|
|
|
return bool(pls)
|
2019-09-18 14:09:36 +00:00
|
|
|
|
|
|
|
|
|
|
2019-09-21 23:41:39 +00:00
|
|
|
|
def main():
|
2019-09-18 16:14:19 +00:00
|
|
|
|
green = 0
|
|
|
|
|
red = 0
|
|
|
|
|
for i, recipient in enumerate(BloodType):
|
|
|
|
|
for j, donor in enumerate(BloodType):
|
|
|
|
|
z = run(donor, recipient)
|
|
|
|
|
lookup = blood_cell_compatibility_lookup(recipient, donor)
|
|
|
|
|
if lookup == z:
|
|
|
|
|
green += 1
|
|
|
|
|
else:
|
|
|
|
|
print(f"'{BloodType(donor).name} -> {BloodType(recipient).name}' should be {lookup}.")
|
|
|
|
|
red += 1
|
|
|
|
|
print("Green:", green)
|
|
|
|
|
print("Red :", red)
|