Example #1
0
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()
Example #2
0
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()
Example #3
0
    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)
Example #4
0
    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)