示例#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 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]
示例#3
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
示例#4
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
示例#5
0
文件: util.py 项目: nask0/opparis
def isValidHMAC( hmac1, hmac2, key ):
	"""
	Compares `hmac1' and `hmac2' after HMACing them again using `key'.

	The arguments `hmac1' and `hmac2' are compared.  If they are equal, `True'
	is returned and otherwise `False'.  To prevent timing attacks, double HMAC
	verification is used meaning that the two arguments are HMACed again before
	(variable-time) string comparison.  The idea is taken from:
	https://www.isecpartners.com/blog/2011/february/double-hmac-verification.aspx
	"""

	assert len(hmac1) == len(hmac2)

	# HMAC the arguments again to prevent timing attacks.
	doubleHmac1 = mycrypto.HMAC_SHA256_128(key, hmac1)
	doubleHmac2 = mycrypto.HMAC_SHA256_128(key, hmac2)

	if doubleHmac1 != doubleHmac2:
		return False

	log.debug("The computed HMAC is valid.")

	return True
示例#6
0
    def extract(self, data, aes, hmacKey):
        """
        Extracts (i.e., decrypts and authenticates) protocol messages.

        The raw `data' coming directly from the wire is decrypted using `aes'
        and authenticated using `hmacKey'.  The payload is then returned as
        unencrypted protocol messages.  In case of invalid headers or HMACs, an
        exception is raised.
        """

        self.recvBuf += data
        msgs = []

        # Keep trying to unpack as long as there is at least a header.
        while len(self.recvBuf) >= const.HDR_LENGTH:

            # If necessary, extract the header fields.
            if self.totalLen is None and self.payloadLen is None and self.flags is None:
                self.totalLen = pack.ntohs(aes.decrypt(self.recvBuf[16:18]))
                self.payloadLen = pack.ntohs(aes.decrypt(self.recvBuf[18:20]))
                self.flags = ord(aes.decrypt(self.recvBuf[20]))

                if not isSane(self.totalLen, self.payloadLen, self.flags):
                    raise base.PluggableTransportError("Invalid header.")

            # Parts of the message are still on the wire; waiting.
            if (len(self.recvBuf) - const.HDR_LENGTH) < self.totalLen:
                break

            rcvdHMAC = self.recvBuf[0:const.HMAC_SHA256_128_LENGTH]
            vrfyHMAC = mycrypto.HMAC_SHA256_128(
                hmacKey,
                self.recvBuf[const.HMAC_SHA256_128_LENGTH:(self.totalLen +
                                                           const.HDR_LENGTH)])

            if rcvdHMAC != vrfyHMAC:
                raise base.PluggableTransportError("Invalid message HMAC.")

            # Decrypt the message and remove it from the input buffer.
            extracted = aes.decrypt(self.recvBuf[const.HDR_LENGTH:(
                self.totalLen + const.HDR_LENGTH)])[:self.payloadLen]
            msgs.append(ProtocolMessage(payload=extracted, flags=self.flags))
            self.recvBuf = self.recvBuf[const.HDR_LENGTH + self.totalLen:]

            # Protocol message processed; now reset length fields.
            self.totalLen = self.payloadLen = self.flags = None

        return msgs
示例#7
0
    def encryptAndHMAC(self, crypter, hmacKey):
        """
        Encrypt and authenticate this protocol message.

        This protocol message is encrypted using `crypter' and authenticated
        using `hmacKey'.  Finally, the encrypted message prepended by a
        HMAC-SHA256-128 is returned and ready to be sent over the wire.
        """

        encrypted = crypter.encrypt(
            pack.htons(self.totalLen) + pack.htons(self.payloadLen) +
            chr(self.flags) + self.payload +
            (self.totalLen - self.payloadLen) * '\0')

        hmac = mycrypto.HMAC_SHA256_128(hmacKey, encrypted)

        return hmac + encrypted
示例#8
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]
        authenticated = False
        for epoch in util.expandedEpoch():
            myHMAC = mycrypto.HMAC_SHA256_128(self.recvHMAC,
                                              potentialTicket[0:index + \
                                              const.MARK_LENGTH] + epoch)

            if util.isValidHMAC(myHMAC, existingHMAC, self.recvHMAC):
                authenticated = True
                break

            log.debug("HMAC invalid.  Trying next epoch value.")

        if not authenticated:
            log.warning("Could not verify the authentication message's HMAC.")
            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
示例#9
0
    def test6_HMAC_SHA256_128( self ):
        self.assertRaises(AssertionError, mycrypto.HMAC_SHA256_128,
                          "x" * (const.SHARED_SECRET_LENGTH - 1), "test")

        self.failUnless(len(mycrypto.HMAC_SHA256_128("x" * \
                        const.SHARED_SECRET_LENGTH, "test")) == 16)