def test_cbc_oram_enc_dec(): aes = Cipher("AES-128-CBC") key = urandom(16) iv = urandom(16) ipt = [urandom(16), "Hello"] IV, data = ipt IV0 = KDF(iv, IV).iv enc = aes.enc(key, IV0) data = enc.update(data) data += enc.finalize() IV1 = KDF(iv, data[0:16]).iv enc = aes.enc(key, IV1) IV = enc.update(IV) IV += enc.finalize() datablock = [IV, data] IV, data = datablock IV1 = KDF(iv, data[0:16]).iv dec = aes.dec(key, IV1) IV = dec.update(IV) IV += dec.finalize() IV0 = KDF(iv, IV).iv dec = aes.dec(key, IV0) data = dec.update(data) data += dec.finalize() datablock = [IV, data] assert ipt == datablock
def aes_enc_dec(self, key, iv, input_): """A helper function which implements the AES-128 encryption in counter mode CTR""" aes = Cipher("AES-128-CTR") enc = aes.enc(key, iv) output = enc.update(input_) output += enc.finalize() return output
def aes_enc_dec(self, data): ''' AES Enc/Dec ''' aes = Cipher("AES-128-CBC") enc = aes.enc(self.key, self.ctr_iv) output = enc.update(data) output += enc.finalize() return output
def test_ctr_enc_dec(): aes = Cipher("AES-128-CTR") key = urandom(16) iv = urandom(16) enc = aes.enc(key, iv) ipt = "Hello" ciphertext = enc.update(ipt) ciphertext += enc.finalize() dec = aes.enc(key, iv) plaintext = dec.update(ciphertext) plaintext += dec.finalize() assert ipt == plaintext
def aes_cbc(self, key, IV, data): #Input: Encryption key, Initialization vector, data to encrypt #Output: Encrypted data with IV and key aes = Cipher("AES-128-CBC") enc = aes.enc(key, IV) output = enc.update(data) output += enc.finalize() return output
def aes_ctr_enc_dec(key, iv, input): """ A helper function that implements AES Counter (CTR) Mode encryption and decryption. Expects a key (16 byte), and IV (16 bytes) and an input plaintext / ciphertext. If it is not obvious convince yourself that CTR encryption and decryption are in fact the same operations. """ aes = Cipher("AES-128-CTR") enc = aes.enc(key, iv) output = enc.update(input) output += enc.finalize() return output
def cascade_rebuild(self, seed, key, iv, flags, inverse, data): #Cascade Rebuild data processing function #print "MIX: Cascade Rebuild enc/perm" if not inverse and not flags[2]: data = self.permute(seed, data, inverse) aes = Cipher("AES-128-CTR") enc = aes.enc(key, iv) #for i in range(len(data)): # data[i] = enc.update(data[i]) # data[i] += enc.finalize() if inverse and not flags[2]: data = self.permute(seed, data, inverse) return data
def parallel_rebuild(self, seed, key, iv, flags, inverse, data): #Parallel Rebuild data processing function if not inverse and not flags[2]: #print "Par Rebuild enc/perm : permute", seed, data, inverse data = self.permute(seed, data, inverse) aes = Cipher("AES-128-CTR") enc = aes.enc(key, iv) #for i in range(len(data)): # data[i] = enc.update(data[i]) # data[i] += enc.finalize() if inverse and not flags[2]: #print "Par Rebuild enc/perm : permute", seed, data, inverse data = self.permute(seed, data, inverse) return data
def mix_operate(message, triplet, setup): mname, mpub, msec = triplet elem, forward, backwards = message G, o, g, o_bytes = setup aes = Cipher("AES-128-CTR") # Derive first key k1 = KDF((msec * elem).export()) # Derive the blinding factor b = Bn.from_binary(k1.b) % o new_elem = b * elem # Check the forward MAC mac1 = hmac.new(k1.kmac, forward[20:], digestmod=sha1).digest() assert forward[:20] == mac1 # Decrypt the payload enc = aes.dec(k1.kenc, k1.iv) pt = enc.update(forward[20:]) pt += enc.finalize() # Parse the forward message xfrom, xto, the_bs, new_forw = pt[:4], pt[4:8], pt[8:8+o_bytes], pt[8+o_bytes:] old_bs = Bn.from_binary(the_bs) # Now encrypt the return part k2 = KDF(((msec * old_bs) * elem).export()) new_bs = old_bs.mod_inverse(o).binary() enc = aes.enc(k2.kenc, k2.iv) new_back_body = enc.update(xto + xfrom + new_bs + backwards) new_back_body += enc.finalize() mac2 = hmac.new(k2.kmac, new_back_body, digestmod=sha1).digest() new_back = mac2 + new_back_body return ((xfrom, xto), (new_elem, new_forw, new_back) )
class SphinxParams: def __init__(self, group=None, header_len=192, body_len=1024, assoc_len=0, k=16, dest_len=16): self.aes = Cipher("AES-128-CTR") self.cbc = Cipher("AES-128-CBC") self.assoc_len = assoc_len self.max_len = header_len self.zero_pad = b"\x00" * (2 * self.max_len) self.m = body_len self.k = k self.dest_len = dest_len self.group = group if not group: self.group = Group_ECC() # The LIONESS PRP def aes_ctr(self, k, m, iv=zero_iv): #k = bytes(k) #m = bytes(m) c = self.aes.enc(k, iv).update(m) return bytes(c) # The LIONESS PRP def aes_cbc_enc(self, k, m, iv=zero_iv): cipher = self.cbc.enc(k, iv) cipher.set_padding(False) c = cipher.update(m) #c = c + cipher.finalize() return bytes(c) # The LIONESS PRP def aes_cbc_dec(self, k, m, iv=zero_iv): cipher = self.cbc.dec(k, iv) cipher.set_padding(False) c = cipher.update(m) #c = c + cipher.finalize() return bytes(c) def lioness_enc(self, key, message): assert len(key) == self.k assert len(message) >= self.k * 2 # Round 1 k1 = self.hash(message[self.k:] + key + b'1')[:self.k] c = self.aes_ctr(key, message[:self.k], iv=k1) r1 = c + message[self.k:] # Round 2 c = self.aes_ctr(key, r1[self.k:], iv=r1[:self.k]) r2 = r1[:self.k] + c # Round 3 k3 = self.hash(r2[self.k:] + key + b'3')[:self.k] c = self.aes_ctr(key, r2[:self.k], iv=k3) r3 = c + r2[self.k:] # Round 4 c = self.aes_ctr(key, r3[self.k:], r3[:self.k]) r4 = r3[:self.k] + c return r4 def lioness_dec(self, key, message): assert len(key) == self.k assert len(message) >= self.k * 2 r4 = message r4_short, r4_long = r4[:self.k], r4[self.k:] # Round 4 r3_long = self.aes_ctr(key, r4_long, iv=r4_short) r3_short = r4_short # Round 3 k2 = self.hash(r3_long + key + b'3')[:self.k] r2_short = self.aes_ctr(key, r3_short, iv=k2) r2_long = r3_long # Round 2 r1_long = self.aes_ctr(key, r2_long, iv=r2_short) r1_short = r2_short # Round 1 k0 = self.hash(r1_long + key + b'1')[:self.k] c = self.aes_ctr(key, r1_short, iv=k0) r0 = c + r1_long return r0 # AES-CTR operation def xor_rho(self, key, plain): assert len(key) == self.k return self.aes_ctr(key, plain) # The HMAC; key is of length k, output is of length k def mu(self, key, data): mac = hmac.new(key, data, digestmod=sha256).digest()[:self.k] return mac # The PRP; key is of length k, data is of length m def pi(self, key, data): assert len(key) == self.k assert len(data) == self.m return self.lioness_enc(key, data) # The inverse PRP; key is of length k, data is of length m def pii(self, key, data): assert len(key) == self.k assert len(data) == self.m return self.lioness_dec(key, data) def small_perm(self, key, data): assert len(data) == self.k # aes = Cipher("AES-128-CBC") enc = self.cbc.enc(key, None) enc.set_padding(False) c = enc.update(data) #c += enc.finalize() return c def small_perm_inv(self, key, data): assert len(data) == self.k # aes = Cipher("AES-128-CBC") dec = self.cbc.dec(key, None) dec.set_padding(False) c = dec.update(data) #c += dec.finalize() return c # The various hashes def hash(self, data): return sha256(data).digest() def get_aes_key(self, s): group = self.group return bytes(self.hash(b"aes_key:" + group.printable(s))[:self.k]) def get_aes_key_all(self, s): group = self.group k = self.hash(b"aes_key:" + group.printable(s))[:self.k] num = 3 # (hrho, hmu, htau) iv = b"UltrUltrUltrUltr" material = self.aes.enc(k, iv).update(b"\x00" * self.k * num) return k, [material[self.k * i:self.k * (i + 1)] for i in range(num)] def derive_key(self, k, flavor): assert len(k) == len(flavor) == self.k iv = flavor m = b"\x00" * self.k K = self.aes.enc(k, iv).update(m) return K def hb(self, k): "Compute a hash of alpha and s to use as a blinding factor" K = self.derive_key(k, b"hbhbhbhbhbhbhbhb") return self.group.makeexp(K) def hrho(self, k): "Compute a hash of s to use as a key for the PRG rho" K = self.derive_key(k, b"hrhohrhohrhohrho") return K def hmu(self, k): "Compute a hash of s to use as a key for the HMAC mu" K = self.derive_key(k, b"hmu:hmu:hmu:hmu:") return K # def hmu2(self, k): # "Compute a hash of s to use as a key for the permutation mu2" # K = self.derive_key(k, b"hmu2:hmu2:hmu2:h") # return K def hpi(self, k): "Compute a hash of s to use as a key for the PRP pi" K = self.derive_key(k, b"hpi:hpi:hpi:hpi:") return K def htau(self, k): "Compute a hash of s to use to see if we've seen s before" K = self.derive_key(k, b"htauhtauhtauhtau") return K def h_body_K(self, k): "The Ultrix key to protect the user data." K = self.derive_key(k, b"UbodUbodUbodUbod") return K def h_root_K(self, k): "The Ultrix key to protect the root key." K = self.derive_key(k, b"UrooUrooUrooUroo") return K def derive_user_keys(self, k, iv, number=2): material = self.aes.enc(k, iv).update(b"\x00" * self.k * number) st_ranges = range(0, self.k * number, self.k) return [material[st:st + self.k] for st in st_ranges]
def test_sortpermuteandencrypt(): m1 = Mix("M1", "localhost", 8001, 0, 0, 0) m1.mix.records = 9 m1.mix.list = [] for i in range(3): m1.mix.list.extend([i]) m1.mix.index = 0 m2 = Mix("M2", "localhost", 8002, 0, 0, 0) m2.mix.records = 9 m2.mix.list = [] for i in range(3): m2.mix.list.extend([i]) m2.mix.index = 1 m3 = Mix("M3", "localhost", 8003, 0, 0, 0) m3.mix.records = 9 m3.mix.list = [] for i in range(3): m3.mix.list.extend([i]) m3.mix.index = 2 pubseed = urandom(16) pubseed2 = urandom(16) seed1 = urandom(16) seed2 = urandom(16) seed3 = urandom(16) key1 = urandom(16) key2 = urandom(16) key3 = urandom(16) iv1 = urandom(16) iv2 = urandom(16) iv3 = urandom(16) nbmix = 3 data1 = [] for i in range(3): data1.extend([str(i) * 4]) data2 = [] for i in range(3): data2.extend([str(i + 3) * 4]) data3 = [] for i in range(3): data3.extend([str(i + 2 * 3) * 4]) alloc1 = m1.mix.permute_global(pubseed, 9, nbmix, 1) sortout1 = m1.mix.sort_global_out(data1, alloc1) alloc2 = m2.mix.permute_global(pubseed, 9, nbmix, 1) sortout2 = m2.mix.sort_global_out(data2, alloc2) alloc3 = m3.mix.permute_global(pubseed, 9, nbmix, 1) sortout3 = m3.mix.sort_global_out(data3, alloc3) print "----------------------------------------------------------" received1 = [sortout1[0], sortout2[0], sortout3[0]] received2 = [sortout1[1], sortout2[1], sortout3[1]] received3 = [sortout1[2], sortout2[2], sortout3[2]] alloc1 = m1.mix.permute_global(pubseed, 9, nbmix, 1) sortin1 = m1.mix.sort_global_in(received1, alloc1) aes = Cipher("AES-128-CTR") enc = aes.enc(key1, iv1) for i in range(len(sortin1)): sortin1[i] = enc.update(sortin1[i]) sortin1[i] += enc.finalize() sortin1 = m1.mix.permute(seed1, sortin1, 1) alloc2 = m2.mix.permute_global(pubseed, 9, nbmix, 1) sortin2 = m2.mix.sort_global_in(received2, alloc2) aes = Cipher("AES-128-CTR") enc = aes.enc(key2, iv2) for i in range(len(sortin2)): sortin2[i] = enc.update(sortin2[i]) sortin2[i] += enc.finalize() sortin2 = m2.mix.permute(seed2, sortin2, 1) alloc3 = m3.mix.permute_global(pubseed, 9, nbmix, 1) sortin3 = m3.mix.sort_global_in(received3, alloc3) aes = Cipher("AES-128-CTR") enc = aes.enc(key3, iv3) for i in range(len(sortin3)): sortin3[i] = enc.update(sortin3[i]) sortin3[i] += enc.finalize() sortin3 = m3.mix.permute(seed3, sortin3, 1) alloc1 = m1.mix.permute_global(pubseed2, 9, nbmix, 1) sortout1 = m1.mix.sort_global_out(sortin1, alloc1) alloc2 = m2.mix.permute_global(pubseed2, 9, nbmix, 1) sortout2 = m2.mix.sort_global_out(sortin2, alloc2) alloc3 = m3.mix.permute_global(pubseed2, 9, nbmix, 1) sortout3 = m3.mix.sort_global_out(sortin3, alloc3) print "----------------------------------------------------------" received1 = [sortout1[0], sortout2[0], sortout3[0]] received2 = [sortout1[1], sortout2[1], sortout3[1]] received3 = [sortout1[2], sortout2[2], sortout3[2]] alloc1 = m1.mix.permute_global(pubseed2, 9, nbmix, 1) sortin1 = m1.mix.sort_global_in(received1, alloc1) alloc2 = m2.mix.permute_global(pubseed2, 9, nbmix, 1) sortin2 = m2.mix.sort_global_in(received2, alloc2) alloc3 = m3.mix.permute_global(pubseed2, 9, nbmix, 1) sortin3 = m3.mix.sort_global_in(received3, alloc3) alloc1 = m1.mix.permute_global(pubseed2, 9, nbmix, 0) sortout1 = m1.mix.sort_global_out(sortin1, alloc1) alloc2 = m2.mix.permute_global(pubseed2, 9, nbmix, 0) sortout2 = m2.mix.sort_global_out(sortin2, alloc2) alloc3 = m3.mix.permute_global(pubseed2, 9, nbmix, 0) sortout3 = m3.mix.sort_global_out(sortin3, alloc3) print "----------------------------------------------------------" received1 = [sortout1[0], sortout2[0], sortout3[0]] received2 = [sortout1[1], sortout2[1], sortout3[1]] received3 = [sortout1[2], sortout2[2], sortout3[2]] alloc1 = m1.mix.permute_global(pubseed2, 9, nbmix, 0) sortin1 = m1.mix.sort_global_in(received1, alloc1) sortin1 = m1.mix.permute(seed1, sortin1, 0) aes = Cipher("AES-128-CTR") enc = aes.enc(key1, iv1) for i in range(len(sortin1)): sortin1[i] = enc.update(sortin1[i]) sortin1[i] += enc.finalize() alloc2 = m2.mix.permute_global(pubseed2, 9, nbmix, 0) sortin2 = m2.mix.sort_global_in(received2, alloc2) sortin2 = m2.mix.permute(seed2, sortin2, 0) aes = Cipher("AES-128-CTR") enc = aes.enc(key2, iv2) for i in range(len(sortin2)): sortin2[i] = enc.update(sortin2[i]) sortin2[i] += enc.finalize() alloc3 = m3.mix.permute_global(pubseed2, 9, nbmix, 0) sortin3 = m3.mix.sort_global_in(received3, alloc3) sortin3 = m3.mix.permute(seed3, sortin3, 0) aes = Cipher("AES-128-CTR") enc = aes.enc(key3, iv3) for i in range(len(sortin3)): sortin3[i] = enc.update(sortin3[i]) sortin3[i] += enc.finalize() alloc1 = m1.mix.permute_global(pubseed, 9, nbmix, 0) sortout1 = m1.mix.sort_global_out(sortin1, alloc1) alloc2 = m2.mix.permute_global(pubseed, 9, nbmix, 0) sortout2 = m2.mix.sort_global_out(sortin2, alloc2) alloc3 = m3.mix.permute_global(pubseed, 9, nbmix, 0) sortout3 = m3.mix.sort_global_out(sortin3, alloc3) print "----------------------------------------------------------" received1 = [sortout1[0], sortout2[0], sortout3[0]] received2 = [sortout1[1], sortout2[1], sortout3[1]] received3 = [sortout1[2], sortout2[2], sortout3[2]] alloc1 = m1.mix.permute_global(pubseed, 9, nbmix, 0) sortin1 = m1.mix.sort_global_in(received1, alloc1) alloc2 = m2.mix.permute_global(pubseed, 9, nbmix, 0) sortin2 = m2.mix.sort_global_in(received2, alloc2) alloc3 = m3.mix.permute_global(pubseed, 9, nbmix, 0) sortin3 = m3.mix.sort_global_in(received3, alloc3) data = [] for i in range(9): data.append(str(i) * 4) res = [] for i in range(len(sortin1)): res.append(sortin1[i]) for i in range(len(sortin2)): res.append(sortin2[i]) for i in range(len(sortin3)): res.append(sortin3[i]) print "res", res print "data", data assert data == res
def mix_package(sender, receiver, triplets): ''' Package a message through a mix-net. ''' aes = Cipher("AES-128-CTR") Bs = [] _, ypub, y = sender pubs = [ ypub ] round_trip = triplets + [ receiver ] + list(reversed(triplets)) secrets = [] prod_bs = Bn(1) for i, (mname, mpub, msec) in enumerate(round_trip): xysec2 = (y * prod_bs) * mpub secrets += [ xysec2 ] if __debug__ and msec is not None: xysec1 = (msec * prod_bs) * ypub assert xysec2 == xysec1 # Blinding factor k = KDF(xysec2.export()) b = Bn.from_binary(k.b) % o Bs += [ b ] # y = (b * y) % o prod_bs = (b * prod_bs) % o #ypub = b * ypub pubs += [ prod_bs * ypub ] # Precompute the correction factors correction_factors = [] for i in range(len(triplets)): total_b = Bn(1) for j in range(i, 2 * len(triplets) - i): total_b = (total_b * Bs[j]) % o assert round_trip[i][0] == round_trip[2 * len(triplets) - i][0] assert total_b * pubs[i] == pubs[2 * len(triplets) - i] correction_factors += [ total_b ] all_factors = [] + correction_factors all_factors += [ Bn(1) ] all_factors += [bf.mod_inverse(o) for bf in reversed(correction_factors)] assert len(all_factors) == len(round_trip) # Generate data stream data = [ sender ] + round_trip + [ sender ] addressing = [] for i, _ in enumerate(round_trip): addressing += [(data[1 + i-1][0], data[1 + i+1][0])] # Derive all keys all_data = zip(round_trip, all_factors, pubs, addressing, secrets) all_keys = [] for (mname, mpub, msec), bs, yelem, (xfrom, xto), Ksec in all_data: k1 = KDF(Ksec.export()) k2 = KDF( (bs * Ksec).export()) all_keys += [(k1, k2)] all_data = zip(round_trip, all_factors, pubs, addressing, all_keys) # Build the backwards path prev = '' backwards_stages = [ ] for j in range(len(mix_names) + 1): (mname, mpub, msec), bs, yelem, (xfrom, xto), (k1, k2) = all_data[j] the_bs = bs.mod_inverse(o).binary() enc = aes.enc(k2.kenc, k2.iv) ciphertext = enc.update(xto + xfrom + the_bs + prev) ciphertext += enc.finalize() mac = hmac.new(k2.kmac, ciphertext, digestmod=sha1).digest() prev = mac + ciphertext backwards_stages += [ prev ] # Build the forwards path prev = '' forwards_stages = [] for jp in range(len(mix_names) + 1): j = len(mix_names) - jp (mname, mpub, msec), bs, yelem, (xfrom, xto), (k1, k2) = all_data[j] the_bs = bs.binary() enc = aes.enc(k1.kenc, k1.iv) ciphertext = enc.update(xfrom + xto + the_bs + prev) ciphertext += enc.finalize() mac = hmac.new(k1.kmac, ciphertext, digestmod=sha1).digest() prev = mac + ciphertext forwards_stages += [ prev ] forwards_stages = list(reversed(forwards_stages)) stages = zip(forwards_stages, backwards_stages) # Check all the MACs if __debug__: for j in range(len(mix_names) + 1): (msg_f, msg_b) = stages.pop(0) (mname, mpub, msec), bs, yelem, (xfrom, xto), (k1, k2) = all_data[j] mac1 = hmac.new(k1.kmac, msg_f[20:], digestmod=sha1).digest() assert msg_f[:20] == mac1 enc = aes.dec(k1.kenc, k1.iv) plaintext = enc.update(msg_f[20:]) plaintext += enc.finalize() assert xfrom == plaintext[:4] and xto == plaintext[4:8] mac2 = hmac.new(k2.kmac, msg_b[20:], digestmod=sha1).digest() assert msg_b[:20] == mac2 # End __debug__ return zip(pubs[:len(mix_names) + 1], forwards_stages + [''], [''] + backwards_stages)
def mix_operate(message, triplet, setup, generate_return_message=False): ''' Operate a Mix with a received message, and its keys. ''' mname, mpub, msec = triplet elem, forward, backwards = message G, o, g, o_bytes = setup aes = Cipher("AES-128-CTR") # Derive first key k1 = KDF((msec * elem).export()) # Derive the blinding factor b = Bn.from_binary(k1.b) % o new_elem = b * elem # Check the forward MAC mac1 = hmac.new(k1.kmac, forward[20:], digestmod=sha1).digest() if not (forward[:20] == mac1): raise Exception("Wrong MAC1") # Decrypt the payload enc = aes.dec(k1.kenc, k1.iv) pt = enc.update(forward[20:]) pt += enc.finalize() # Parse the forward message xcode = pt[0] if not (xcode == "0" or xcode == "1"): raise Exception("Wrong routing code") pt = pt[1:] if xcode == "0": xfrom, xto, the_bs, new_forw = pt[:4], pt[4:8], pt[8:8+o_bytes], pt[8+o_bytes:] old_bs = Bn.from_binary(the_bs) # Now package the return part k2 = KDF(((msec * old_bs) * elem).export()) enc = aes.enc(k2.kenc, k2.iv) new_back_body = enc.update("1" + xto + xfrom + backwards) new_back_body += enc.finalize() mac2 = hmac.new(k2.kmac, new_back_body, digestmod=sha1).digest() new_back = mac2 + new_back_body if generate_return_message: ret_elem = old_bs * elem ret_forw = new_back ret_back = None return ((xto, xfrom), (ret_elem, ret_forw, ret_back) ) else: xfrom, xto, new_forw = pt[:4], pt[4:8], pt[8:] # Returns do not need to build returns if not (backwards == None): raise Exception("Backwards header should be None") new_back = None return ((xfrom, xto), (new_elem, new_forw, new_back) )