Ejemplo n.º 1
0
    def test_code_from_hash_with_alternate_lengths(self):
        """Test Otp.code_from_hash().

        Try with alternate code lengths.

        """
        cut = HOTP()
        # code_length 1
        #
        should_be = ("4", "2", "2", "9", "4", "6", "2", "3", "1", "9")
        for i in range(0, 10):
            code = cut.code_from_hash(self.expected[i][2], 1)
            self.assertEqual(should_be[i], code)
        # code_length 9
        #
        should_be = (
            "284755224",
            "094287082",
            "137359152",
            "726969429",
            "640338314",
            "868254676",
            "918287922",
            "082162583",
            "673399871",
            "645520489")
        for i in range(0, 10):
            code = cut.code_from_hash(self.expected[i][2], 9)
            self.assertEqual(should_be[i], code)
Ejemplo n.º 2
0
    def test_convert_base32_bad_chars(self):
        """Test Otp.convert_base32_secret_key().

        Check that an input base32 encoded string that is not encoded correctly
        throws the expected exception.

        """
        cut = HOTP()
        # First be certain the method works with a correct base32-encoded
        # input string
        #
        in_string = "ABCDEFGH"
        cut.convert_base32_secret_key(in_string)
        # Then check that it throws the expected exception with an
        # incorrectly coded input string.
        #
        # lower case not acceptable
        in_string = "abcdefgh"
        with self.assertRaises(ValueError):
            cut.convert_base32_secret_key(in_string)
        # numbers outside 2-7 not acceptable
        in_string = "ABCDEFG1"
        with self.assertRaises(ValueError):
            cut.convert_base32_secret_key(in_string)
        # padding character elsewhere than end of string
        in_string = "ABCD=FGH"
        with self.assertRaises(ValueError):
            cut.convert_base32_secret_key(in_string)
Ejemplo n.º 3
0
    def test_generate_code_from_time_period_not_numeric(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to non-numeric period.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_time(self.secret, period="abc")
Ejemplo n.º 4
0
    def reference_generate_code_from_time(self, secret_key):
        """Reference implementation of generate_code_from_time method.

        A reference/alternate implementation of Otp.generate_code_from_time()
        which is to be used to generate expected values for unit tests.

        Returns:
            A tuple containing:
                * The time-based OTP, as a string of digits.
                * The integer number of seconds remaining in the current
                  interval.

        """
        import time
        import datetime
        from hashlib import sha1
        import hmac

        cut = HOTP()
        # message := current Unix time ÷ 30
        #
        local_now = datetime.datetime.now()
        seconds_now = time.mktime(local_now.timetuple())
        intervals = seconds_now // 30
        remaining_seconds = seconds_now - (intervals * 30)
        message = cut.num_to_counter(intervals)
        # hash := HMAC-SHA1(key, message)
        #
        hmac = hmac.new(secret_key, message, sha1)
        hash = hmac.hexdigest()
        # offset := last nibble of hash
        #
        offset = int("0" + hash[-1], 16)
        offset *= 2
        # truncated_hash := hash[offset..offset+3]
        # (that is 4 bytes starting at the offset)
        #
        truncated_hash = hash[offset: offset + (4 * 2)]
        # Set the first bit of truncated_hash to zero
        # (remove the most significant bit)
        #
        new_high_order_byte = hex(
            int(truncated_hash[0:2], 16) & int('7F', 16))[2:]
        new_high_order_byte = \
            "0" * (2 - len(new_high_order_byte)) + new_high_order_byte
        truncated_hash = new_high_order_byte + truncated_hash[2:]
        # code := truncated_hash mod 1000000
        #
        int_hash = int(truncated_hash, 16)
        code = int_hash % 1000000
        # pad code with 0 until length of code is 6
        #
        code_string = str(code)
        code_string = "0" * (6 - len(code_string)) + code_string
        # return code
        #
        return code_string, int(30 - remaining_seconds)
Ejemplo n.º 5
0
    def test_generate_code_from_counter_secret_wrong_type(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to wrong secret type.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.generate_code_from_counter(1.234, self.expected[0][0])
Ejemplo n.º 6
0
    def test_code_from_hash_bad_hash(self):
        """Test Otp.code_from_hash().

        Check that the RFC4226 test cases work for code_from_hash()

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.code_from_hash("abc")
Ejemplo n.º 7
0
    def test_generate_code_from_time_secret_wrong_type(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to wrong secret type.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.generate_code_from_time(1.234)
Ejemplo n.º 8
0
    def test_generate_code_from_time_period_wrong_type(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to wrong period type.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.generate_code_from_time(self.secret, period=(6, 3))
Ejemplo n.º 9
0
    def test_code_from_hash_long_code_length(self):
        """Test Otp.code_from_hash().

        Check that the RFC4226 test cases work for code_from_hash()

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.code_from_hash(self.expected[0][2], 11)
Ejemplo n.º 10
0
    def reference_generate_code_from_time(self, secret_key):
        """Reference implementation of generate_code_from_time method.

        A reference/alternate implementation of Otp.generate_code_from_time()
        which is to be used to generate expected values for unit tests.

        Returns:
            A tuple containing:
                * The time-based OTP, as a string of digits.
                * The integer number of seconds remaining in the current
                  interval.

        """
        import time
        import datetime
        from hashlib import sha1
        import hmac

        cut = HOTP()
        # message := current Unix time ÷ 30
        #
        local_now = datetime.datetime.now()
        seconds_now = time.mktime(local_now.timetuple())
        intervals = seconds_now // 30
        remaining_seconds = seconds_now - (intervals * 30)
        message = cut.num_to_counter(intervals)
        # hash := HMAC-SHA1(key, message)
        #
        hmac = hmac.new(secret_key, message, sha1)
        hash = hmac.hexdigest()
        # offset := last nibble of hash
        #
        offset = int("0" + hash[-1], 16)
        offset *= 2
        # truncated_hash := hash[offset..offset+3]
        # (that is 4 bytes starting at the offset)
        #
        truncated_hash = hash[offset:offset + (4 * 2)]
        # Set the first bit of truncated_hash to zero
        # (remove the most significant bit)
        #
        new_high_order_byte = hex(
            int(truncated_hash[0:2], 16) & int('7F', 16))[2:]
        new_high_order_byte = \
            "0" * (2 - len(new_high_order_byte)) + new_high_order_byte
        truncated_hash = new_high_order_byte + truncated_hash[2:]
        # code := truncated_hash mod 1000000
        #
        int_hash = int(truncated_hash, 16)
        code = int_hash % 1000000
        # pad code with 0 until length of code is 6
        #
        code_string = str(code)
        code_string = "0" * (6 - len(code_string)) + code_string
        # return code
        #
        return code_string, int(30 - remaining_seconds)
Ejemplo n.º 11
0
    def test_code_from_hash_bad_hash(self):
        """Test Otp.code_from_hash().

        Check that the RFC4226 test cases work for code_from_hash()

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.code_from_hash("abc")
Ejemplo n.º 12
0
    def test_code_from_hash_wrong_length(self):
        """Test Otp.code_from_hash().

        Check that the RFC4226 test cases work for code_from_hash()

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.code_from_hash(bytes.fromhex("abcdef"))
Ejemplo n.º 13
0
    def test_generate_code_from_counter_counter_bad(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to invalid counter value.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_counter(self.secret, -1)
Ejemplo n.º 14
0
    def test_code_from_hash_long_code_length(self):
        """Test Otp.code_from_hash().

        Check that the RFC4226 test cases work for code_from_hash()

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.code_from_hash(self.expected[0][2], 11)
Ejemplo n.º 15
0
    def test_generate_code_from_counter_counter_wrong_type(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to wrong counter type.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_counter(self.secret, "abcdefgh")
Ejemplo n.º 16
0
    def test_generate_code_from_time_secret_wrong_type(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to wrong secret type.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.generate_code_from_time(1.234)
Ejemplo n.º 17
0
    def test_code_from_hash_wrong_length(self):
        """Test Otp.code_from_hash().

        Check that the RFC4226 test cases work for code_from_hash()

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.code_from_hash(bytes.fromhex("abcdef"))
Ejemplo n.º 18
0
    def test_generate_code_from_counter_code_length_not_numeric(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to non-numeric code_length.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_counter(
                self.secret, self.expected[0][0], code_length="abc")
Ejemplo n.º 19
0
    def test_generate_code_from_counter_code_length_wrong_type(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to wrong code_length type.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.generate_code_from_counter(
                self.secret, self.expected[0][0], code_length=(6, 3))
Ejemplo n.º 20
0
    def test_generate_code_from_counter_counter_bad(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to invalid counter value.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_counter(
                self.secret, -1)
Ejemplo n.º 21
0
    def test_generate_code_from_counter_counter_wrong_type(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to wrong counter type.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_counter(
                self.secret, "abcdefgh")
Ejemplo n.º 22
0
    def test_generate_code_from_counter_secret_wrong_type(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to wrong secret type.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.generate_code_from_counter(
                1.234, self.expected[0][0])
Ejemplo n.º 23
0
    def test_hash_from_hmac_not_20bytes(self):
        """Test Otp.hash_from_hmac().

        Check that HMAC length validation is performed.

        """
        cut = HOTP()
        hmac = bytes.fromhex("ff0102030405060708090a0b0c0d0e0f101112")
        with self.assertRaises(ValueError):
            cut.hash_from_hmac(hmac)
Ejemplo n.º 24
0
    def test_code_from_hash(self):
        """Test Otp.code_from_hash().

        Check that the RFC4226 test cases work for code_from_hash()

        """
        cut = HOTP()
        for i in range(0, 10):
            code = cut.code_from_hash(self.expected[i][2])
            self.assertEqual(self.expected[i][3], code)
Ejemplo n.º 25
0
    def test_counter_from_time_period_wrong_type(self):
        """Test Otp.counter_from_time().

        Check that providing a period that is not numeric or convertable
        to numeric raises the appropriate exception.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.counter_from_time(period=(6, 3))
Ejemplo n.º 26
0
    def test_hash_from_hmac_not_byte_string(self):
        """Test Otp.hash_from_hmac().

        Check that HMAC type (byte string) validation is performed.

        """
        cut = HOTP()
        hmac = bytearray.fromhex("ff0102030405060708090a0b0c0d0e0f101112f0")
        with self.assertRaises(TypeError):
            cut.hash_from_hmac(hmac)
Ejemplo n.º 27
0
    def test_generate_hmac(self):
        """Test Otp.generate_hmac().

        Check that the RFC4226 test cases work for generate_hmac().

        """
        cut = HOTP()
        for i in range(0, 10):
            hmac = cut.generate_hmac(self.secret, self.expected[i][0])
            self.assertEqual(self.expected[i][1], hmac)
Ejemplo n.º 28
0
    def test_generate_code_from_time_period_wrong_type(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to wrong period type.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.generate_code_from_time(
                self.secret, period=(6, 3))
Ejemplo n.º 29
0
    def test_generate_hmac(self):
        """Test Otp.generate_hmac().

        Check that the RFC4226 test cases work for generate_hmac().

        """
        cut = HOTP()
        for i in range(0, 10):
            hmac = cut.generate_hmac(self.secret, self.expected[i][0])
            self.assertEqual(self.expected[i][1], hmac)
Ejemplo n.º 30
0
    def test_generate_code_from_time_secret_wrong_base32_length(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to secret string that is invalid
        base32 encoding (too long, bad padding).

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_time("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQAAA")
Ejemplo n.º 31
0
    def test_hash_from_hmac_not_20bytes(self):
        """Test Otp.hash_from_hmac().

        Check that HMAC length validation is performed.

        """
        cut = HOTP()
        hmac = bytes.fromhex("ff0102030405060708090a0b0c0d0e0f101112")
        with self.assertRaises(ValueError):
            cut.hash_from_hmac(hmac)
Ejemplo n.º 32
0
    def test_generate_code_from_time_period_not_numeric(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to non-numeric period.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_time(
                self.secret, period="abc")
Ejemplo n.º 33
0
    def test_counter_from_time_period_wrong_type(self):
        """Test Otp.counter_from_time().

        Check that providing a period that is not numeric or convertable
        to numeric raises the appropriate exception.

        """
        cut = HOTP()
        with self.assertRaises(TypeError):
            cut.counter_from_time(period=(6, 3))
Ejemplo n.º 34
0
    def test_code_from_hash(self):
        """Test Otp.code_from_hash().

        Check that the RFC4226 test cases work for code_from_hash()

        """
        cut = HOTP()
        for i in range(0, 10):
            code = cut.code_from_hash(self.expected[i][2])
            self.assertEqual(self.expected[i][3], code)
Ejemplo n.º 35
0
    def test_hash_from_hmac_not_byte_string(self):
        """Test Otp.hash_from_hmac().

        Check that HMAC type (byte string) validation is performed.

        """
        cut = HOTP()
        hmac = bytearray.fromhex("ff0102030405060708090a0b0c0d0e0f101112f0")
        with self.assertRaises(TypeError):
            cut.hash_from_hmac(hmac)
Ejemplo n.º 36
0
    def test_generate_code_from_time_secret_bad_base32(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to secret string that is invalid
        base32 encoding.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_time("GEZDGNBVGY1TQOJQGEZDGNBVGY1TQOJQ")
Ejemplo n.º 37
0
    def test_convert_base32_bad_chars(self):
        """Test Otp.convert_base32_secret_key().

        Check that an input base32 encoded string that is not encoded correctly
        throws the expected exception.

        """
        cut = HOTP()
        # First be certain the method works with a correct base32-encoded
        # input string
        #
        in_string = "ABCDEFGH"
        cut.convert_base32_secret_key(in_string)
        # Then check that it throws the expected exception with an
        # incorrectly coded input string.
        #
        # lower case not acceptable
        in_string = "abcdefgh"
        with self.assertRaises(ValueError):
            cut.convert_base32_secret_key(in_string)
        # numbers outside 2-7 not acceptable
        in_string = "ABCDEFG1"
        with self.assertRaises(ValueError):
            cut.convert_base32_secret_key(in_string)
        # padding character elsewhere than end of string
        in_string = "ABCD=FGH"
        with self.assertRaises(ValueError):
            cut.convert_base32_secret_key(in_string)
Ejemplo n.º 38
0
    def test_generate_hmac_counter_not_byte_string(self):
        """Test Otp.generate_hmac().

        Check that a counter that is other than a byte string.

        """
        cut = HOTP()
        # If the counter byte string is less than 8 bytes
        #
        with self.assertRaises(TypeError):
            cut.generate_hmac(self.secret, "1234")
Ejemplo n.º 39
0
    def test_num_to_counter_too_large(self):
        """Test Otp.num_to_counter().

        Check that an exception is raised if a very
        value is passed to num_to_counter().

        """
        cut = HOTP()
        num = 2**64
        with self.assertRaises(ValueError):
            cut.num_to_counter(num)
Ejemplo n.º 40
0
    def test_generate_hmac_secret_not_byte_string(self):
        """Test Otp.generate_hmac().

        Check that a counter that is other than a byte string.

        """
        cut = HOTP()
        # If the counter byte string is less than 8 bytes
        #
        with self.assertRaises(TypeError):
            cut.generate_hmac("1234567890", self.expected[1][0])
Ejemplo n.º 41
0
    def test_generate_code_from_time_secret_bad_base32(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to secret string that is invalid
        base32 encoding.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_time(
                "GEZDGNBVGY1TQOJQGEZDGNBVGY1TQOJQ")
Ejemplo n.º 42
0
    def test_generate_code_from_counter_counter_long(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to counter byte string that is
        too short.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_counter(
                self.secret, bytes.fromhex("010203040506070809"))
Ejemplo n.º 43
0
    def test_num_to_counter_negative(self):
        """Test Otp.num_to_counter().

        Check that an exception is raised if a negative
        value is passed to num_to_counter().

        """
        cut = HOTP()
        num = -1
        with self.assertRaises(ValueError):
            cut.num_to_counter(num)
Ejemplo n.º 44
0
    def test_generate_code_from_time_secret_wrong_base32_length(self):
        """Test Otp.generate_code_from_time().

        Check for appropriate exception to secret string that is invalid
        base32 encoding (too long, bad padding).

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_time(
                "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQAAA")
Ejemplo n.º 45
0
    def test_hash_from_hmac(self):
        """Test Otp.hash_from_hmac().

        Check that expected truncated hash values are produced.

        """
        cut = HOTP()
        for i in range(0, 10):
            hash = cut.hash_from_hmac(self.expected[i][1])
            self.assertEqual(self.expected[i][2], hash)
        return
Ejemplo n.º 46
0
    def test_num_to_counter_too_large(self):
        """Test Otp.num_to_counter().

        Check that an exception is raised if a very
        value is passed to num_to_counter().

        """
        cut = HOTP()
        num = 2**64
        with self.assertRaises(ValueError):
            cut.num_to_counter(num)
Ejemplo n.º 47
0
    def test_num_to_counter_not_number(self):
        """Test Otp.num_to_counter().

        Check that an exception is raised if a non-numeric
        value is passed to num_to_counter().

        """
        cut = HOTP()
        num = "abcd"
        with self.assertRaises(ValueError):
            cut.num_to_counter(num)
Ejemplo n.º 48
0
    def test_generate_code_from_counter_counter_long(self):
        """Test Otp.generate_code_from_counter().

        Check for appropriate exception to counter byte string that is
        too short.

        """
        cut = HOTP()
        with self.assertRaises(ValueError):
            cut.generate_code_from_counter(self.secret,
                                           bytes.fromhex("010203040506070809"))
Ejemplo n.º 49
0
    def test_hmac_from_counter(self):
        """Test Otp.generate_hmac().

        Check that expected HMAC-SHA-1 digest values are produced.

        """
        cut = HOTP()
        for i in range(0, 10):
            counter = cut.num_to_counter(i)
            actual_hmac = cut.generate_hmac(self.secret, counter)
            self.assertEqual(self.expected[i][1], actual_hmac)
Ejemplo n.º 50
0
    def test_num_to_counter_negative(self):
        """Test Otp.num_to_counter().

        Check that an exception is raised if a negative
        value is passed to num_to_counter().

        """
        cut = HOTP()
        num = -1
        with self.assertRaises(ValueError):
            cut.num_to_counter(num)
Ejemplo n.º 51
0
    def test_hash_from_hmac_clear_high_bit(self):
        """Test Otp.hash_from_hmac().

        Check that the high order bit is cleared in the truncated hash.

        """
        cut = HOTP()
        hmac = bytes.fromhex("ff0102030405060708090a0b0c0d0e0f101112f0")
        expected = bytes.fromhex("7f010203")
        hash = cut.hash_from_hmac(hmac)
        self.assertEqual(expected, hash)
Ejemplo n.º 52
0
    def test_hmac_from_counter(self):
        """Test Otp.generate_hmac().

        Check that expected HMAC-SHA-1 digest values are produced.

        """
        cut = HOTP()
        for i in range(0, 10):
            counter = cut.num_to_counter(i)
            actual_hmac = cut.generate_hmac(self.secret, counter)
            self.assertEqual(self.expected[i][1], actual_hmac)
Ejemplo n.º 53
0
    def test_hash_from_hmac_clear_high_bit(self):
        """Test Otp.hash_from_hmac().

        Check that the high order bit is cleared in the truncated hash.

        """
        cut = HOTP()
        hmac = bytes.fromhex("ff0102030405060708090a0b0c0d0e0f101112f0")
        expected = bytes.fromhex("7f010203")
        hash = cut.hash_from_hmac(hmac)
        self.assertEqual(expected, hash)
Ejemplo n.º 54
0
    def test_generate_hmac_counter_not_byte_string(self):
        """Test Otp.generate_hmac().

        Check that a counter that is other than a byte string.

        """
        cut = HOTP()
        # If the counter byte string is less than 8 bytes
        #
        with self.assertRaises(TypeError):
            cut.generate_hmac(self.secret, "1234")
Ejemplo n.º 55
0
    def test_generate_hmac_secret_not_byte_string(self):
        """Test Otp.generate_hmac().

        Check that a counter that is other than a byte string.

        """
        cut = HOTP()
        # If the counter byte string is less than 8 bytes
        #
        with self.assertRaises(TypeError):
            cut.generate_hmac("1234567890", self.expected[1][0])
Ejemplo n.º 56
0
    def test_hash_from_hmac(self):
        """Test Otp.hash_from_hmac().

        Check that expected truncated hash values are produced.

        """
        cut = HOTP()
        for i in range(0, 10):
            hash = cut.hash_from_hmac(self.expected[i][1])
            self.assertEqual(self.expected[i][2], hash)
        return
Ejemplo n.º 57
0
    def test_num_to_counter_not_number(self):
        """Test Otp.num_to_counter().

        Check that an exception is raised if a non-numeric
        value is passed to num_to_counter().

        """
        cut = HOTP()
        num = "abcd"
        with self.assertRaises(ValueError):
            cut.num_to_counter(num)