def __init__(self, polynomial_list, chosen_candidate, G, com_pk): self.group = G self.com_pk = com_pk self.order = com_pk.order time_start_proof = time.process_time() chosen_candidate = Bn.from_num(chosen_candidate) random_to_eval = self.order.random() self.commitment_to_eval = com_pk.commit([chosen_candidate], random_to_eval)[0] value_eval = Bn.from_num(0) random_eval = Bn.from_num(0) commitment_eval = self.com_pk.commit([value_eval], random_eval)[0] self.proof = PolynomialProof( com_pk, polynomial_list, self.commitment_to_eval, commitment_eval, chosen_candidate, value_eval, random_to_eval, random_eval, ) time_end_proof = time.process_time() self.time_proof = time_end_proof - time_start_proof
def verify(self, polynomial_list): value_eval = Bn.from_num(0) random_eval = Bn.from_num(0) commitment_eval = self.com_pk.commit([value_eval], random_eval)[0] time_start_verify = time.process_time() self.proof.verify(self.com_pk, polynomial_list, self.commitment_to_eval, commitment_eval) time_end_verify = time.process_time() self.time_verify = time_end_verify - time_start_verify
def __init__( self, kp_tally, pk_vote, added_token, reencrypted_vote, randomizer, number_dummies, ): # Security parameters self.sec_param = 256 self.bn_two = Bn.from_num(2) self.hash_reduction = self.bn_two.pow(self.sec_param) self.pk_tally = kp_tally.pk self.sk_tally = kp_tally.sk self.group_tally = self.pk_tally.group self.generator_tally = self.group_tally.generator() self.order_tally = self.group_tally.order() self.pk_vote = pk_vote self.group_vote = self.pk_vote.group self.generator_vote = self.pk_vote.generator self.order_vote = self.group_vote.order() self.token = added_token self.reencrypted_vote = reencrypted_vote self.nr_candidates = self.reencrypted_vote.length self.randomizer = randomizer self.number_dummies = number_dummies self.hiding_sk = None self.hiding_randomizer = None
def __init__(self, kp_tally, pk_vote, token, vote, reencrypted_vote, randomizer): # Security parameters self.sec_param = 256 self.bn_two = Bn.from_num(2) self.hash_reduction = self.bn_two.pow(self.sec_param) # Public key used for tokens self.pk_tally = kp_tally.pk self.sk_tally = kp_tally.sk self.group_tally = self.pk_tally.group self.generator_tally = self.group_tally.generator() self.order_tally = self.group_tally.order() # Public key used for votes self.pk_vote = pk_vote self.group_vote = self.pk_vote.group self.generator_vote = self.group_vote.generator() self.order_vote = self.group_vote.order() self.token = token self.vote = vote self.nr_candidates = self.vote.length self.reencrypted_vote = reencrypted_vote self.randomizer = randomizer self.hiding_sk = None self.hiding_randomizer = None
def __init__(self, com_pk, commitment, product, A, randomizers): self.order = com_pk.group.order() self.m = len(A) self.n = len(A[0]) # Maybe here we have a calculation which slows down the running time product_rows_A = [ modular_prod([Bn.from_num(a[i]) for a in A], self.order) for i in range(self.n) ] self.commitment_products, randomizer_commitment_products = com_pk.commit( product_rows_A) self.hadamard = HadamardProductArgument( com_pk, commitment, self.commitment_products, A, randomizers, randomizer_commitment_products, ) self.single_value = SingleValueProdArg( com_pk, self.commitment_products, product, product_rows_A, randomizer_commitment_products, )
def _challenge(elements): """Packages a challenge in a bijective way""" elem = [len(elements)] + elements elem_str = list(map(str, elem)) elem_len = list(map(lambda x: "%s||%s" % (len(x), x), elem_str)) state = "|".join(elem_len) H = sha256() H.update(state.encode("utf8")) return Bn.from_binary(H.digest())
def compute_challenge_poly(transcript, p): """ Compute challenge given transcript """ transcript = flatten(transcript) m = hashlib.sha512() for element in transcript: try: m.update(element.commitment.export()) except AttributeError: try: m.update(hex(element.commitment).encode()) except: m.update(element.commitment.hex().encode()) hashed = m.hexdigest() return (Bn.from_hex(hashed)).mod(Bn.from_num(p))
def election_setup(group, number_voters, security_param): order = group.order() counter_space = Bn.from_num(2).pow(security_param - 2) vids = [] counter = [] for i in range(number_voters): vids.append(order.random()) index = counter_space + counter_space.random() counter.append(index) return vids, counter
def compute_challenge(transcript, p): """Compute challenge given transcript TODO: return something of the right size, l_c bits in the paper. TODO: use proper unique encoding of elements """ m = hashlib.sha512() for element in transcript: try: m.update(element.export()) except AttributeError: try: m.update(hex(element).encode()) except: m.update(hex(element.vid).encode()) m.update(hex(element.index).encode()) m.update(hex(element.tag).encode()) m.update(hex(element.vote).encode()) hashed = m.hexdigest() return (Bn.from_hex(hashed)).mod(Bn.from_num(p))
def __init__(self, commitments, responses, challenge=None): # Security parameters self.sec_param = 256 self.bn_two = Bn.from_num(2) self.hash_reduction = self.bn_two.pow(self.sec_param) ( self.commitment_pk, self.commitment_token, self.commitment_vote1, self.commitment_vote2, ) = commitments self.response_sk, self.response_randomizer = responses self.challenge = challenge
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) # Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() shared_keys = [] hmacs = [] # no blinding factor between Alice and the first mix new_private_key = private_key # the first ciphertext is the plaintext address_cipher = address_plaintext message_cipher = message_plaintext # generate new private key for each mix's public key for public_key in public_keys: # get a shared key shared_element = new_private_key * public_key key_material = sha512(shared_element.export()).digest() # prepend the shared key to the list of shared keys shared_keys = [key_material] + shared_keys # update the private key's value with a blinding factor new_private_key = new_private_key * Bn.from_binary(key_material[48:]) # iterate through the public keys in the reverse order, because we want # to compute hmacs in the reverse order for i, public_key in enumerate(public_keys[::-1]): # Use different parts of the shared key for different operations hmac_key = shared_keys[i][:16] address_key = shared_keys[i][16:32] message_key = shared_keys[i][32:48] # Decrypt the address and the message iv = b"\x00" * 16 # we encrypt the ciphertext address_cipher = aes_ctr_enc_dec(address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, iv, message_cipher) # Create the HMAC h = Hmac(b"sha512", hmac_key) # encrypt the known hmacs for j, hmac in enumerate(hmacs): # Ensure the IV is different for each hmac and encode it iv = pack("H14s", j, b"\x00" * 14) hmacs[j] = aes_ctr_enc_dec(hmac_key, iv, hmac) h.update(hmacs[j]) # finish building the hmac h.update(address_cipher) h.update(message_cipher) # prepend the result to the list of hmacs hmacs = [h.digest()[:20]] + hmacs return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
values_sum = values[0] if len(values) > 1: for i in values[1:]: values_sum = (values_sum + i).mod(modulo) return values_sum if __name__ == "__main__": # import doctest # # doctest.testmod() G = EcGroup() com_pk = com.PublicKey(G, 3) order = com_pk.group.order() A = [ [Bn.from_num(10), Bn.from_num(20), Bn.from_num(30)], [Bn.from_num(40), Bn.from_num(20), Bn.from_num(30)], [Bn.from_num(60), Bn.from_num(20), Bn.from_num(40)], ] for _ in range(10): A.extend(A) length_A = len(A) commits_rands_A = [com_pk.commit(a) for a in A] comm_A = [a[0] for a in commits_rands_A] random_comm_A = [a[1] for a in commits_rands_A] b = modular_prod( [ modular_prod([Bn.from_num(A[i][j])
def mix_server_n_hop(private_key, message_list, final=False): """ Decodes a NHopMixMessage message and outputs either messages destined to the next mix or a list of tuples (address, message) (if final=True) to be sent to their final recipients. Broadly speaking the mix will process each message in turn: - it derives a shared key (using its private_key), - checks the first hmac, - decrypts all other parts, - either forwards or decodes the message. """ G = EcGroup() out_queue = [] # Process all messages for msg in message_list: ## Check elements and lengths if not G.check_point(msg.ec_public_key) or \ not isinstance(msg.hmacs, list) or \ not len(msg.hmacs[0]) == 20 or \ not len(msg.address) == 258 or \ not len(msg.message) == 1002: raise Exception("Malformed input message") ## First get a shared key shared_element = private_key * msg.ec_public_key key_material = sha512(shared_element.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] # Extract a blinding factor for the public_key blinding_factor = Bn.from_binary(key_material[48:]) new_ec_public_key = blinding_factor * msg.ec_public_key ## Check the HMAC h = Hmac(b"sha512", hmac_key) for other_mac in msg.hmacs[1:]: h.update(other_mac) h.update(msg.address) h.update(msg.message) expected_mac = h.digest() if not secure_compare(msg.hmacs[0], expected_mac[:20]): raise Exception("HMAC check failure") ## Decrypt the hmacs, address and the message aes = Cipher("AES-128-CTR") # Decrypt hmacs new_hmacs = [] for i, other_mac in enumerate(msg.hmacs[1:]): # Ensure the IV is different for each hmac iv = pack("H14s", i, b"\x00" * 14) hmac_plaintext = aes_ctr_enc_dec(hmac_key, iv, other_mac) new_hmacs += [hmac_plaintext] # Decrypt address & message iv = b"\x00" * 16 address_plaintext = aes_ctr_enc_dec(address_key, iv, msg.address) message_plaintext = aes_ctr_enc_dec(message_key, iv, msg.message) if final: # Decode the address and message address_len, address_full = unpack("!H256s", address_plaintext) message_len, message_full = unpack("!H1000s", message_plaintext) out_msg = (address_full[:address_len], message_full[:message_len]) out_queue += [out_msg] else: # Pass the new mix message to the next mix out_msg = NHopMixMessage(new_ec_public_key, new_hmacs, address_plaintext, message_plaintext) out_queue += [out_msg] return out_queue
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() iv = b"\x00" * 16 # caluculate the blinding factor of all the # hops, can't be done during hmacs as it's in a different order. # blind_factors = [] shared_keys = [] for i, k in enumerate(public_keys): if i == 0: blind_factors.append(1) else: pub_key = public_keys[i - 1] # The shared key that the hop will use to calculate the factor shared_key = shared_keys[i - 1] key_digest = sha512(shared_key).digest() # the blinding factor that they will use blinding_factor = Bn.from_binary(key_digest[48:]) blind_factors.append(blinding_factor) shared_key = public_keys[i].pt_mul(private_key) for j, fac in enumerate(blind_factors[0:i + 1]): shared_key = shared_key.pt_mul(blind_factors[j]) shared_key = shared_key.export() shared_keys.append(shared_key) ## Reverse the key lists, since we will compute hmacs in reverse order shared_keys.reverse() public_keys.reverse() message_ciphers = [] address_ciphers = [] hmacs = [] previous_hmac_key = None for i, pub in enumerate(public_keys): # Get shared key from precomputed list shared_key = shared_keys[i] key_digest = sha512(shared_key).digest() address_key = key_digest[16:32] message_key = key_digest[32:48] hmac_key = key_digest[:16] ## 1. Encrypt The Message if i == 0: message_cipher = aes_ctr_enc_dec(message_key, iv, message_plaintext) address_cipher = aes_ctr_enc_dec(address_key, iv, address_plaintext) else: message_cipher = aes_ctr_enc_dec(message_key, iv, message_ciphers[i - 1]) address_cipher = aes_ctr_enc_dec(address_key, iv, address_ciphers[i - 1]) message_ciphers.append(message_cipher) address_ciphers.append(address_cipher) ## 2. Encrypt the old HMACs for q, mac in enumerate(hmacs): iv = pack("H14s", len(hmacs) - q - 1, b"\x00" * 14) hmacs[q] = aes_ctr_enc_dec(hmac_key, iv, mac) previous_hmac_key = hmac_key ## 3. Compute the new HMAC h = Hmac(b"sha512", hmac_key) ## Iterate backwards through the previous hmacs for old_mac in hmacs[::-1]: h.update(old_mac) h.update(address_ciphers[i]) h.update(message_ciphers[i]) new_mac = h.digest() hmacs.append(new_mac[:20]) # Hmacs were built in reverse order, put them back in order hmacs.reverse() return NHopMixMessage(client_public_key, hmacs, address_ciphers[len(address_ciphers) - 1], message_ciphers[len(message_ciphers) - 1])
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) hmacs = [] address_cipher = address_plaintext message_cipher = message_plaintext ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() blinding_factor = Bn(1) new_ec_public_keys = [] ## ADD CODE HERE i = 0 for public_key in public_keys: # We don't want to blind the first key if i == 0: new_ec_public_keys.append(public_key) else: ## First get a shared key ## public key part is the last seen public key shared_element = private_key * new_ec_public_keys[-1] key_material = sha512(shared_element.export()).digest() # Building up list of all public keys - they have been blinded blinding_factor *= Bn.from_binary(key_material[48:]) new_ec_public_keys.append(blinding_factor * public_key) i += 1 # Want to encrypt message with the first mix's key on the outside - reverse our public keys for this for key in reversed(new_ec_public_keys): ## First get a shared key shared_element = private_key * key key_material = sha512(shared_element.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] iv = b"\x00" * 16 # Build cipher on top of cipher from last mix to create message such as P1(P2(P3...(M)...)) address_cipher = aes_ctr_enc_dec(address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, iv, message_cipher) ## Check the HMAC h = Hmac(b"sha512", hmac_key) # Decrypt hmacs new_hmacs = [] for i, other_mac in enumerate(hmacs): # Ensure the IV is different for each hmac iv = pack("H14s", i, b"\x00" * 14) hmac_plaintext = aes_ctr_enc_dec(hmac_key, iv, other_mac) h.update(hmac_plaintext) new_hmacs += [hmac_plaintext] h.update(address_cipher) h.update(message_cipher) expected_mac = h.digest()[:20] hmacs = [expected_mac] + new_hmacs return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() address_cipher = None message_cipher = None hmacs = [] blinded_public_keys = [public_keys[0]] blinding_factor = Bn(1) for public_key in public_keys[1:]: ## First get a shared key shared_element = private_key * blinded_public_keys[-1] key_material = sha512(shared_element.export()).digest() blinding_factor *= Bn.from_binary(key_material[48:]) blind_pk = blinding_factor * public_key blinded_public_keys.append(blind_pk) blinded_public_keys.reverse() for public_key in blinded_public_keys: # ## First get a shared key # Get a shared key shared_element = private_key * public_key key_material = sha512(shared_element.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] h = Hmac(b"sha512", hmac_key) for other_mac in hmacs: h.update(other_mac) if address_cipher is None: address_cipher = aes_ctr_enc_dec(address_key, b'\x00' * 16, address_plaintext) else: address_cipher = aes_ctr_enc_dec(address_key, b'\x00' * 16, address_cipher) if message_cipher is None: message_cipher = aes_ctr_enc_dec(message_key, b'\x00' * 16, message_plaintext) else: message_cipher = aes_ctr_enc_dec(message_key, b'\x00' * 16, message_cipher) h.update(address_cipher) h.update(message_cipher) mac = h.digest()[:20] hmacs.append(mac) hmacs.reverse() return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() ## ADD CODE HERE # Generate key materials key_materials = [] next_blinding_factor = None for pubkey in public_keys: shared_element = None if not next_blinding_factor is None: private_key *= next_blinding_factor shared_element = private_key * pubkey key_material = sha512(shared_element.export()).digest() hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] next_blinding_factor = Bn.from_binary(key_material[48:]) key_materials += [(hmac_key, address_key, message_key)] new_hmacs = [] address_cipher = bytes(address_plaintext) message_cipher = bytes(message_plaintext) for i, key_material in reversed(list(enumerate(key_materials))): # Encrypt the address and the message iv = b"\x00" * 16 address_cipher = aes_ctr_enc_dec(key_material[1], iv, address_cipher) message_cipher = aes_ctr_enc_dec(key_material[2], iv, message_cipher) # Encrypt the HMACs for i, hmac in enumerate(new_hmacs): iv = pack("H14s", i, b"\x00" * 14) new_hmacs[i] = aes_ctr_enc_dec(key_material[0], iv, hmac) # Compute the HMAC h = Hmac(b"sha512", key_material[0]) for hmac in new_hmacs: h.update(hmac) h.update(address_cipher) h.update(message_cipher) new_hmac = h.digest()[:20] new_hmacs = [new_hmac] + new_hmacs return NHopMixMessage(client_public_key, new_hmacs, address_cipher, message_cipher)
def __init__( self, com_pk, A, B, random_comm_A, random_comm_B, bilinear_const=Bn.from_decimal("1"), ): """ :param com_pk: Commitment key :param commitment_A: Commitment of A :param commitment_B: Commitment of B :param A: Matrix formed by rows a_i :param B: Matrix formed by rows b_i :param random_comm_A vector of random values used for commitment_A :param random_comm_B: vector of random values used for commitment_B """ self.order = com_pk.group.order() self.m = len(A) self.n = len(A[0]) self.bilinear_const = bilinear_const # Prepare announcement A.insert(0, [self.order.random() for _ in range(self.n)]) B.append([self.order.random() for _ in range(self.n)]) random_comm_A.insert(0, self.order.random()) random_comm_B.append(self.order.random()) self.announcement_a0, _ = com_pk.commit_reduced( A[0], self.n, random_comm_A[0]) self.announcement_bm, _ = com_pk.commit_reduced( B[-1], self.n, random_comm_B[-1]) diagonals = [] for k in range(2 * self.m + 1): diagonal = 0 for i in range(self.m + 1): j = self.m - k + i if j < 0: continue if j > self.m: break diagonal += (self.bilinear_map(A[i], B[j], self.bilinear_const, self.order)).mod(self.order) diagonals.append(diagonal) commitment_rand_diagonals = [ self.order.random() for _ in range(2 * self.m + 1) ] commitment_rand_diagonals[self.m + 1] = 0 self.announcement_diagonals = [ com_pk.commit_reduced([diagonals[i]], 1, commitment_rand_diagonals[i])[0] for i in range(self.m * 2 + 1) ] # Prepare challenge (for the moment we only put two announcements, as I yet need to determine how to deal with # the matrices. Maybe I form a class, maybe not. Once decided, I'll add them here (same for announcement of # diagonals). self.challenge = compute_challenge( [self.announcement_a0, self.announcement_bm], self.order) # Compute the response A_modified = [[ (A[j][i] * (self.challenge.mod_pow(j, self.order))).mod(self.order) for i in range(self.n) ] for j in range(self.m + 1)] self.response_as = [ modular_sum(x, self.order) for x in zip(*A_modified[:self.m + 1]) ] self.response_randomizer_A = modular_sum( [(self.challenge.mod_pow(i, self.order) * random_comm_A[i]).mod( self.order) for i in range(self.m + 1)], self.order, ) B_modified = [[ B[j][i] * (self.challenge.mod_pow(self.m - j, self.order)) for i in range(self.n) ] for j in range(self.m + 1)] self.response_bs = [ modular_sum(x, self.order) for x in zip(*B_modified[:self.m + 1]) ] self.response_randomizer_B = modular_sum( [(self.challenge.mod_pow(self.m - i, self.order) * random_comm_B[i]).mod(self.order) for i in range(self.m + 1)], self.order, ) self.response_randomizer_diagonals = modular_sum( [(self.challenge.mod_pow(i, self.order) * commitment_rand_diagonals[i]).mod(self.order) for i in range(self.m * 2 + 1)], self.order, )
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() shared_keys = [] # PART 1 # use of a blinding factor to provide bit-wise unlikability of # the public key associated with the message for public_key in public_keys: # get a shared key shared_element = private_key * public_key key_material = sha512(shared_element.export()).digest() # store into list from back to front shared_keys = [key_material] + shared_keys # update private key value of client with a blinding factor for next shared key derivation private_key = private_key * Bn.from_binary(key_material[48:]) address_cipher = address_plaintext message_cipher = message_plaintext # PART 2 # inclusion of a (list) of hmacs as the second part of the mix message # done in reverse order to compute hmacs hmacs = [] for i, public_key in enumerate(reversed(shared_keys)): # Use different parts of the shared key for different operations hmac_key = shared_keys[i][:16] address_key = shared_keys[i][16:32] message_key = shared_keys[i][32:48] # Ecrypt the address and the message iv = b"\x00" * 16 address_cipher = aes_ctr_enc_dec(address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, iv, message_cipher) # Check HMAC h = Hmac(b"sha512", hmac_key) # PART 3 # Encryption of the hmacs (in addition to the address and message) at each step of mixing # loop skipped on first call for j, other_hmac in enumerate(hmacs): # Ensure the IV is different for each hmac iv = pack("H14s", j, b"\x00" * 14) hmacs[j] = aes_ctr_enc_dec(hmac_key, iv, other_hmac) h.update(hmacs[j]) h.update(address_cipher) h.update(message_cipher) hmacs = [h.digest()[:20]] + hmacs ## add result to hmacs list return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() # initilise lists for hmacs and blinding_keys hmacs = [] blinding_keys = [] # initilise variables address_cipher = address_plaintext message_cipher = message_plaintext # initial blinding_factor set to 1 blinding_factor = Bn(1) # append first public key to list blinding_keys.append(public_keys[0]) length = len(public_keys) for i in range(1, length): # generate shared element shared = private_key * blinding_keys[-1] material = sha512(shared.export()).digest() # generate another bliding factor blinding_factor = blinding_factor * Bn.from_binary(material[48:]) # calculate and append blinding factor to a list public_key = public_keys[i] blinding_keys = blinding_keys + [blinding_factor * public_key] # In reverse order calculate message for each hop using generated blinding factors blinding_keys = reversed(blinding_keys) for key in blinding_keys: # generate shared element shared = private_key * key material = sha512(shared.export()).digest() # split shared key for different operations hmac_key = material[:16] address_key = material[16:32] message_key = material[32:48] # random inistialisation vector iv = b"\x00"*16 # encoding address and message address_cipher = aes_ctr_enc_dec(address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, iv, message_cipher) # temporary list of hmacs temp_hmacs = [] h = Hmac(b"sha512", hmac_key) for i in range(0,len(hmacs)): prev_mac = hmacs[i] # Ensure the IV is different for each hmac iv = pack("H14s", i, b"\x00"*14) # encode hmac_plaintext = aes_ctr_enc_dec(hmac_key, iv, prev_mac) # add the encoded hmac to the temporary list temp_hmacs = temp_hmacs + [hmac_plaintext] h.update(hmac_plaintext) h.update(address_cipher) h.update(message_cipher) # turn h into binary expected_mac = h.digest() # take first 20 bits expected_mac = expected_mac[:20] # insert expected_mac (final mac) to the beggining of the list temp_hmacs = [expected_mac] + temp_hmacs # update hmacs list with temp_hmacs list hmacs = temp_hmacs return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def __init__(self, com_pk, commitment, product, committed_values, randomizer): self.n = len(committed_values) self.order = com_pk.group.order() self.sec_param_l_e = 160 self.sec_param_l_s = 80 self.bn_two = Bn.from_num(2) # Prepare announcement products = [committed_values[0]] for i in range(1, self.n): products.append( (products[i - 1] * committed_values[i]).mod(self.order)) commitment_rand_one = self.order.random() commitment_rand_two = self.order.random() commitment_rand_three = self.order.random() d_randoms = [self.order.random() for _ in range(self.n)] delta_randoms = [self.order.random() for _ in range(self.n)] delta_randoms[0] = d_randoms[0] delta_randoms[-1] = 0 value_to_commit_two = [ -delta_randoms[i] * d_randoms[i + 1] for i in range(self.n - 1) ] value_to_commit_three = [ delta_randoms[i + 1] - committed_values[i + 1] * delta_randoms[i] - products[i] * d_randoms[i + 1] for i in range(self.n - 1) ] self.announcement_one, _ = com_pk.commit(d_randoms, commitment_rand_one) self.announcement_two, _ = com_pk.commit_reduced( value_to_commit_two, self.n - 1, commitment_rand_two) self.announcement_three, _ = com_pk.commit_reduced( value_to_commit_three, self.n - 1, commitment_rand_three) # Compute challenge [Verify validity of this] self.challenge = compute_challenge( [ commitment, product, self.announcement_one, self.announcement_two, self.announcement_three, ], self.order, ) # Compute response self.response_committed_values = [ (self.challenge * committed_values[i] + d_randoms[i]).mod( self.order) for i in range(self.n) ] self.response_product = [ (self.challenge * products[i] + delta_randoms[i]).mod(self.order) for i in range(self.n) ] self.response_randomizer = (self.challenge * randomizer + commitment_rand_one).mod(self.order) self.response_randomizer_commitments = ( self.challenge * commitment_rand_three + commitment_rand_two).mod( self.order)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # This assertion was originally commented out, can instead check each of the keys one by one for public_key in public_keys: assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() ## ADD CODE HERE # Calculate shared keys, taking into account blinding shared_elems = [] blinded = [] new = public_keys[0] for i, public_key in enumerate(public_keys): shared = private_key * new shared_elems.append(shared) if i == len(public_keys) - 1: break key_material = sha512(shared.export()).digest() blinding = Bn.from_binary(key_material[48:]) blinded.append(blinding) blind = blinded[0] for b in range(len(blinded[1:])): blind = blind * blinded[b + 1] new = blind * public_keys[i + 1] # Shared keys are used 'backwards' shared_elems.reverse() public_keys.reverse() # At the start of the encryption chain, the payload is in plaintext address_cipher = address_plaintext message_cipher = message_plaintext # Perform the encoding at each stage, using the shared elements calculated hmacs = [] for i, public_key in enumerate(public_keys): shared_element = shared_elems[i] # Get the appropriate key from each part of the shared key key_material = sha512(shared_element.export()).digest() hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] # Encrypt the message and address at each stage iv = b"\x00" * 16 address_cipher = aes_ctr_enc_dec(address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, iv, message_cipher) # Encrypt the hmacs at each stage for j, hm in enumerate(hmacs): iv = pack("H14s", j, b"\x00" * 14) hmac_plaintext = aes_ctr_enc_dec(key_material[:16], iv, hm) hmacs[j] = hmac_plaintext # Generate the expected mac for each stage h = Hmac(b"sha512", hmac_key) for hm in hmacs: h.update(hm) h.update(address_cipher) h.update(message_cipher) expected_mac = h.digest()[:20] # Each hmac is inserted at the start of the list, as the last hmac is the first to be # inspected - the last encryption is also the first to be decrypted hmacs.insert(0, expected_mac) return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) address_cipher = address_plaintext message_cipher = message_plaintext ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() ## ADD CODE HERE ###Initialise blinding factor and blindking keys blinding_factor = Bn(1) blinding_keys=[] ##initialise Hmacs array hmacs = [] ###start from second key as first key is not blinded in the mix for i in range(len(public_keys)): ###first key is not blinded so since blinding public_key = blinding_factor*public_keys[i] blinding_keys.append(public_key) ## shared_element = private_key * blinding_keys[i] key_material = sha512(shared_element.export()).digest() blinding_factor *= Bn.from_binary(key_material[48:]) ### reverse order as you encrypt from last mix first for k in reversed(blinding_keys): ##shared element shared_element = private_key * k key_material = sha512(shared_element.export()).digest() ###produce keys for hmac address and message hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] ##assign iv for ciphers and encyrpt iv = b"\x00" * 16 address_cipher = aes_ctr_enc_dec(address_key,iv,address_cipher) message_cipher = aes_ctr_enc_dec(message_key,iv,message_cipher) h = Hmac(b"sha512",hmac_key)## initialise hmac ####produce hmacs temp = [] for j,other in enumerate(hmacs): ###seperate iv for each hmac iv = pack("H14s",j,b"\x00"*14) hmac_plaintext = aes_ctr_enc_dec(hmac_key,iv,other) temp+=[hmac_plaintext] h.update(hmac_plaintext) h.update(address_cipher) h.update(message_cipher) expected_mac = h.digest()[:20] temp =[expected_mac]+temp hmacs=temp return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
product *= i return product if __name__ == "__main__": # import doctest # # doctest.testmod() G = EcGroup() com_pk = com.PublicKey(G, 3) key_pair = elgamal.KeyPair(G) pk = key_pair.pk ctxts = [pk.encrypt((i) * G.generator()) for i in range(9)] ctxts = [ctxts[i * 3:(i + 1) * 3] for i in range(3)] exponents = [2, 0, 1, 3, 5, 8, 6, 7, 4] exponents_Bn = [Bn.from_num(i) for i in exponents] exponents = [exponents_Bn[i * 3:(i + 1) * 3] for i in range(3)] randomizers = [G.order().random() for _ in range(3)] reencryption_randomization = G.order().random() product_ctxts = prod([ MultiExponantiation.ctxt_weighted_sum(ctxts[i], exponents[i]) for i in range(3) ]) exponantiated_reencrypted_product = ( pk.encrypt(G.infinite(), reencryption_randomization) * product_ctxts) commitment_permutation = [ com_pk.commit(exponents[i], randomizers[i])[0] for i in range(3) ]
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() hmacs = [] new_public_keys = [] # process the keys with blinding_factor blinding_factor = Bn(1) for public_key in public_keys: public_key = blinding_factor * public_key shared_element = private_key * public_key key_material = sha512(shared_element.export()).digest() # Extract a blinding factor for the public_key blinding_factor *= Bn.from_binary(key_material[48:]) new_public_keys.insert( 0, public_key) # we need to encode messages in reverse order address_cipher = address_plaintext message_cipher = message_plaintext for public_key in new_public_keys: shared_element = private_key * public_key key_material = sha512(shared_element.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] # Decrypt address & message iv = b"\x00" * 16 address_cipher = aes_ctr_enc_dec(address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, iv, message_cipher) # Check the HMAC h = Hmac(b"sha512", hmac_key) # Decrypt hmacs new_hmacs = [] for i, other_mac in enumerate(hmacs): # Ensure the IV is different for each hmac iv = pack("H14s", i, b"\x00" * 14) hmac_plaintext = aes_ctr_enc_dec(hmac_key, iv, other_mac) new_hmacs += [hmac_plaintext] h.update(hmac_plaintext) h.update(address_cipher) h.update(message_cipher) expected_mac = h.digest()[:20] hmacs = new_hmacs hmacs.insert(0, expected_mac) return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() ## ADD CODE HERE hmacs = [] shared_keys = [] ## generate all used shared key for public_key in public_keys: shared_element = private_key * public_key key_material = sha512(shared_element.export()).digest() ## Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] ## Extract a blinding factor for next private_key blinding_factor = Bn.from_binary(key_material[48:]) private_key = blinding_factor * private_key shared_keys.append([hmac_key, address_key, message_key]) for shared_key in reversed(shared_keys): ## get the shared key hmac_key, address_key, message_key = shared_key[0], shared_key[ 1], shared_key[2] ## Encrypt the address and the message iv = b"\x00" * 16 address_cipher = aes_ctr_enc_dec(address_key, iv, address_plaintext) message_cipher = aes_ctr_enc_dec(message_key, iv, message_plaintext) address_plaintext = address_cipher message_plaintext = message_cipher ## HMAC new_hmacs = [] h = Hmac(b"sha512", hmac_key) # Encrypt hmacs for i, hmac in enumerate(hmacs): # Ensure the IV is different for each hmac iv = pack("H14s", i, b"\x00" * 14) hmac_ciphertext = aes_ctr_enc_dec(hmac_key, iv, hmac) h.update(hmac_ciphertext) new_hmacs += [hmac_ciphertext] h.update(address_cipher) h.update(message_cipher) hmac = h.digest()[:20] new_hmacs.insert(0, hmac) hmacs = new_hmacs return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) # Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() hmacs = [] blinding_factor = Bn(1) new_pub_keys = [] # Get a list of new blinded public keys counter = 0 for key in public_keys: if counter == 0: new_pub_keys.append(key) else: shared_element = private_key * new_pub_keys[-1] shared_key = sha512(shared_element.export()).digest() blinding_factor *= Bn.from_binary(shared_key[48:]) new_key = blinding_factor * key new_pub_keys.append(new_key) counter += 1 #reverse the list as you start encrypting from last hop new_pub_keys.reverse() address_cipher = address_plaintext message_cipher = message_plaintext for pub_key in new_pub_keys: # Generate Keying Material new_shared_material = private_key * pub_key new_shared_key = sha512(new_shared_material.export()).digest() # Calculate Other Keys hmac_key = new_shared_key[:16] address_key = new_shared_key[16:32] message_key = new_shared_key[32:48] iv = b"\x00"*16 address_cipher = aes_ctr_enc_dec(address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, iv, message_cipher) decrypted_hmacs = [] h = Hmac(b"sha512", hmac_key) # Decrypt HMACs and update them for i,other_hmac in enumerate(hmacs): hmac_iv = pack("H14s", i, b"\x00"*14) decrypted_hmac = aes_ctr_enc_dec(hmac_key, hmac_iv, other_hmac) h.update(decrypted_hmac) decrypted_hmacs.append(decrypted_hmac) # Update HMAC of message and cipher h.update(address_cipher) h.update(message_cipher) expected_hmac = h.digest()[:20] # Add new HMACs to list hmacs = [expected_hmac] + decrypted_hmacs ## ADD CODE HERE return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() # Get list of private keys multiplied with bliding factors private_keys = list() for public_key in public_keys: # Add private key to list private_keys.append(private_key) # Get shared key shared_element = private_key * public_key key_material = sha512(shared_element.export()).digest() # Extract a blinding factor for the private_key blinding_factor = Bn.from_binary(key_material[48:]) private_key = blinding_factor * private_key hmacs = list() address_cipher = address_plaintext message_cipher = message_plaintext for private_key, public_key in reversed(zip(private_keys, public_keys)): # Get shared key shared_element = private_key * public_key key_material = sha512(shared_element.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] # Encrypt address & message iv = b"\x00"*16 address_cipher = aes_ctr_enc_dec(address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, iv, message_cipher) # Generate next HMAC and encrypt previous HMACs h = Hmac(b"sha512", hmac_key) for i in range(0, len(hmacs)): iv = pack("H14s", i, b"\x00"*14) hmacs[i] = aes_ctr_enc_dec(hmac_key, iv, hmacs[i]) h.update(hmacs[i]) h.update(address_cipher) h.update(message_cipher) expected_mac = h.digest()[:20] hmacs.insert(0, expected_mac) return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() ## Get shared keys (top-down) keyring = namedtuple("Keyring", ["hmac_key", "address_key", "message_key"]) shared_keys = [] accumulated_blinding_factor = Bn(1) for public_key in public_keys: shared_key = accumulated_blinding_factor * private_key * public_key key_material = sha512(shared_key.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] blinding_factor = Bn.from_binary(key_material[48:]) accumulated_blinding_factor = accumulated_blinding_factor * blinding_factor # Store keyring shared_keys.append(keyring(hmac_key, address_key, message_key)) ## Construct NHop message (bottom-up) hmacs = [] address_cipher = address_plaintext message_cipher = message_plaintext for shared_key in reversed(shared_keys): # Encrypt address and message; accumulate iv = b"\x00" * 16 address_cipher = aes_ctr_enc_dec(shared_key.address_key, iv, address_cipher) message_cipher = aes_ctr_enc_dec(shared_key.message_key, iv, message_cipher) # Encrypt other HMACs for i, hmac in enumerate(hmacs): # Ensure the IV is different for each hmac iv = pack("H14s", i, b"\x00" * 14) hmacs[i] = aes_ctr_enc_dec(shared_key.hmac_key, iv, hmac) # Get HMAC h = Hmac(b"sha512", shared_key.hmac_key) for hmac in hmacs: h.update(hmac) h.update(address_cipher) h.update(message_cipher) expected_mac = h.digest()[:20] hmacs.insert(0, expected_mac) return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() ## ADD CODE HERE shared_element = None address_cipher = None message_cipher = None hmacs = [] hmackeys = [] messkeys = [] addkeys = [] messciphers = [] addciphers = [] for i in range(len(public_keys)): ## First get a shared key #* shared_element = private_key * public_keys[i] key_material = sha512(shared_element.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] # This section: like the beginning of mix_server_n_hop address_key = key_material[16:32] message_key = key_material[32:48] # Extract a blinding factor for the new private key blinding_factor = Bn.from_binary(key_material[48:]) private_key = blinding_factor * private_key #* # Add keys to corresponding arrays hmackeys += [hmac_key] addkeys += [address_key] messkeys += [message_key] for i in range(len(public_keys)): # Encrypt address & message iv = b"\x00"*16 if i==0: address_cipher = aes_ctr_enc_dec(addkeys[len(public_keys)-1-i], iv, address_plaintext) message_cipher = aes_ctr_enc_dec(messkeys[len(public_keys)-1-i], iv, message_plaintext) else: address_cipher = aes_ctr_enc_dec(addkeys[len(public_keys)-1-i], iv, address_cipher) message_cipher = aes_ctr_enc_dec(messkeys[len(public_keys)-1-i], iv, message_cipher) messciphers += [message_cipher] addciphers += [address_cipher] for i in range(len(public_keys)): # Encryption of hmacs. Individual as we need whole ciphertext to compute macs correctly # Use mac keys in reverse as last ciphertext needs first key new_hmacs = [] for j, other_mac in enumerate(hmacs): # Ensure IV is not the same for each hmac iv = pack("H14s", j, b"\x00"*14) hmac_plaintext = aes_ctr_enc_dec(hmackeys[len(public_keys)-1-i], iv, other_mac) new_hmacs += [hmac_plaintext] h = Hmac(b"sha512", hmackeys[len(public_keys)-1-i]) # Calculate new digest new_hmacs = new_hmacs[::-1] for other_mac in reversed(new_hmacs): h.update(other_mac) h.update(addciphers[i]) h.update(messciphers[i]) digest = h.digest() digest = digest[:20] new_hmacs += [digest] hmacs = new_hmacs[::-1] hmacs = new_hmacs[::-1] return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_client_n_hop(public_keys, address, message): """ Encode a message to travel through a sequence of mixes with a sequence public keys. The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). """ G = EcGroup() blinding_factor = Bn(1) # assert G.check_point(public_key) assert isinstance(address, bytes) and len(address) <= 256 assert isinstance(message, bytes) and len(message) <= 1000 # Encode the address and message # use those encoded values as the payload you encrypt! address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) ## Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() ## ADD CODE HERE hmacs = [] address_cipher = address_plaintext message_cipher = message_plaintext blinded_public_keys = [public_keys[0]] for public_key in public_keys[1:]: ## First get a shared key shared_element = private_key * blinded_public_keys[-1] key_material = sha512(shared_element.export()).digest() blinding_factor *= Bn.from_binary(key_material[48:]) blinded_public_keys.append(blinding_factor * public_key) for key in reversed(blinded_public_keys): ## First get a shared key shared_element = private_key * key key_material = sha512(shared_element.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] address_cipher = aes_ctr_enc_dec(address_key, b"\x00" * 16, address_cipher) message_cipher = aes_ctr_enc_dec(message_key, b"\x00" * 16, message_cipher) ## Check the HMAC h = Hmac(b"sha512", hmac_key) # implement hmacs new_hmacs = [] for i, other_mac in enumerate(hmacs): # Ensure the IV is different for each hmac hmac_plaintext = aes_ctr_enc_dec(hmac_key, pack("H14s", i, b"\x00" * 14), other_mac) h.update(hmac_plaintext) new_hmacs += [hmac_plaintext] h.update(address_cipher) h.update(message_cipher) expected_mac = h.digest()[:20] hmacs = [expected_mac] + new_hmacs return NHopMixMessage(client_public_key, hmacs, address_cipher, message_cipher)
def mix_server_n_hop(private_key, message_list, use_blinding_factor=False, final=False): """ Decodes a NHopMixMessage message and outputs either messages destined to the next mix or a list of tuples (address, message) (if final=True) to be sent to their final recipients. Broadly speaking the mix will process each message in turn: - it derives a shared key (using its private_key), - checks the first hmac, - decrypts all other parts, - Either forwards or decodes the message. The implementation of the blinding factor is optional and therefore only activated in the bonus tests. """ G = EcGroup() out_queue = [] # Process all messages for msg in message_list: ## Check elements and lengths if not G.check_point(msg.ec_public_key) or \ not isinstance(msg.hmacs, list) or \ not len(msg.hmacs[0]) == 20 or \ not len(msg.address) == 258 or \ not len(msg.message) == 1002: raise Exception("Malformed input message") ## First get a shared key shared_element = private_key * msg.ec_public_key key_material = sha512(shared_element.export()).digest() # Use different parts of the shared key for different operations hmac_key = key_material[:16] address_key = key_material[16:32] message_key = key_material[32:48] # Extract a blinding factor for the public_key # (only if you're brave enough for the bonus task) new_ec_public_key = msg.ec_public_key if (use_blinding_factor): blinding_factor = Bn.from_binary(key_material[48:]) new_ec_public_key = blinding_factor * msg.ec_public_key ## Check the HMAC h = Hmac(b"sha512", hmac_key) for other_mac in msg.hmacs[1:]: h.update(other_mac) h.update(msg.address) h.update(msg.message) expected_mac = h.digest() if not secure_compare(msg.hmacs[0], expected_mac[:20]): raise Exception("HMAC check failure") # Decrypt hmacs new_hmacs = [] for i, other_mac in enumerate(msg.hmacs[1:]): # Ensure the IV is different for each hmac iv = pack("H14s", i, b"\x00"*14) hmac_plaintext = aes_ctr_enc_dec(hmac_key, iv, other_mac) new_hmacs += [hmac_plaintext] # Decrypt address & message iv = b"\x00"*16 address_plaintext = aes_ctr_enc_dec(address_key, iv, msg.address) message_plaintext = aes_ctr_enc_dec(message_key, iv, msg.message) if final: # Decode the address and message address_len, address_full = unpack("!H256s", address_plaintext) message_len, message_full = unpack("!H1000s", message_plaintext) out_msg = (address_full[:address_len], message_full[:message_len]) out_queue += [out_msg] else: # Pass the new mix message to the next mix out_msg = NHopMixMessage(new_ec_public_key, new_hmacs, address_plaintext, message_plaintext) out_queue += [out_msg] return out_queue