crypto_computing/week3.py

202 lines
5.5 KiB
Python

from __future__ import annotations
import random
from .week1 import BloodType, blood_cell_compatibility_lookup
class Protocol:
def __init__(self, alice, bob):
self.alice: Alice = alice
self.bob: Bob = bob
self.dealer = Dealer()
self.alice.share_inputs(bob)
self.bob.share_inputs(alice)
def xor(self, x_name, y_name, z_name):
self.alice.xor(x_name, y_name, z_name)
self.bob.xor(x_name, y_name, z_name)
def xor_with_constant(self, x_name, c, z_name):
self.alice.xor_with_constant(x_name, c, z_name)
self.bob.xor_with_constant(x_name, c, z_name)
def and_(self, x_name, y_name, z_name):
self.dealer.gen_randoms()
self.alice.set_randoms(self.dealer.randoms_alice)
self.bob.set_randoms(self.dealer.randoms_bob)
self.xor(x_name, "u", "d")
self.xor(y_name, "v", "e")
self.open("d")
self.open("e")
# z = w ^ (e & x) ^ (d & y) ^ (e & d)
# z = w ^ a ^ b ^ c
# z = z1 ^ z2
self.and_with_public(x_name, "e", "a")
self.and_with_public(y_name, "d", "b")
self.and_publics("e", "d", "c")
self.xor("w", "a", "z1")
self.xor_with_public("b", "c", "z2")
self.xor("z1", "z2", z_name)
def and_with_public(self, x_name, y_name, z_name):
self.alice.and_with_public(x_name, y_name, z_name)
self.bob.and_with_public(x_name, y_name, z_name)
def and_publics(self, x_name, y_name, z_name):
self.alice.and_publics(x_name, y_name, z_name)
self.bob.and_publics(x_name, y_name, z_name)
def xor_with_public(self, x_name, c_name, z_name):
self.alice.xor_with_public(x_name, c_name, z_name)
self.bob.xor_with_public(x_name, c_name, z_name)
def open(self, x_name):
self.alice.open(x_name, self.bob.send(x_name))
self.bob.open(x_name, self.alice.send(x_name))
def output(self, z_name):
return self.alice.open(z_name, self.bob.send(z_name))
class Party:
def __init__(self, **inputs) -> None:
self.inputs = inputs
self.secrets = {}
self.publics = {}
def share(self, x_name):
x_other = random.randint(0, 1)
self.secrets[x_name] = self.inputs[x_name] ^ x_other
return x_other
def share_inputs(self, other_party: Party):
for name, value in self.inputs.items():
other_party.receive(name, self.share(name))
def set_randoms(self, randoms):
self.secrets["u"], self.secrets["v"], self.secrets["w"] = randoms
def xor(self, x_name, y_name, z_name):
x = self.secrets[x_name]
y = self.secrets[y_name]
self.secrets[z_name] = x ^ y
def xor_with_constant(self, x_name, c, z_name):
x = self.secrets[x_name]
self.secrets[z_name] = c ^ x
def and_with_public(self, x_name, y_name, z_name):
x = self.secrets[x_name]
y = self.publics[y_name]
self.secrets[z_name] = x & y
def and_publics(self, x_name, y_name, z_name):
x = self.publics[x_name]
y = self.publics[y_name]
self.publics[z_name] = x & y
def xor_with_public(self, x_name, c_name, z_name):
x = self.secrets[x_name]
c = self.publics[c_name]
self.secrets[z_name] = c ^ x
def open(self, x_name, other_share):
x = self.secrets[x_name]
z = x ^ other_share
self.publics[x_name] = z
return z
def send(self, x_name):
return self.secrets[x_name]
def receive(self, x_name, x):
self.secrets[x_name] = x
class Alice(Party):
pass
class Bob(Party):
def xor_with_constant(self, x_name, c, z_name):
"""
xor with constant is asymmetric, so bob simply saves x to z.
"""
x = self.secrets[x_name]
self.secrets[z_name] = x
def xor_with_public(self, x_name, c_name, z_name):
"""
xor with constant is asymmetric, so bob simply saves x to z.
"""
x = self.secrets[x_name]
self.secrets[z_name] = x
class Dealer:
def gen_randoms(self):
u = random.randint(0,1)
v = random.randint(0,1)
w = u & v
u_b = random.randint(0,1)
u_a = u_b ^ u
v_b = random.randint(0,1)
v_a = v_b ^ v
w_b = random.randint(0,1)
w_a = w_b ^ w
assert((u_a ^ u_b) & (v_a ^ v_b) == w)
assert(w_a ^ w_b == w)
self.randoms_alice = (u_a, v_a, w_a)
self.randoms_bob = (u_b, v_b, w_b)
def run(da, db, ds, ra, rb, rs):
proto = Protocol(Alice(da=da, db=db, ds=ds), Bob(ra=ra, rb=rb, rs=rs))
proto.xor_with_constant("ra", 1, "k1")
proto.xor_with_constant("rb", 1, "k2")
proto.xor_with_constant("rs", 1, "k3")
proto.and_("da", "k1", "l1")
proto.and_("db", "k2", "l2")
proto.and_("ds", "k3", "l3")
proto.xor_with_constant("l1", 1, "m1")
proto.xor_with_constant("l2", 1, "m2")
proto.xor_with_constant("l3", 1, "m3")
proto.and_("m1", "m2", "n")
proto.and_("n", "m3", "z")
z = proto.output("z")
return z
def main():
green = 0
red = 0
for i, recipient in enumerate(BloodType):
for j, donor in enumerate(BloodType):
z = run(*donor.value, *recipient.value)
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)