I created a TOTP algorithm and provided a key from a site. When I entered that key into various sites to check if they all produce the same OTP, I found that they do. However, my algorithm sometimes generates the correct OTP and other times produces the wrong OTP. Why is this happening?
import hmac
import hashlib
import struct
import time
import base64
def generate_totp(secret, time_step=30, digits=6, current_time=None):
if current_time is None:
current_time = int(time.time())
print("is NONE waalaa --- ",current_time)
print(current_time)
current_time //= time_step
time_bytes = struct.pack('>Q', current_time)
secret = base64.b32decode(secret, casefold=True)
hmac_result = hmac.new(secret, time_bytes, hashlib.sha1).digest()
offset = hmac_result[-1] & 0xF
truncated_hash = hmac_result[offset : offset + 4]
otp = struct.unpack('>I', truncated_hash)[0]
otp = otp % (10 ** digits)
otp_str = str(otp).zfill(digits)
return otp_str, current_time
def get_time_until_next_step(time_step=30):
current_time = int(time.time())
print("This is the current time", current_time)
return time_step - (current_time % time_step)
# Example Usage:
if __name__ == "__main__":
secret_key = "2FASTEST"
while True:
wait_time = get_time_until_next_step()
time.sleep(wait_time)
current_totp, current_time = generate_totp(secret_key, current_time=int(time.time()))
print(f"Generated TOTP: {current_totp}")
How to correct it ?
first bit of truncated hash must be 0, your code is resulting correct when the first bit is a 0, and incorrect when first bit is 1, so you must do and operation of truncated hash with 01111111...(32-bits)
otp = struct.unpack('>I', truncated_hash)[0]
otp = otp & 0x7fffffff
otp = otp % (10 ** digits)
otp_str = str(otp).zfill(digits)