Пример #1
0
    def createHandshake( self ):
        """
        Create and return a ready-to-be-sent UniformDH handshake.

        The returned handshake data includes the public key, pseudo-random
        padding, the mark and the HMAC.  If a UniformDH object has not been
        initialised yet, a new instance is created.
        """

        assert self.sharedSecret is not None

        log.debug("Creating UniformDH handshake message.")

        if self.udh is None:
            self.udh = obfs3_dh.UniformDH()
        publicKey = self.udh.get_public()

        assert (const.MAX_PADDING_LENGTH - const.PUBLIC_KEY_LENGTH) >= 0

        # Subtract the length of the public key to make the handshake on
        # average as long as a redeemed ticket.  That should thwart statistical
        # length-based attacks.
        padding = mycrypto.strongRandom(random.randint(0,
                                        const.MAX_PADDING_LENGTH -
                                        const.PUBLIC_KEY_LENGTH))

        # Add a mark which enables efficient location of the HMAC.
        mark = mycrypto.HMAC_SHA256_128(self.sharedSecret, publicKey)

        # Authenticate the handshake including the current approximate epoch.
        mac = mycrypto.HMAC_SHA256_128(self.sharedSecret,
                                       publicKey + padding + mark +
                                       util.getEpoch())

        return publicKey + padding + mark + mac
Пример #2
0
    def createHandshake(self):
        """
        Create and return a ready-to-be-sent UniformDH handshake.

        The returned handshake data includes the public key, pseudo-random
        padding, the mark and the HMAC.  If a UniformDH object has not been
        initialised yet, a new instance is created.
        """

        assert self.sharedSecret is not None

        log.debug("Creating UniformDH handshake message.")

        if self.udh is None:
            self.udh = obfs3_dh.UniformDH()
        publicKey = self.udh.get_public()

        assert (const.MAX_PADDING_LENGTH - const.PUBLIC_KEY_LENGTH) >= 0

        # Subtract the length of the public key to make the handshake on
        # average as long as a redeemed ticket.  That should thwart statistical
        # length-based attacks.
        padding = mycrypto.strongRandom(
            random.randint(0,
                           const.MAX_PADDING_LENGTH - const.PUBLIC_KEY_LENGTH))

        # Add a mark which enables efficient location of the HMAC.
        mark = mycrypto.HMAC_SHA256_128(self.sharedSecret, publicKey)

        # Authenticate the handshake including the current approximate epoch.
        mac = mycrypto.HMAC_SHA256_128(
            self.sharedSecret, publicKey + padding + mark + util.getEpoch())

        return publicKey + padding + mark + mac
Пример #3
0
    def extractPublicKey( self, data, srvState=None ):
        """
        Extract and return a UniformDH public key out of `data'.

        Before the public key is touched, the HMAC is verified.  If the HMAC is
        invalid or some other error occurs, `False' is returned.  Otherwise,
        the public key is returned.  The extracted data is finally drained from
        the given `data' object.
        """

        assert self.sharedSecret is not None

        # Do we already have the minimum amount of data?
        if len(data) < (const.PUBLIC_KEY_LENGTH + const.MARK_LENGTH +
                        const.HMAC_SHA256_128_LENGTH):
            return False

        log.debug("Attempting to extract the remote machine's UniformDH "
                  "public key out of %d bytes of data." % len(data))

        handshake = data.peek()

        # First, find the mark to efficiently locate the HMAC.
        publicKey = handshake[:const.PUBLIC_KEY_LENGTH]
        mark = mycrypto.HMAC_SHA256_128(self.sharedSecret, publicKey)

        index = util.locateMark(mark, handshake)
        if not index:
            return False

        # Now that we know where the authenticating HMAC is: verify it.
        hmacStart = index + const.MARK_LENGTH
        existingHMAC = handshake[hmacStart:
                                 (hmacStart + const.HMAC_SHA256_128_LENGTH)]
        myHMAC = mycrypto.HMAC_SHA256_128(self.sharedSecret,
                                          handshake[0 : hmacStart] +
                                          util.getEpoch())

        if not util.isValidHMAC(myHMAC, existingHMAC, self.sharedSecret):
            log.warning("The HMAC is invalid: `%s' vs. `%s'." %
                        (myHMAC.encode('hex'), existingHMAC.encode('hex')))
            return False

        # Do nothing if the ticket is replayed.  Immediately closing the
        # connection would be suspicious.
        if srvState is not None and srvState.isReplayed(existingHMAC):
            log.warning("The HMAC was already present in the replay table.")
            return False

        data.drain(index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH)

        if srvState is not None:
            log.debug("Adding the HMAC authenticating the UniformDH message " \
                      "to the replay table: %s." % existingHMAC.encode('hex'))
            srvState.registerKey(existingHMAC)

        return handshake[:const.PUBLIC_KEY_LENGTH]
Пример #4
0
    def extractPublicKey(self, data, srvState=None):
        """
        Extract and return a UniformDH public key out of `data'.

        Before the public key is touched, the HMAC is verified.  If the HMAC is
        invalid or some other error occurs, `False' is returned.  Otherwise,
        the public key is returned.  The extracted data is finally drained from
        the given `data' object.
        """

        assert self.sharedSecret is not None

        # Do we already have the minimum amount of data?
        if len(data) < (const.PUBLIC_KEY_LENGTH + const.MARK_LENGTH +
                        const.HMAC_SHA256_128_LENGTH):
            return False

        log.debug("Attempting to extract the remote machine's UniformDH "
                  "public key out of %d bytes of data." % len(data))

        handshake = data.peek()

        # First, find the mark to efficiently locate the HMAC.
        publicKey = handshake[:const.PUBLIC_KEY_LENGTH]
        mark = mycrypto.HMAC_SHA256_128(self.sharedSecret, publicKey)

        index = util.locateMark(mark, handshake)
        if not index:
            return False

        # Now that we know where the authenticating HMAC is: verify it.
        hmacStart = index + const.MARK_LENGTH
        existingHMAC = handshake[hmacStart:(hmacStart +
                                            const.HMAC_SHA256_128_LENGTH)]
        myHMAC = mycrypto.HMAC_SHA256_128(
            self.sharedSecret, handshake[0:hmacStart] + util.getEpoch())

        if not util.isValidHMAC(myHMAC, existingHMAC, self.sharedSecret):
            log.warning("The HMAC is invalid: `%s' vs. `%s'." %
                        (myHMAC.encode('hex'), existingHMAC.encode('hex')))
            return False

        # Do nothing if the ticket is replayed.  Immediately closing the
        # connection would be suspicious.
        if srvState is not None and srvState.isReplayed(existingHMAC):
            log.warning("The HMAC was already present in the replay table.")
            return False

        data.drain(index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH)

        if srvState is not None:
            log.debug("Adding the HMAC authenticating the UniformDH message " \
                      "to the replay table: %s." % existingHMAC.encode('hex'))
            srvState.registerKey(existingHMAC)

        return handshake[:const.PUBLIC_KEY_LENGTH]
Пример #5
0
    def createHandshake(self, srvState=None):
        """
        Create and return a ready-to-be-sent UniformDH handshake.

        The returned handshake data includes the public key, pseudo-random
        padding, the mark and the HMAC.  If a UniformDH object has not been
        initialised yet, a new instance is created.
        """

        assert self.sharedSecret is not None

        log.debug("Creating UniformDH handshake message.")

        if self.udh is None:
            self.udh = obfs3_dh.UniformDH()
        publicKey = self.udh.get_public()

        assert (const.MAX_PADDING_LENGTH - const.PUBLIC_KEY_LENGTH) >= 0

        # Subtract the length of the public key to make the handshake on
        # average as long as a redeemed ticket.  That should thwart statistical
        # length-based attacks.
        padding = mycrypto.strongRandom(
            random.randint(0,
                           const.MAX_PADDING_LENGTH - const.PUBLIC_KEY_LENGTH))

        # Add a mark which enables efficient location of the HMAC.
        mark = mycrypto.HMAC_SHA256_128(self.sharedSecret, publicKey)

        if self.echoEpoch is None:
            epoch = util.getEpoch()
        else:
            epoch = self.echoEpoch
            log.debug("Echoing epoch rather than recreating it.")

        # Authenticate the handshake including the current approximate epoch.
        mac = mycrypto.HMAC_SHA256_128(self.sharedSecret,
                                       publicKey + padding + mark + epoch)

        if self.weAreServer and (srvState is not None):
            log.debug("Adding the HMAC authenticating the server's UniformDH "
                      "message to the replay table: %s." % mac.encode('hex'))
            srvState.registerKey(mac)

        return publicKey + padding + mark + mac
Пример #6
0
    def createHandshake( self, srvState=None ):
        """
        Create and return a ready-to-be-sent UniformDH handshake.

        The returned handshake data includes the public key, pseudo-random
        padding, the mark and the HMAC.  If a UniformDH object has not been
        initialised yet, a new instance is created.
        """

        assert self.sharedSecret is not None

        log.debug("Creating UniformDH handshake message.")

        if self.udh is None:
            self.udh = obfs3_dh.UniformDH()
        publicKey = self.udh.get_public()

        assert (const.MAX_PADDING_LENGTH - const.PUBLIC_KEY_LENGTH) >= 0

        # Subtract the length of the public key to make the handshake on
        # average as long as a redeemed ticket.  That should thwart statistical
        # length-based attacks.
        padding = mycrypto.strongRandom(random.randint(0,
                                        const.MAX_PADDING_LENGTH -
                                        const.PUBLIC_KEY_LENGTH))

        # Add a mark which enables efficient location of the HMAC.
        mark = mycrypto.HMAC_SHA256_128(self.sharedSecret, publicKey)

        if self.echoEpoch is None:
            epoch = util.getEpoch()
        else:
            epoch = self.echoEpoch
            log.debug("Echoing epoch rather than recreating it.")

        # Authenticate the handshake including the current approximate epoch.
        mac = mycrypto.HMAC_SHA256_128(self.sharedSecret,
                                       publicKey + padding + mark + epoch)

        if self.weAreServer and (srvState is not None):
            log.debug("Adding the HMAC authenticating the server's UniformDH "
                      "message to the replay table: %s." % mac.encode('hex'))
            srvState.registerKey(mac)

        return publicKey + padding + mark + mac
Пример #7
0
def createTicketMessage(rawTicket, HMACKey):
    """
    Create and return a ready-to-be-sent ticket authentication message.

    Pseudo-random padding and a mark are added to `rawTicket' and the result is
    then authenticated using `HMACKey' as key for a HMAC.  The resulting
    authentication message is then returned.
    """

    assert len(rawTicket) == const.TICKET_LENGTH
    assert len(HMACKey) == const.TICKET_HMAC_KEY_LENGTH

    # Subtract the length of the ticket to make the handshake on
    # average as long as a UniformDH handshake message.
    padding = mycrypto.strongRandom(
        random.randint(0, const.MAX_PADDING_LENGTH - const.TICKET_LENGTH))

    mark = mycrypto.HMAC_SHA256_128(HMACKey, rawTicket)
    hmac = mycrypto.HMAC_SHA256_128(
        HMACKey, rawTicket + padding + mark + util.getEpoch())

    return rawTicket + padding + mark + hmac
Пример #8
0
def createTicketMessage( rawTicket, HMACKey ):
    """
    Create and return a ready-to-be-sent ticket authentication message.

    Pseudo-random padding and a mark are added to `rawTicket' and the result is
    then authenticated using `HMACKey' as key for a HMAC.  The resulting
    authentication message is then returned.
    """

    assert len(rawTicket) == const.TICKET_LENGTH
    assert len(HMACKey) == const.TICKET_HMAC_KEY_LENGTH

    # Subtract the length of the ticket to make the handshake on
    # average as long as a UniformDH handshake message.
    padding = mycrypto.strongRandom(random.randint(0,
                                    const.MAX_PADDING_LENGTH -
                                    const.TICKET_LENGTH))

    mark = mycrypto.HMAC_SHA256_128(HMACKey, rawTicket)

    hmac = mycrypto.HMAC_SHA256_128(HMACKey, rawTicket + padding +
                                    mark + util.getEpoch())

    return rawTicket + padding + mark + hmac
Пример #9
0
    def receiveTicket( self, data ):
        """
        Extract and verify a potential session ticket.

        The given `data' is treated as a session ticket.  The ticket is being
        decrypted and authenticated (yes, in that order).  If all these steps
        succeed, `True' is returned.  Otherwise, `False' is returned.
        """

        if len(data) < (const.TICKET_LENGTH + const.MARK_LENGTH +
                        const.HMAC_SHA256_128_LENGTH):
            return False

        potentialTicket = data.peek()

        # Now try to decrypt and parse the ticket.  We need the master key
        # inside to verify the HMAC in the next step.
        if not self.decryptedTicket:
            newTicket = ticket.decrypt(potentialTicket[:const.TICKET_LENGTH],
                                       self.srvState)
            if newTicket != None and newTicket.isValid():
                self.deriveSecrets(newTicket.masterKey)
                self.decryptedTicket = True
            else:
                return False

        # First, find the mark to efficiently locate the HMAC.
        mark = mycrypto.HMAC_SHA256_128(self.recvHMAC,
                                        potentialTicket[:const.TICKET_LENGTH])

        index = util.locateMark(mark, potentialTicket)
        if not index:
            return False

        # Now, verify if the HMAC is valid.
        existingHMAC = potentialTicket[index + const.MARK_LENGTH:
                                       index + const.MARK_LENGTH +
                                       const.HMAC_SHA256_128_LENGTH]
        myHMAC = mycrypto.HMAC_SHA256_128(self.recvHMAC,
                                          potentialTicket[0:
                                          index + const.MARK_LENGTH] +
                                          util.getEpoch())

        if not util.isValidHMAC(myHMAC, existingHMAC, self.recvHMAC):
            log.warning("The HMAC is invalid: `%s' vs. `%s'." %
                        (myHMAC.encode('hex'), existingHMAC.encode('hex')))
            return False

        # Do nothing if the ticket is replayed.  Immediately closing the
        # connection would be suspicious.
        if self.srvState.isReplayed(existingHMAC):
            log.warning("The HMAC was already present in the replay table.")
            return False

        data.drain(index + const.MARK_LENGTH + const.HMAC_SHA256_128_LENGTH)

        log.debug("Adding the HMAC authenticating the ticket message to the " \
                  "replay table: %s." % existingHMAC.encode('hex'))
        self.srvState.registerKey(existingHMAC)

        log.debug("Switching to state ST_CONNECTED.")
        self.protoState = const.ST_CONNECTED

        return True
Пример #10
0
 def test5_getEpoch( self ):
     e = util.getEpoch()
     self.failUnless(isinstance(e, basestring))