202 lines
5.5 KiB
Python
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)
|