diff --git a/week3.py b/week3.py new file mode 100644 index 0000000..e5410c3 --- /dev/null +++ b/week3.py @@ -0,0 +1,201 @@ +from __future__ import annotations + + +import random + +from crypto.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)