def decode(self): """Helper method: Determines this message's type and contents.""" if self.payload is None: return message = self.payload self.contents = None try: self.dPayload = mixminion.BuildMessage.decodePayload(message, "") if self.dPayload is None: # encrypted message self.type = 'enc' self.contents = message self.headers = {} elif self.dPayload.isSingleton(): # forward message, singleton. self.type = 'plain' body = self.dPayload.getUncompressedContents() self.contents, self.headers = \ Packet.parseMessageAndHeaders(body) else: # forward message, fragment. self.isfrag = 1 self.type = 'plain' self.contents = message self.headers = {} except Packet.CompressedDataTooLong, _: self.contents = Packet.parsePayload(message).getContents() self.type = 'long' self.headers = {}
def setTagged(self,tagged=1): """Re-frame the routingInfo in this packet. If 'tagged' is true, then the routingInfo starts with TAG_LEN bytes of decoding handle, and the rest is address. If 'tagged' is false, then it's all address. """ x = self.tag+self.address if tagged: if len(x)<Packet.TAG_LEN: raise Packet.ParseError("Missing decoding handle for exit type") self.tag = x[:Packet.TAG_LEN] self.address = x[Packet.TAG_LEN:] else: self.tag = "" self.address = x
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 getTextEncodedMessage(self): """Return a Packet.TextEncodedMessage object for this packet.""" tag = None if self.isOvercompressed(): tp = 'LONG' elif self.isEncrypted(): tp = 'ENC' tag = self.tag elif self.isPrintingAscii(): assert self.isPlaintext() tp = 'TXT' elif self.isFragment(): assert self.isPlaintext() tp = 'FRAG' else: assert self.isPlaintext() tp = 'BIN' return Packet.TextEncodedMessage(self.contents, tp, tag)
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)