Ejemplo n.º 1
0
    def checkOtp(self, otpVal, counter=None, window=None, options=None):
        """
        checkOtp - validate the token otp against a given otpvalue

        :param otpVal: the to be verified otpvalue
        :type otpVal:  string

        :param counter: the counter state. It is not used by the YubiKey because the current counter value
        is sent encrypted inside the OTP value
        :type counter: int

        :param window: the counter +window, which is not used in the YubiKey because the current
        counter value is sent encrypted inside the OTP, allowing a simple comparison between the encrypted
        counter value and the stored counter value
        :type window: int

        :param options: the dict, which could contain token specific info
        :type options: dict

        :return: the counter state or an error code (< 0):
        -1 if the OTP is old (counter < stored counter)
        -2 if the private_uid sent in the OTP is wrong (different from the one stored with the token)
        -3 if the CRC verification fails
        :rtype: int

        From: http://www.yubico.com/wp-content/uploads/2013/04/YubiKey-Manual-v3_1.pdf
                    6 Implementation details

        """
        res = -1

        if len(otpVal) < self.getOtpLen():
            return res

        serial = self.token.getSerial()
        secObj = self._get_secret_object()

        anOtpVal = otpVal.lower()

        # The prefix is the characters in front of the last 32 chars
        # We can also check the PREFIX! At the moment, we do not use it!
        yubi_prefix = anOtpVal[:-32]

        # verify the prefix if any
        enroll_prefix = self.getFromTokenInfo('public_uid', None)
        if enroll_prefix and enroll_prefix != yubi_prefix:
            return res

        # The variable otp val is the last 32 chars
        yubi_otp = anOtpVal[-32:]

        try:
            otp_bin = modhex_decode(yubi_otp)
            msg_bin = secObj.aes_decrypt(otp_bin)
        except KeyError:
            log.warning("failed to decode yubi_otp!")
            return res

        msg_hex = binascii.hexlify(msg_bin)

        uid = msg_hex[0:12]
        log.debug("[checkOtp] uid: %r" % uid)
        log.debug("[checkOtp] prefix: %r" %
                  binascii.hexlify(modhex_decode(yubi_prefix)))

        # usage_counter can go from 1 – 0x7fff
        usage_counter = msg_hex[12:16]

        # TODO: We also could check the timestamp
        # - the timestamp. see http://www.yubico.com/wp-content/uploads/2013/04/YubiKey-Manual-v3_1.pdf
        timestamp = msg_hex[16:22]

        # session counter can go from 00 to 0xff
        session_counter = msg_hex[22:24]
        random = msg_hex[24:28]

        log.debug("[checkOtp] decrypted: usage_count: %r, session_count: %r" %
                  (usage_counter, session_counter))

        # The checksum is a CRC-16 (16-bit ISO 13239 1st complement) that
        # occupies the last 2 bytes of the decrypted OTP value. Calculating the
        # CRC-16 checksum of the whole decrypted OTP should give a fixed residual
        # of 0xf0b8 (see Yubikey-Manual - Chapter 6: Implementation details).
        crc = msg_hex[28:]
        log.debug("[checkOtp] calculated checksum (61624): %r" %
                  checksum(msg_hex))
        if checksum(msg_hex) != 0xf0b8:
            log.warning("[checkOtp] CRC checksum for token %r failed" % serial)
            return -3

        # create the counter as integer
        # Note: The usage counter is stored LSB!
        count_hex = usage_counter[2:4] + usage_counter[0:2] + session_counter
        count_int = int(count_hex, 16)
        log.debug('[checkOtp] decrypted counter: %r' % count_int)

        tokenid = self.getFromTokenInfo("yubikey.tokenid")
        if not tokenid:
            log.debug("[checkOtp] Got no tokenid for %r. Setting to %r." %
                      (serial, uid))
            tokenid = uid
            self.addToTokenInfo("yubikey.tokenid", tokenid)

        if tokenid != uid:
            # wrong token!
            log.warning(
                "[checkOtp] The wrong token was presented for %r. Got %r, expected %r."
                % (serial, uid, tokenid))
            return -2

        log.debug('[checkOtp] compare counter to LinOtpCount: %r' %
                  self.token.LinOtpCount)
        if count_int >= self.token.LinOtpCount:
            res = count_int

        return res
Ejemplo n.º 2
0
    def checkOtp(self, otpVal, counter=None, window=None, options=None):
        """
        checkOtp - validate the token otp against a given otpvalue

        :param otpVal: the to be verified otpvalue
        :type otpVal:  string

        :param counter: the counter state. It is not used by the YubiKey because the current counter value
        is sent encrypted inside the OTP value
        :type counter: int

        :param window: the counter +window, which is not used in the YubiKey because the current
        counter value is sent encrypted inside the OTP, allowing a simple comparison between the encrypted
        counter value and the stored counter value
        :type window: int

        :param options: the dict, which could contain token specific info
        :type options: dict

        :return: the counter state or an error code (< 0):
        -1 if the OTP is old (counter < stored counter)
        -2 if the private_uid sent in the OTP is wrong (different from the one stored with the token)
        -3 if the CRC verification fails
        :rtype: int

        From: http://www.yubico.com/wp-content/uploads/2013/04/YubiKey-Manual-v3_1.pdf
                    6 Implementation details

        """
        log.debug("[checkOtp] begin. Validate the token otp: otpVal: %r, counter: %r,  options: %r "
                  % (otpVal, counter, options))
        res = -1

        if len(otpVal) < self.getOtpLen():
            return res

        serial = self.token.getSerial()
        secObj = self._get_secret_object()

        anOtpVal = otpVal.lower()

        # The prefix is the characters in front of the last 32 chars
        # We can also check the PREFIX! At the moment, we do not use it!
        yubi_prefix = anOtpVal[:-32]

        # verify the prefix if any
        enroll_prefix = self.getFromTokenInfo('public_uid', None)
        if enroll_prefix and enroll_prefix != yubi_prefix:
            return res

        # The variable otp val is the last 32 chars
        yubi_otp = anOtpVal[-32:]

        try:
            otp_bin = modhex_decode(yubi_otp)
            msg_bin = secObj.aes_decrypt(otp_bin)
        except KeyError:
            log.warning("failed to decode yubi_otp!")
            return res

        msg_hex = binascii.hexlify(msg_bin)

        uid = msg_hex[0:12]
        log.debug("[checkOtp] uid: %r" % uid)
        log.debug("[checkOtp] prefix: %r" % binascii.hexlify(modhex_decode(yubi_prefix)))

        # usage_counter can go from 1 – 0x7fff
        usage_counter = msg_hex[12:16]

        # TODO: We also could check the timestamp
        # - the timestamp. see http://www.yubico.com/wp-content/uploads/2013/04/YubiKey-Manual-v3_1.pdf
        timestamp = msg_hex[16:22]


        # session counter can go from 00 to 0xff
        session_counter = msg_hex[22:24]
        random = msg_hex[24:28]

        log.debug("[checkOtp] decrypted: usage_count: %r, session_count: %r"
                  % (usage_counter, session_counter))

        # The checksum is a CRC-16 (16-bit ISO 13239 1st complement) that
        # occupies the last 2 bytes of the decrypted OTP value. Calculating the
        # CRC-16 checksum of the whole decrypted OTP should give a fixed residual
        # of 0xf0b8 (see Yubikey-Manual - Chapter 6: Implementation details).
        crc = msg_hex[28:]
        log.debug("[checkOtp] calculated checksum (61624): %r" % checksum(msg_hex))
        if checksum(msg_hex) != 0xf0b8:
            log.warning("[checkOtp] CRC checksum for token %r failed" % serial)
            return -3

        # create the counter as integer
        # Note: The usage counter is stored LSB!
        count_hex = usage_counter[2:4] + usage_counter[0:2] + session_counter
        count_int = int(count_hex, 16)
        log.debug('[checkOtp] decrypted counter: %r' % count_int)

        tokenid = self.getFromTokenInfo("yubikey.tokenid")
        if not tokenid:
            log.debug("[checkOtp] Got no tokenid for %r. Setting to %r." % (serial, uid))
            tokenid = uid
            self.addToTokenInfo("yubikey.tokenid", tokenid)

        if tokenid != uid:
            # wrong token!
            log.warning("[checkOtp] The wrong token was presented for %r. Got %r, expected %r."
                        % (serial, uid, tokenid))
            return -2


        log.debug('[checkOtp] compare counter to LinOtpCount: %r' % self.token.LinOtpCount)
        if count_int >= self.token.LinOtpCount:
            res = count_int

        return res