def _evaluate_gate_grr3(self, gate, l1, l2): if l1.pp_bit == l2.pp_bit == 0: result = label.from_bitstring(crypto_utils.decrypt(l2, crypto_utils.decrypt(l1, bytes(16)))) print("BOB: The select bits of both labels for this gate are zero. Decrypting 0^N, instead of accessing " "the garbled table") return result else: return self._evaluate_gate_pp(gate, l1, l2)
def _evaluate_gate_pp(self, gate, l1, l2): # as we learned in the section on point-and-permute, # we select the (2 * r1 + r2)th entry in the table label_index = l1.pp_bit * 2 + l2.pp_bit # since there are only three entries, we have to shift the index accordingly if config.USE_GRR3: label_index = label_index - 1 print( "BOB: Using point-and-permute. {}{} corresponds to entry {}; decrypting entry {} with ciphertext {}".format( int(l1.pp_bit), int(l2.pp_bit), label_index, label_index, gate.table[label_index])) return label.from_bitstring(crypto_utils.decrypt(l2, crypto_utils.decrypt(l1, gate.table[label_index])))
def _evaluate_gate_standard(self, gate, l1, l2): out_label = None for entry in gate.table: try: candidate = crypto_utils.decrypt(l2, crypto_utils.decrypt(l1, entry)) # with the AES implementation we are using, it seems that the padding check functions as a way to # verify correct decryption, since if a message isn't padded right that probably means we don't have # the right keys. But it's still good practice to verify the label is correct anyway. if candidate[0:config.CLASSIC_SECURITY_PARAMETER * 8].int == 0: out_label = label.from_bitstring(candidate) break else: raise ValueError() except ValueError: print("BOB: Failed to decrypt {}! Trying next entry...".format(entry.hex)) if out_label is None: print( "ERROR: Bob was unable to decrypt all four encrypted entries of gate {}. This should never happen. " "Terminating program...".format( str(gate))) return out_label
def garble_gate_grr3(self, gate, in1_labels, in2_labels): print("ALICE: Garbling gate {} ".format(gate)) gate.table = [] # grab the input labels that will be placed first in the table in1_zero_pp = in1_labels[0] if in1_labels[ 0].pp_bit == 0 else in1_labels[1] in2_zero_pp = in2_labels[0] if in2_labels[ 0].pp_bit == 0 else in2_labels[1] print("ALICE: Generating zero label for labels {}, {}".format( in1_zero_pp, in2_zero_pp)) zero_label = label.from_bitstring( crypto_utils.decrypt( in2_zero_pp, crypto_utils.decrypt(in1_zero_pp, bitstring.Bits(128)))) print("ALICE: Found c = {}, where that 0^N encrypted under {}, {} = c". format(zero_label, in1_zero_pp, in2_zero_pp)) # Compute the underlying value of the zero label. Since label objects don't track their underlying semantic # value, we have do this in a slightly roundabout wy zero_label_value = gate.run(in1_labels.index(in1_zero_pp), in2_labels.index(in2_zero_pp)) # manually generate the other output label if config.USE_FREE_XOR: zero_label_other = label.from_bitstring(zero_label.to_bitstring() ^ self.R) else: zero_label_other = label.from_bitstring( bitstring.BitArray(bytes=os.urandom(16))) # fix the other label point&permute bit, which needs to be the opposite of the zero label--whatever its one is zero_label_other.pp_bit = not zero_label.pp_bit out_labels = [None] * 2 out_labels[zero_label_value] = zero_label out_labels[not zero_label_value] = zero_label_other self.wire_labels[gate.out] = out_labels # now we proceed in a very similar way to the vanilla garbling for selection in [(0, 0), (0, 1), (1, 0), (1, 1)]: # for each possible input configuration... # grab the labels corresponding to that configuration l1 = in1_labels[selection[0]] l2 = in2_labels[selection[1]] # no need to do this for the pair corresponding to the zero ciphertext... this is the point of GRR3 if l1 != in1_zero_pp or l2 != in2_zero_pp: output_bit = gate.run(selection[0], selection[1]) lout = out_labels[output_bit] print(" Encrypting label {} with {}, {}, for {} = {} {} {}". format(lout, l1, l2, output_bit, selection[0], gate.op, selection[1])) encrypted_label = crypto_utils.encrypt( l1, crypto_utils.encrypt(l2, lout)) self.encrypted_entries[encrypted_label] = ( l1, l2 ) # cache ciphertext -> keys, to make it easier to p&p print(" Encrypted label: " + str(encrypted_label.hex)) gate.table.append(encrypted_label) return out_labels
def _evaluate_gate_free_XOR(self, gate, l1, l2): out_label = label.from_bitstring(l1.to_bitstring() ^ l2.to_bitstring()) print("BOB: Using Free-XOR; computing {} XOR {} = {}".format(l1, l2, out_label)) return out_label # it's that easy!
def simplest_OT(m0, m1, i): print( "OT: Alice, the sender, has messages m0 = {} and m1 = {}. Bob, the receiver wishes to receive message m{}." .format(m0, m1, i)) # the finite group of integers upon which we operate; 65521 is the largest 16 bit prime. # We use a group of prime order so that every element is a generator G = 65521 g = random.randint( 2, G) # this value is agreed upon by both parties, and shared a = random.randint(2, 2**16 - 1) # Sender (Alice's) secret a b = random.randint(2, 2**16 - 1) # Receiver (Bob's) secret b print("OT: Alice generates a = {}, and keeps a secret from Bob".format(a)) print("OT: Bob generates b = {}, and keeps b secret from Alice".format(b)) A = (g**a) % G print("OT: Alice transfers A = g^a = {} to Bob".format(A)) B = 0 if i == 0: B = (g**b) % G print( "OT: Since i = 0, Bob calculates B = g^b = {} and transfers it to Alice" .format(B)) elif i == 1: B = (((g**b) % G) * A) % G print( "OT: Since i = 0, Bob calculates B = Ag^b = {} and transfers it to Alice" .format(B)) else: raise ValueError("The receiver must choose an index from (0, 1)") k = (A**b) % G print("OT: Bob transfers B to Alice, and computes k = A^b = {}".format(k)) k0 = (B**a) % G k1 = (((B * modinv(A, G) % G))**a) % G print("OT: Alice computes k0 = B^a = {}, kl (B/A)^a = {}".format(k0, k1)) c0 = crypto_utils.encrypt(bitstring.Bits(uint=k0, length=2**16), m0) c1 = crypto_utils.encrypt(bitstring.Bits(uint=k1, length=2**16), m1) print( "OT: Alice encrypts m0 with key k0 and transfers to Bob c0 = E(k0, m0) = {}" .format(c0)) print( "OT: Alice encrypts m1 with key k1 and transfers to Bob c1 = E(k1, m1) = {}" .format(c1)) if i == 0: decrypted_c0 = label.from_bitstring( crypto_utils.decrypt(bitstring.BitArray(uint=k, length=2**16), c0.bytes)) print("OT: Bob successfully decrypts c0 to yield {}".format( decrypted_c0)) # assert(decrypted_c0 == m0) return decrypted_c0 else: decrypted_c1 = label.from_bitstring( crypto_utils.decrypt(bitstring.BitArray(uint=k, length=2**16), c1.bytes)) print("OT: Bob successfully decrypts c0 to yield {}".format( decrypted_c1)) return decrypted_c1