def mix_server_one_hop(private_key, message_list): """ Implements the decoding for a simple one-hop mix. Each message is decoded in turn: - A shared key is derived from the message public key and the mix private_key. - the hmac is checked against all encrypted parts of the message - the address and message are decrypted, decoded and returned """ 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 len(msg.hmac) == 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] ## Check the HMAC h = Hmac(b"sha512", hmac_key) h.update(msg.address) h.update(msg.message) expected_mac = h.digest() print "my hmac: " + str(msg.hmac) print "ex hmac: " + str(expected_mac[:20]) if not secure_compare(msg.hmac, expected_mac[:20]): raise Exception("HMAC check failure") ## Decrypt the address and the 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) # Decode the address and message address_len, address_full = unpack("!H256s", address_plaintext) message_len, message_full = unpack("!H1000s", message_plaintext) output = (address_full[:address_len], message_full[:message_len]) out_queue += [output] return sorted(out_queue)
def mix_server_one_hop(private_key, message_list): """ Implements the decoding for a simple one-hop mix. Each message is decoded in turn: - A shared key is derived from the message public key and the mix private_key. - the hmac is checked against all encrypted parts of the message - the address and message are decrypted, decoded and returned """ 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 len(msg.hmac) == 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] ## Check the HMAC h = Hmac(b"sha512", hmac_key) h.update(msg.address) h.update(msg.message) expected_mac = h.digest()[:20] if not secure_compare(msg.hmac, expected_mac[:20]): raise Exception("HMAC check failure") ## Decrypt the address and the message iv = b"\x00" * 16 # Why are we using an all zero IV?! # iv = urandom(16) address_plaintext = aes_ctr_enc_dec(address_key, iv, msg.address) message_plaintext = aes_ctr_enc_dec(message_key, iv, msg.message) # Decode the address and message address_len, address_full = unpack("!H256s", address_plaintext) message_len, message_full = unpack("!H1000s", message_plaintext) output = (address_full[:address_len], message_full[:message_len]) out_queue += [output] return sorted(out_queue)
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_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