def encodeMessage(message, overhead, uncompressedFragmentPrefix="", paddingPRNG=None): """Given a message, compress it, fragment it into individual payloads, and add extra fields (size, hash, etc) as appropriate. Return a list of strings, each of which is a message payload suitable for use in build*Message. message: the initial message overhead: number of bytes to omit from each payload, given the type ofthe message encoding. (0 or ENC_FWD_OVERHEAD) uncompressedFragmentPrefix: If we fragment the message, we add this string to the message after compression but before whitening and fragmentation. paddingPRNG: generator for padding. Note: If multiple strings are returned, be sure to shuffle them before transmitting them to the network. """ assert overhead in (0, ENC_FWD_OVERHEAD) if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() origLength = len(message) payload = compressData(message) length = len(payload) if length > 1024 and length*20 <= origLength: LOG.warn("Message is very compressible and will look like a zlib bomb") paddingLen = PAYLOAD_LEN - SINGLETON_PAYLOAD_OVERHEAD - overhead - length # If the compressed payload fits in 28K, we're set. if paddingLen >= 0: # We pad the payload, and construct a new SingletonPayload, # including this payload's size and checksum. payload += paddingPRNG.getBytes(paddingLen) p = SingletonPayload(length, None, payload) p.computeHash() return [ p.pack() ] # Okay, we need to fragment the message. First, add the prefix if needed. if uncompressedFragmentPrefix: payload = uncompressedFragmentPrefix+payload # Now generate a message ID messageid = Crypto.getCommonPRNG().getBytes(FRAGMENT_MESSAGEID_LEN) # Figure out how many chunks to divide it into... p = mixminion.Fragments.FragmentationParams(len(payload), overhead) # ... fragment the payload into chunks... rawFragments = p.getFragments(payload) fragments = [] # ... and annotate each chunk with appropriate payload header info. for i in xrange(len(rawFragments)): pyld = FragmentPayload(i, None, messageid, p.length, rawFragments[i]) pyld.computeHash() fragments.append(pyld.pack()) rawFragments[i] = None return fragments
def encodeMessage(message, overhead, uncompressedFragmentPrefix="", paddingPRNG=None): """Given a message, compress it, fragment it into individual payloads, and add extra fields (size, hash, etc) as appropriate. Return a list of strings, each of which is a message payload suitable for use in build*Message. message: the initial message overhead: number of bytes to omit from each payload, given the type ofthe message encoding. (0 or ENC_FWD_OVERHEAD) uncompressedFragmentPrefix: If we fragment the message, we add this string to the message after compression but before whitening and fragmentation. paddingPRNG: generator for padding. Note: If multiple strings are returned, be sure to shuffle them before transmitting them to the network. """ assert overhead in (0, ENC_FWD_OVERHEAD) if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() origLength = len(message) payload = compressData(message) length = len(payload) if length > 1024 and length*20 <= origLength: log.warn("Message is very compressible and will look like a zlib bomb") paddingLen = PAYLOAD_LEN - SINGLETON_PAYLOAD_OVERHEAD - overhead - length # If the compressed payload fits in 28K, we're set. if paddingLen >= 0: # We pad the payload, and construct a new SingletonPayload, # including this payload's size and checksum. payload += paddingPRNG.getBytes(paddingLen) p = SingletonPayload(length, None, payload) p.computeHash() return [ p.pack() ] # Okay, we need to fragment the message. First, add the prefix if needed. if uncompressedFragmentPrefix: payload = uncompressedFragmentPrefix+payload # Now generate a message ID messageid = Crypto.getCommonPRNG().getBytes(FRAGMENT_MESSAGEID_LEN) # Figure out how many chunks to divide it into... p = mixminion.Fragments.FragmentationParams(len(payload), overhead) # ... fragment the payload into chunks... rawFragments = p.getFragments(payload) fragments = [] # ... and annotate each chunk with appropriate payload header info. for i in xrange(len(rawFragments)): pyld = FragmentPayload(i, None, messageid, p.length, rawFragments[i]) pyld.computeHash() fragments.append(pyld.pack()) rawFragments[i] = None return fragments
def _buildReplyBlockImpl(path, exitType, exitInfo, expiryTime=0, secretPRNG=None, tag=None): """Helper function: makes a reply block, given a tag and a PRNG to generate secrets. Returns a 3-tuple containing (1) a newly-constructed reply block, (2) a list of secrets used to make it, (3) a tag. path: A list of ServerInfo exitType: Routing type to use for the final node exitInfo: Routing info for the final node, not including tag. expiryTime: The time at which this block should expire. secretPRNG: A PRNG to use for generating secrets. If not provided, uses an AES counter-mode stream seeded from our entropy source. Note: the secrets are generated so that they will be used to encrypt the message in reverse order. tag: If provided, a 159-bit tag. If not provided, a new one is generated. """ if secretPRNG is None: secretPRNG = Crypto.getCommonPRNG() if expiryTime is None: # XXXX This is dangerous, and should go away; the user should # XXXX *always* specify an expiry time. LOG.warn("Inferring expiry time for reply block") expiryTime = min([s.getValidUntil() for s in path]) checkPathLength(None, path, exitType, exitInfo, explicitSwap=0) LOG.debug("Building reply block for path %s", [s.getNickname() for s in path]) LOG.debug(" Delivering to %04x:%r", exitType, exitInfo) # The message is encrypted first by the end-to-end key, then by # each of the path keys in order. We need to reverse these steps, so we # generate the path keys back-to-front, followed by the end-to-end key. secrets = [ secretPRNG.getBytes(SECRET_LEN) for _ in range(len(path)+1) ] headerSecrets = secrets[:-1] headerSecrets.reverse() sharedKey = secrets[-1] # (This will go away when we deprecate 'stateful' reply blocks if tag is None: tag = _getRandomTag(secretPRNG) header = _buildHeader(path, headerSecrets, exitType, tag+exitInfo, paddingPRNG=Crypto.getCommonPRNG()) return ReplyBlock(header, expiryTime, SWAP_FWD_HOST_TYPE, path[0].getMMTPHostInfo().pack(), sharedKey), secrets, tag
def _buildReplyBlockImpl(path, exitType, exitInfo, expiryTime=0, secretPRNG=None, tag=None): """Helper function: makes a reply block, given a tag and a PRNG to generate secrets. Returns a 3-tuple containing (1) a newly-constructed reply block, (2) a list of secrets used to make it, (3) a tag. path: A list of ServerInfo exitType: Routing type to use for the final node exitInfo: Routing info for the final node, not including tag. expiryTime: The time at which this block should expire. secretPRNG: A PRNG to use for generating secrets. If not provided, uses an AES counter-mode stream seeded from our entropy source. Note: the secrets are generated so that they will be used to encrypt the message in reverse order. tag: If provided, a 159-bit tag. If not provided, a new one is generated. """ if secretPRNG is None: secretPRNG = Crypto.getCommonPRNG() if expiryTime is None: # XXXX This is dangerous, and should go away; the user should # XXXX *always* specify an expiry time. log.warn("Inferring expiry time for reply block") expiryTime = min([s.getValidUntil() for s in path]) checkPathLength(None, path, exitType, exitInfo, explicitSwap=0) log.debug("Building reply block for path %s", [s.getNickname() for s in path]) log.debug(" Delivering to %04x:%r", exitType, exitInfo) # The message is encrypted first by the end-to-end key, then by # each of the path keys in order. We need to reverse these steps, so we # generate the path keys back-to-front, followed by the end-to-end key. secrets = [ secretPRNG.getBytes(SECRET_LEN) for _ in range(len(path)+1) ] headerSecrets = secrets[:-1] headerSecrets.reverse() sharedKey = secrets[-1] # (This will go away when we deprecate 'stateful' reply blocks if tag is None: tag = _getRandomTag(secretPRNG) header = _buildHeader(path, headerSecrets, exitType, tag+exitInfo, paddingPRNG=Crypto.getCommonPRNG()) return ReplyBlock(header, expiryTime, SWAP_FWD_HOST_TYPE, path[0].getMMTPHostInfo().pack(), sharedKey), secrets, tag
def buildReplyBlock(path, exitType, exitInfo, userKey, expiryTime=None, secretRNG=None): """Construct a 'state-carrying' reply block that does not require the reply-message recipient to remember a list of secrets. Instead, all secrets are generated from an AES counter-mode stream, and the seed for the stream is stored in the 'tag' field of the final block's routing info. (See the spec for more info). path: a list of ServerInfo objects exitType,exitInfo: The address to deliver the final message. userKey: a string used to encrypt the seed. NOTE: We used to allow another kind of 'non-state-carrying' reply block that stored its secrets on disk, and used an arbitrary tag to determine which set of secrets to use. """ if secretRNG is None: secretRNG = Crypto.getCommonPRNG() # We need to pick the seed to generate our keys. To make the decoding # step a little faster, we find a seed such that H(seed|userKey|"Validate") # ends with 0. This way, we can detect whether we really have a reply # message with 99.6% probability. (Otherwise, we'd need to repeatedly # lioness-decrypt the payload in order to see whether the message was # a reply.) while 1: seed = _getRandomTag(secretRNG) if Crypto.sha1(seed + userKey + "Validate")[-1] == "\x00": break prng = Crypto.AESCounterPRNG(Crypto.sha1(seed + userKey + "Generate")[:16]) replyBlock, secrets, tag = _buildReplyBlockImpl(path, exitType, exitInfo, expiryTime, prng, seed) STATUS.log("GENERATED_SURB", formatBase64(tag)) return replyBlock
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 buildReplyPacket(payload, path1, replyBlock, paddingPRNG=None): """Build a message using a reply block. 'path1' is a sequence of ServerInfo for the nodes on the first leg of the path. 'payload' must be exactly 28K long. """ if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() LOG.debug("Encoding reply message for %s-byte payload", len(payload)) LOG.debug(" Using path %s/??", [s.getNickname() for s in path1]) assert len(payload) == PAYLOAD_LEN # Encrypt the payload so that it won't appear as plaintext to the # crossover note. (We use 'decrypt' so that the message recipient can # simply use 'encrypt' to reverse _all_ the steps of the reply path.) k = Crypto.Keyset(replyBlock.encryptionKey).getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE) payload = Crypto.lioness_decrypt(payload, k) return _buildPacket(payload, None, None, path1=path1, path2=replyBlock)
def buildReplyPacket(payload, path1, replyBlock, paddingPRNG=None): """Build a message using a reply block. 'path1' is a sequence of ServerInfo for the nodes on the first leg of the path. 'payload' must be exactly 28K long. """ if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() LOG.debug("Encoding reply message for %s-byte payload", len(payload)) LOG.debug(" Using path %s/??",[s.getNickname() for s in path1]) assert len(payload) == PAYLOAD_LEN # Encrypt the payload so that it won't appear as plaintext to the # crossover note. (We use 'decrypt' so that the message recipient can # simply use 'encrypt' to reverse _all_ the steps of the reply path.) k = Crypto.Keyset(replyBlock.encryptionKey).getLionessKeys( Crypto.PAYLOAD_ENCRYPT_MODE) payload = Crypto.lioness_decrypt(payload, k) return _buildPacket(payload, None, None, path1=path1, path2=replyBlock)
def buildForwardPacket(payload, exitType, exitInfo, path1, path2, paddingPRNG=None, suppressTag=0): """Construct a forward message. payload: The payload to deliver. Must be exactly 28K. If the payload is None, 28K of random data is sent. 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 paddingPRNG: random number generator used to generate padding. If None, a new PRNG is initialized. suppressTag: if true, do not include a decodind handle in the routingInfo for this packet. Neither path1 nor path2 may be empty. If one is, MixError is raised. """ if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() if not path1: raise MixError("First leg of path is empty") if not path2: raise MixError("Second leg of path is empty") assert len(payload) == PAYLOAD_LEN LOG.trace( " Building packet with path %s:%s; delivering to %04x:%r", ",".join([s.getNickname() for s in path1]), ",".join([s.getNickname() for s in path2]), exitType, exitInfo, ) # Choose a random decoding tag. if not suppressTag: tag = _getRandomTag(paddingPRNG) exitInfo = tag + exitInfo return _buildPacket(payload, exitType, exitInfo, path1, path2, paddingPRNG, suppressTag=suppressTag)
def buildReplyBlock(path, exitType, exitInfo, userKey, expiryTime=None, secretRNG=None): """Construct a 'state-carrying' reply block that does not require the reply-message recipient to remember a list of secrets. Instead, all secrets are generated from an AES counter-mode stream, and the seed for the stream is stored in the 'tag' field of the final block's routing info. (See the spec for more info). path: a list of ServerInfo objects exitType,exitInfo: The address to deliver the final message. userKey: a string used to encrypt the seed. NOTE: We used to allow another kind of 'non-state-carrying' reply block that stored its secrets on disk, and used an arbitrary tag to determine which set of secrets to use. """ if secretRNG is None: secretRNG = Crypto.getCommonPRNG() # We need to pick the seed to generate our keys. To make the decoding # step a little faster, we find a seed such that H(seed|userKey|"Validate") # ends with 0. This way, we can detect whether we really have a reply # message with 99.6% probability. (Otherwise, we'd need to repeatedly # lioness-decrypt the payload in order to see whether the message was # a reply.) while 1: seed = _getRandomTag(secretRNG) if Crypto.sha1(seed+userKey+"Validate")[-1] == '\x00': break prng = Crypto.AESCounterPRNG(Crypto.sha1(seed+userKey+"Generate")[:16]) replyBlock, secrets, tag = _buildReplyBlockImpl(path, exitType, exitInfo, expiryTime, prng, seed) STATUS.log("GENERATED_SURB", formatBase64(tag)) return replyBlock
def buildForwardPacket(payload, exitType, exitInfo, path1, path2, paddingPRNG=None, suppressTag=0): """Construct a forward message. payload: The payload to deliver. Must be exactly 28K. If the payload is None, 28K of random data is sent. 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 paddingPRNG: random number generator used to generate padding. If None, a new PRNG is initialized. suppressTag: if true, do not include a decodind handle in the routingInfo for this packet. Neither path1 nor path2 may be empty. If one is, MixError is raised. """ if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() if not path1: raise MixError("First leg of path is empty") if not path2: raise MixError("Second leg of path is empty") assert len(payload) == PAYLOAD_LEN LOG.trace(" Building packet with path %s:%s; delivering to %04x:%r", ",".join([s.getNickname() for s in path1]), ",".join([s.getNickname() for s in path2]), exitType, exitInfo) # Choose a random decoding tag. if not suppressTag: tag = _getRandomTag(paddingPRNG) exitInfo = tag + exitInfo return _buildPacket(payload, exitType, exitInfo, path1, path2, paddingPRNG,suppressTag=suppressTag)
def _buildPacket(payload, exitType, exitInfo, path1, path2, paddingPRNG=None, paranoia=0, suppressTag=0): """Helper method to create a message. The following fields must be set: payload: the intended exit payload. Must be 28K. (exitType, exitInfo): the routing type and info for the final node. (Ignored for reply messages; 'exitInfo' should include the 20-byte decoding tag.) path1: a sequence of ServerInfo objects, one for each node on the first leg of the path. path2: EITHER a sequence of ServerInfo objects, one for each node on the second leg of the path. OR a ReplyBlock object. The following fields are optional: paddingPRNG: A pseudo-random number generator used to pad the headers. If not provided, we use a counter-mode AES stream seeded from our entropy source. paranoia: If this is false, we use the padding PRNG to generate header secrets too. Otherwise, we read all of our header secrets from the true entropy source. """ assert len(payload) == PAYLOAD_LEN reply = None if isinstance(path2, ReplyBlock): reply = path2 path2 = None else: if len(exitInfo) < TAG_LEN and not suppressTag: raise MixError("Implausibly short exit info: %r" % exitInfo) if exitType < MIN_EXIT_TYPE and exitType != DROP_TYPE: raise MixError("Invalid exit type: %4x" % exitType) checkPathLength(path1, path2, exitType, exitInfo, explicitSwap=(reply is None), suppressTag=suppressTag) ### SETUP CODE: let's handle all the variant cases. # Set up the random number generators. if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() if paranoia: nHops = len(path1) if path2: nHops += len(path2) secretRNG = Crypto.getTrueRNG() else: secretRNG = paddingPRNG # Determine exit routing for path1. if reply: path1exittype = reply.routingType path1exitinfo = reply.routingInfo else: path1exittype, path1exitinfo = path1[-1].getRoutingFor(path2[0], swap=1) # Generate secrets for path1. secrets1 = [secretRNG.getBytes(SECRET_LEN) for _ in path1] if path2: # Make secrets for header 2, and construct header 2. We do this before # making header1 so that our rng won't be used for padding yet. secrets2 = [secretRNG.getBytes(SECRET_LEN) for _ in range(len(path2))] header2 = _buildHeader(path2, secrets2, exitType, exitInfo, paddingPRNG) else: secrets2 = None header2 = reply.header # Construct header1. header1 = _buildHeader(path1, secrets1, path1exittype, path1exitinfo, paddingPRNG) return _constructMessage(secrets1, secrets2, header1, header2, payload)
def buildRandomPayload(paddingPRNG=None): """Return a new random payload, suitable for use in a DROP packet.""" if not paddingPRNG: paddingPRNG = Crypto.getCommonPRNG() return paddingPRNG.getBytes(PAYLOAD_LEN)
def _buildPacket(payload, exitType, exitInfo, path1, path2, paddingPRNG=None, paranoia=0, suppressTag=0): """Helper method to create a message. The following fields must be set: payload: the intended exit payload. Must be 28K. (exitType, exitInfo): the routing type and info for the final node. (Ignored for reply messages; 'exitInfo' should include the 20-byte decoding tag.) path1: a sequence of ServerInfo objects, one for each node on the first leg of the path. path2: EITHER a sequence of ServerInfo objects, one for each node on the second leg of the path. OR a ReplyBlock object. The following fields are optional: paddingPRNG: A pseudo-random number generator used to pad the headers. If not provided, we use a counter-mode AES stream seeded from our entropy source. paranoia: If this is false, we use the padding PRNG to generate header secrets too. Otherwise, we read all of our header secrets from the true entropy source. """ assert len(payload) == PAYLOAD_LEN reply = None if isinstance(path2, ReplyBlock): reply = path2 path2 = None else: if len(exitInfo) < TAG_LEN and not suppressTag: raise MixError("Implausibly short exit info: %r"%exitInfo) if exitType < MIN_EXIT_TYPE and exitType != DROP_TYPE: raise MixError("Invalid exit type: %4x"%exitType) checkPathLength(path1, path2, exitType, exitInfo, explicitSwap=(reply is None), suppressTag=suppressTag) ### SETUP CODE: let's handle all the variant cases. # Set up the random number generators. if paddingPRNG is None: paddingPRNG = Crypto.getCommonPRNG() if paranoia: nHops = len(path1) if path2: nHops += len(path2) secretRNG = Crypto.getTrueRNG() else: secretRNG = paddingPRNG # Determine exit routing for path1. if reply: path1exittype = reply.routingType path1exitinfo = reply.routingInfo else: path1exittype, path1exitinfo = path1[-1].getRoutingFor(path2[0],swap=1) # Generate secrets for path1. secrets1 = [ secretRNG.getBytes(SECRET_LEN) for _ in path1 ] if path2: # Make secrets for header 2, and construct header 2. We do this before # making header1 so that our rng won't be used for padding yet. secrets2 = [ secretRNG.getBytes(SECRET_LEN) for _ in range(len(path2))] header2 = _buildHeader(path2,secrets2,exitType,exitInfo,paddingPRNG) else: secrets2 = None header2 = reply.header # Construct header1. header1 = _buildHeader(path1,secrets1,path1exittype,path1exitinfo, paddingPRNG) return _constructMessage(secrets1, secrets2, header1, header2, payload)