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)
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)
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)
def test_num_to_counter_float(self): """Test Otp.num_to_counter(). Check that floating point values work. """ cut = HOTP() counter = cut.num_to_counter(12345678.9) self.assertEqual(bytes.fromhex("0000000000bc614e"), counter) for i in range(0, 10): counter = cut.num_to_counter(i + 0.6) self.assertEqual(self.expected[i][0], counter) return
def test_num_to_counter(self): """Test Otp.num_to_counter(). Check that various integer values work. Includes large and small values. """ cut = HOTP() counter = cut.num_to_counter(2**63 + 7) self.assertEqual(bytes.fromhex("8000000000000007"), counter) for i in range(0, 10): counter = cut.num_to_counter(i) self.assertEqual(self.expected[i][0], counter) return
def test_num_to_counter(self): """Test Otp.num_to_counter(). Check that various integer values work. Includes large and small values. """ cut = HOTP() counter = cut.num_to_counter(2 ** 63 + 7) self.assertEqual(bytes.fromhex("8000000000000007"), counter) for i in range(0, 10): counter = cut.num_to_counter(i) self.assertEqual(self.expected[i][0], counter) return
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)
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)
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)