def _decodeEncryptedForwardPayload(payload, tag, key): """Helper function: decode an encrypted forward payload. Return values are the same as decodePayload. payload: the payload to decode tag: the decoding tag key: the RSA key of the payload's recipient.""" assert len(tag) == TAG_LEN assert len(payload) == PAYLOAD_LEN # Given an N-byte RSA key, the first N bytes of tag+payload will be # encrypted with RSA, and the rest with a lioness key given in the # first N. Try decrypting... msg = tag + payload try: rsaPart = Crypto.pk_decrypt(msg[: key.get_modulus_bytes()], key) except Crypto.CryptoError: return None rest = msg[key.get_modulus_bytes() :] k = Crypto.Keyset(rsaPart[:SECRET_LEN]).getLionessKeys(Crypto.END_TO_END_ENCRYPT_MODE) rest = rsaPart[SECRET_LEN:] + Crypto.lioness_decrypt(rest, k) # ... and then, check the checksum and continue. if not _checkPayload(rest): raise MixError("Invalid checksum on encrypted forward payload") return parsePayload(rest)
def _decodeEncryptedForwardPayload(payload, tag, key): """Helper function: decode an encrypted forward payload. Return values are the same as decodePayload. payload: the payload to decode tag: the decoding tag key: the RSA key of the payload's recipient.""" assert len(tag) == TAG_LEN assert len(payload) == PAYLOAD_LEN # Given an N-byte RSA key, the first N bytes of tag+payload will be # encrypted with RSA, and the rest with a lioness key given in the # first N. Try decrypting... msg = tag+payload try: rsaPart = Crypto.pk_decrypt(msg[:key.get_modulus_bytes()], key) except Crypto.CryptoError: return None rest = msg[key.get_modulus_bytes():] k = Crypto.Keyset(rsaPart[:SECRET_LEN]).getLionessKeys( Crypto.END_TO_END_ENCRYPT_MODE) rest = rsaPart[SECRET_LEN:] + Crypto.lioness_decrypt(rest, k) # ... and then, check the checksum and continue. if not _checkPayload(rest): raise MixError("Invalid checksum on encrypted forward payload") return parsePayload(rest)
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 processPacket(self, msg): """Given a 32K mixminion packet, processes it completely. Return one of: None [if the packet should be dropped.] a DeliveryPacket object a RelayedPacket object May raise CryptoError, ParseError, or ContentError if the packet is malformatted, misencrypted, unparseable, repeated, or otherwise unhandleable. WARNING: This implementation does nothing to prevent timing attacks: dropped packets, packets with bad digests, replayed packets, and exit packets are all processed faster than forwarded packets. You must prevent timing attacks elsewhere.""" # Break into headers and payload pkt = Packet.parsePacket(msg) header1 = Packet.parseHeader(pkt.header1) encSubh = header1[:Packet.ENC_SUBHEADER_LEN] header1 = header1[Packet.ENC_SUBHEADER_LEN:] assert len(header1) == Packet.HEADER_LEN - Packet.ENC_SUBHEADER_LEN assert len(header1) == (128*16) - 256 == 1792 # Try to decrypt the first subheader. Try each private key in # order. Only fail if all private keys fail. subh = None e = None self.lock.acquire() try: for pk, hashlog in self.privatekeys: try: subh = Crypto.pk_decrypt(encSubh, pk) break except Crypto.CryptoError, err: e = err finally: self.lock.release() if not subh: # Nobody managed to get us the first subheader. Raise the # most-recently-received error. raise e if len(subh) != Packet.MAX_SUBHEADER_LEN: raise ContentError("Bad length in RSA-encrypted part of subheader") subh = Packet.parseSubheader(subh) #may raise ParseError # Check the version: can we read it? if subh.major != Packet.MAJOR_NO or subh.minor != Packet.MINOR_NO: raise ContentError("Invalid protocol version") # Check the digest of all of header1 but the first subheader. if subh.digest != Crypto.sha1(header1): raise ContentError("Invalid digest") # Get ready to generate packet keys. keys = Crypto.Keyset(subh.secret) # Replay prevention replayhash = keys.get(Crypto.REPLAY_PREVENTION_MODE, Crypto.DIGEST_LEN) if hashlog.seenHash(replayhash): raise ContentError("Duplicate packet detected.") else: hashlog.logHash(replayhash) # If we're meant to drop, drop now. rt = subh.routingtype if rt == Packet.DROP_TYPE: return None # Prepare the key to decrypt the header in counter mode. We'll be # using this more than once. header_sec_key = Crypto.aes_key(keys.get(Crypto.HEADER_SECRET_MODE)) # Prepare key to generate padding junk_key = Crypto.aes_key(keys.get(Crypto.RANDOM_JUNK_MODE)) # Pad the rest of header 1 header1 += Crypto.prng(junk_key, Packet.OAEP_OVERHEAD + Packet.MIN_SUBHEADER_LEN + subh.routinglen) assert len(header1) == (Packet.HEADER_LEN - Packet.ENC_SUBHEADER_LEN + Packet.OAEP_OVERHEAD+Packet.MIN_SUBHEADER_LEN + subh.routinglen) assert len(header1) == 1792 + 42 + 42 + subh.routinglen == \ 1876 + subh.routinglen # Decrypt the rest of header 1, encrypting the padding. header1 = Crypto.ctr_crypt(header1, header_sec_key) # If the subheader says that we have extra routing info that didn't # fit in the RSA-encrypted part, get it now. overflowLength = subh.getOverflowLength() if overflowLength: subh.appendOverflow(header1[:overflowLength]) header1 = header1[overflowLength:] assert len(header1) == ( 1876 + subh.routinglen - max(0,subh.routinglen-Packet.MAX_ROUTING_INFO_LEN)) header1 = subh.underflow + header1 assert len(header1) == Packet.HEADER_LEN # Decrypt the payload. payload = Crypto.lioness_decrypt(pkt.payload, keys.getLionessKeys(Crypto.PAYLOAD_ENCRYPT_MODE)) # If we're an exit node, there's no need to process the headers # further. if rt >= Packet.MIN_EXIT_TYPE: return DeliveryPacket(rt, subh.getExitAddress(0), keys.get(Crypto.APPLICATION_KEY_MODE), payload) # If we're not an exit node, make sure that what we recognize our # routing type. if rt not in (Packet.SWAP_FWD_IPV4_TYPE, Packet.FWD_IPV4_TYPE, Packet.SWAP_FWD_HOST_TYPE, Packet.FWD_HOST_TYPE): raise ContentError("Unrecognized Mixminion routing type") # Decrypt header 2. header2 = Crypto.lioness_decrypt(pkt.header2, keys.getLionessKeys(Crypto.HEADER_ENCRYPT_MODE)) # If we're the swap node, (1) decrypt the payload with a hash of # header2... (2) decrypt header2 with a hash of the payload... # (3) and swap the headers. if Packet.typeIsSwap(rt): hkey = Crypto.lioness_keys_from_header(header2) payload = Crypto.lioness_decrypt(payload, hkey) hkey = Crypto.lioness_keys_from_payload(payload) header2 = Crypto.lioness_decrypt(header2, hkey) header1, header2 = header2, header1 # Build the address object for the next hop address = Packet.parseRelayInfoByType(rt, subh.routinginfo) # Construct the packet for the next hop. pkt = Packet.Packet(header1, header2, payload).pack() return RelayedPacket(address, pkt)