Пример #1
0
    def test_28_yubikey_utils(self):
        self.assertEquals(modhex_encode(b'\x47'), 'fi')
        self.assertEquals(modhex_encode(b'\xba\xad\xf0\x0d'), 'nlltvcct')
        self.assertEquals(modhex_encode(binascii.unhexlify('0123456789abcdef')),
                          'cbdefghijklnrtuv')
        self.assertEquals(modhex_encode('Hallo'), 'fjhbhrhrhv')
        # and the other way around
        self.assertEquals(modhex_decode('fi'), b'\x47')
        self.assertEquals(modhex_decode('nlltvcct'), b'\xba\xad\xf0\x0d')
        self.assertEquals(modhex_decode('cbdefghijklnrtuv'),
                          binascii.unhexlify('0123456789abcdef'))
        self.assertEquals(modhex_decode('fjhbhrhrhv'), b'Hallo')

        # now test the crc function
        self.assertEquals(checksum(b'\x01\x02\x03\x04'), 0xc66e)
        self.assertEquals(checksum(b'\x01\x02\x03\x04\x919'), 0xf0b8)
Пример #2
0
    def test_28_yubikey_utils(self):
        self.assertEquals(modhex_encode(b'\x47'), 'fi')
        self.assertEquals(modhex_encode(b'\xba\xad\xf0\x0d'), 'nlltvcct')
        self.assertEquals(modhex_encode(binascii.unhexlify('0123456789abcdef')),
                          'cbdefghijklnrtuv')
        self.assertEquals(modhex_encode('Hallo'), 'fjhbhrhrhv')
        # and the other way around
        self.assertEquals(modhex_decode('fi'), b'\x47')
        self.assertEquals(modhex_decode('nlltvcct'), b'\xba\xad\xf0\x0d')
        self.assertEquals(modhex_decode('cbdefghijklnrtuv'),
                          binascii.unhexlify('0123456789abcdef'))
        self.assertEquals(modhex_decode('fjhbhrhrhv'), b'Hallo')

        # now test the crc function
        self.assertEquals(checksum(b'\x01\x02\x03\x04'), 0xc66e)
        self.assertEquals(checksum(b'\x01\x02\x03\x04\x919'), 0xf0b8)
Пример #3
0
    def check_otp(self, anOtpVal, counter=None, window=None, options=None):
        """
        validate the token otp against a given otpvalue

        :param anOtpVal: the to be verified otpvalue
        :type anOtpVal:  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

        """
        res = -1

        serial = self.token.serial
        secret = self.token.get_otpkey()

        # The prefix is the characters in front of the last 32 chars
        yubi_prefix = anOtpVal[:-32]
        # The variable otp val is the last 32 chars
        yubi_otp = anOtpVal[-32:]

        try:
            otp_bin = modhex_decode(yubi_otp)
        except KeyError:
            # The OTP value is no yubikey aes otp value and can not be decoded
            return -4

        msg_bin = secret.aes_decrypt(otp_bin)
        msg_hex = binascii.hexlify(msg_bin)

        # 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).
        log.debug("calculated checksum (61624): {0!r}".format(
            checksum(msg_hex)))
        if checksum(msg_hex) != 0xf0b8:  # pragma: no cover
            log.warning("CRC checksum for token {0!r} failed".format(serial))
            return -3

        uid = msg_hex[0:12]
        log.debug("uid: {0!r}".format(uid))
        log.debug("prefix: {0!r}".format(
            binascii.hexlify(modhex_decode(yubi_prefix))))
        # usage_counter can go from 1 – 0x7fff
        usage_counter = msg_hex[12:16]
        timestamp = msg_hex[16:22]
        # session counter can go from 00 to 0xff
        session_counter = msg_hex[22:24]
        random = msg_hex[24:28]
        crc = msg_hex[28:]
        log.debug("decrypted: usage_count: {0!r}, session_count: {1!r}".format(
            usage_counter, session_counter))

        # 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('decrypted counter: {0!r}'.format(count_int))

        tokenid = self.get_tokeninfo("yubikey.tokenid")
        if not tokenid:
            log.debug("Got no tokenid for {0!r}. Setting to {1!r}.".format(
                serial, uid))
            tokenid = uid
            self.add_tokeninfo("yubikey.tokenid", tokenid)

        prefix = self.get_tokeninfo("yubikey.prefix")
        if not prefix:
            log.debug("Got no prefix for {0!r}. Setting to {1!r}.".format(
                serial, yubi_prefix))
            self.add_tokeninfo("yubikey.prefix", yubi_prefix)

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

        # TODO: We also could check the timestamp
        # see http://www.yubico.com/wp-content/uploads/2013/04/YubiKey-Manual-v3_1.pdf
        log.debug('compare counter to database counter: {0!r}'.format(
            self.token.count))
        if count_int >= self.token.count:
            res = count_int
            # on success we save the used counter
            self.inc_otp_counter(res)

        return res
Пример #4
0
    def check_otp(self, anOtpVal, counter=None, window=None, options=None):
        """
        validate the token otp against a given otpvalue

        :param anOtpVal: the to be verified otpvalue
        :type anOtpVal:  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

        """
        res = -1

        serial = self.token.serial
        secret = self.token.get_otpkey()

        # The prefix is the characters in front of the last 32 chars
        yubi_prefix = anOtpVal[:-32]
        # The variable otp val is the last 32 chars
        yubi_otp = anOtpVal[-32:]

        # TODO: We can also check the PREFIX! At the moment, we do not use it!

        try:
            otp_bin = modhex_decode(yubi_otp)
        except KeyError:
            # The OTP value is no yubikey aes otp value and can not be decoded
            return -4

        msg_bin = secret.aes_decrypt(otp_bin)
        msg_hex = binascii.hexlify(msg_bin)

        # 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).
        log.debug("calculated checksum (61624): %r" % checksum(msg_hex))
        if checksum(msg_hex) != 0xf0b8:  # pragma: no cover
            log.warning("CRC checksum for token %r failed" % serial)
            return -3

        uid = msg_hex[0:12]
        log.debug("uid: %r" % uid)
        log.debug("prefix: %r" % binascii.hexlify(modhex_decode(yubi_prefix)))
        # usage_counter can go from 1 – 0x7fff
        usage_counter = msg_hex[12:16]
        timestamp = msg_hex[16:22]
        # session counter can go from 00 to 0xff
        session_counter = msg_hex[22:24]
        random = msg_hex[24:28]
        crc = msg_hex[28:]
        log.debug("decrypted: usage_count: %r, session_count: %r" %
                  (usage_counter, session_counter))

        # 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('decrypted counter: %r' % count_int)

        tokenid = self.get_tokeninfo("yubikey.tokenid")
        if not tokenid:
            log.debug("Got no tokenid for %r. Setting to %r." % (serial, uid))
            tokenid = uid
            self.add_tokeninfo("yubikey.tokenid", tokenid)

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


        # TODO: We also could check the timestamp
        # see http://www.yubico.com/wp-content/uploads/2013/04/YubiKey-Manual-v3_1.pdf
        log.debug('compare counter to database counter: %r' % self.token.count)
        if count_int >= self.token.count:
            res = count_int
            # on success we save the used counter
            self.inc_otp_counter(res)

        return res