def buildEncryptedForwardPacket(payload, exitType, exitInfo, path1, path2, key, paddingPRNG=None, secretRNG=None): """Construct a forward message encrypted with the public key of a given user. payload: The payload to deliver. Must be 28K-42b long. exitType: The routing type for the final node. (2 bytes, >=0x100) exitInfo: The routing info for the final node, not including tag. path1: Sequence of ServerInfo objects for the first leg of the path path2: Sequence of ServerInfo objects for the 2nd leg of the path key: Public key of this message's recipient. paddingPRNG: random number generator used to generate padding. If None, a new PRNG is initialized. """ if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() if secretRNG is None: secretRNG = paddingPRNG LOG.debug("Encoding encrypted forward message for %s-byte payload", len(payload)) LOG.debug(" Using path %s/%s", [s.getNickname() for s in path1], [s.getNickname() for s in path2]) LOG.debug(" Delivering to %04x:%r", exitType, exitInfo) # (For encrypted-forward messages, we have overhead for OAEP padding # and the session key, but we save 20 bytes by spilling into the tag.) assert len(payload) == PAYLOAD_LEN - ENC_FWD_OVERHEAD # Generate the session key, and prepend it to the payload. sessionKey = secretRNG.getBytes(SECRET_LEN) payload = sessionKey+payload # We'll encrypt the first part of the new payload with RSA, and the # second half with Lioness, based on the session key. rsaDataLen = key.get_modulus_bytes()-OAEP_OVERHEAD rsaPart = payload[:rsaDataLen] lionessPart = payload[rsaDataLen:] # RSA encryption: To avoid leaking information about our RSA modulus, # we keep trying to encrypt until the MSBit of our encrypted value is # zero. while 1: encrypted = Crypto.pk_encrypt(rsaPart, key) if not (ord(encrypted[0]) & 0x80): break # Lioness encryption. k= Crypto.Keyset(sessionKey).getLionessKeys(Crypto.END_TO_END_ENCRYPT_MODE) lionessPart = Crypto.lioness_encrypt(lionessPart, k) # Now we re-divide the payload into the part that goes into the tag, and # the 28K of the payload proper... payload = encrypted + lionessPart tag = payload[:TAG_LEN] payload = payload[TAG_LEN:] exitInfo = tag + exitInfo assert len(payload) == 28*1024 # And now, we can finally build the message. return _buildPacket(payload, exitType, exitInfo, path1, path2,paddingPRNG)
def buildEncryptedForwardPacket(payload, exitType, exitInfo, path1, path2, key, paddingPRNG=None, secretRNG=None): """Construct a forward message encrypted with the public key of a given user. payload: The payload to deliver. Must be 28K-42b long. exitType: The routing type for the final node. (2 bytes, >=0x100) exitInfo: The routing info for the final node, not including tag. path1: Sequence of ServerInfo objects for the first leg of the path path2: Sequence of ServerInfo objects for the 2nd leg of the path key: Public key of this message's recipient. paddingPRNG: random number generator used to generate padding. If None, a new PRNG is initialized. """ if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() if secretRNG is None: secretRNG = paddingPRNG log.debug("Encoding encrypted forward message for %s-byte payload", len(payload)) log.debug(" Using path %s/%s", [s.getNickname() for s in path1], [s.getNickname() for s in path2]) log.debug(" Delivering to %04x:%r", exitType, exitInfo) # (For encrypted-forward messages, we have overhead for OAEP padding # and the session key, but we save 20 bytes by spilling into the tag.) assert len(payload) == PAYLOAD_LEN - ENC_FWD_OVERHEAD # Generate the session key, and prepend it to the payload. sessionKey = secretRNG.getBytes(SECRET_LEN) payload = sessionKey+payload # We'll encrypt the first part of the new payload with RSA, and the # second half with Lioness, based on the session key. rsaDataLen = key.get_modulus_bytes()-OAEP_OVERHEAD rsaPart = payload[:rsaDataLen] lionessPart = payload[rsaDataLen:] # RSA encryption: To avoid leaking information about our RSA modulus, # we keep trying to encrypt until the MSBit of our encrypted value is # zero. while 1: encrypted = Crypto.pk_encrypt(rsaPart, key) if not (ord(encrypted[0]) & 0x80): break # Lioness encryption. k= Crypto.Keyset(sessionKey).getLionessKeys(Crypto.END_TO_END_ENCRYPT_MODE) lionessPart = Crypto.lioness_encrypt(lionessPart, k) # Now we re-divide the payload into the part that goes into the tag, and # the 28K of the payload proper... payload = encrypted + lionessPart tag = payload[:TAG_LEN] payload = payload[TAG_LEN:] exitInfo = tag + exitInfo assert len(payload) == 28*1024 # And now, we can finally build the message. return _buildPacket(payload, exitType, exitInfo, path1, path2,paddingPRNG)
def _constructMessage(secrets1, secrets2, header1, header2, payload): """Helper method: Builds a message, given both headers, all known secrets, and the padded payload. If using a reply block for header2, secrets2 should be null. """ assert len(payload) == PAYLOAD_LEN assert len(header1) == len(header2) == HEADER_LEN if secrets2: # (Copy secrets2 so we don't reverse the original) secrets2 = secrets2[:] # If we're not using a reply block, encrypt the payload for # each key in the second path, in reverse order. secrets2.reverse() for secret in secrets2: ks = Crypto.Keyset(secret) key = ks.getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE) payload = Crypto.lioness_encrypt(payload, key) # Encrypt header2 with a hash of the payload. key = Crypto.lioness_keys_from_payload(payload) header2 = Crypto.lioness_encrypt(header2, key) # Encrypt payload with a hash of header2. Now tagging either will make # both unrecoverable. key = Crypto.lioness_keys_from_header(header2) payload = Crypto.lioness_encrypt(payload, key) # Copy secrets1 so we don't reverse the original. secrets1 = secrets1[:] # Now, encrypt header2 and the payload for each node in path1, reversed. secrets1.reverse() for secret in secrets1: ks = Crypto.Keyset(secret) hkey = ks.getLionessKeys(Crypto.HEADER_ENCRYPT_MODE) pkey = ks.getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE) header2 = Crypto.lioness_encrypt(header2, hkey) payload = Crypto.lioness_encrypt(payload, pkey) return Packet(header1, header2, payload).pack()
def _constructMessage(secrets1, secrets2, header1, header2, payload): """Helper method: Builds a message, given both headers, all known secrets, and the padded payload. If using a reply block for header2, secrets2 should be null. """ assert len(payload) == PAYLOAD_LEN assert len(header1) == len(header2) == HEADER_LEN if secrets2: # (Copy secrets2 so we don't reverse the original) secrets2 = secrets2[:] # If we're not using a reply block, encrypt the payload for # each key in the second path, in reverse order. secrets2.reverse() for secret in secrets2: ks = Crypto.Keyset(secret) key = ks.getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE) payload = Crypto.lioness_encrypt(payload, key) # Encrypt header2 with a hash of the payload. key = Crypto.lioness_keys_from_payload(payload) header2 = Crypto.lioness_encrypt(header2, key) # Encrypt payload with a hash of header2. Now tagging either will make # both unrecoverable. key = Crypto.lioness_keys_from_header(header2) payload = Crypto.lioness_encrypt(payload, key) # Copy secrets1 so we don't reverse the original. secrets1 = secrets1[:] # Now, encrypt header2 and the payload for each node in path1, reversed. secrets1.reverse() for secret in secrets1: ks = Crypto.Keyset(secret) hkey = ks.getLionessKeys(Crypto.HEADER_ENCRYPT_MODE) pkey = ks.getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE) header2 = Crypto.lioness_encrypt(header2,hkey) payload = Crypto.lioness_encrypt(payload,pkey) return Packet(header1, header2, payload).pack()
def _decodeReplyPayload(payload, secrets, check=0): """Helper function: decode a reply payload, given a known list of packet master secrets. If 'check' is true, then 'secrets' may be overlong. Return values are the same as decodePayload. [secrets must be in _reverse_ order] """ # Reverse the 'decrypt' operations of the reply mixes, and the initial # 'decrypt' of the originating user... for sec in secrets: k = Crypto.Keyset(sec).getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE) payload = Crypto.lioness_encrypt(payload, k) if check and _checkPayload(payload): return parsePayload(payload) # If 'check' is false, then we might still have a good payload. If # 'check' is true, we don't. if check or not _checkPayload(payload): raise MixError("Invalid checksum on reply payload") return parsePayload(payload)