pythonbase64passwordshmactotp

Why does my TOTP algorithm sometimes generate the wrong OTP and other times the correct one?


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 ?


Solution

  • 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)