Last active
April 19, 2025 12:20
-
-
Save M0r13n/9da20bfa2f7313e2bc473d279c6a5a06 to your computer and use it in GitHub Desktop.
Basic HMAC-based One-Time Password Algorithm in Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Basic HMAC-based One-Time Password Algorithm in Python | |
# Based on RFC 4226 and RFC 6238 | |
import base64 | |
import datetime | |
import hashlib | |
import struct | |
import time | |
def dt(hmac_result: bytes) -> int: | |
# Generate a 4-byte string (Dynamic Truncation) and return it as a 31 bit number | |
offset = hmac_result[-1] & 0xf | |
bin_code = (hmac_result[offset] & 0x7f) << 24 \ | |
| (hmac_result[offset + 1] & 0xff) << 16 \ | |
| (hmac_result[offset + 2] & 0xff) << 8 \ | |
| (hmac_result[offset + 3] & 0xff) | |
return bin_code | |
def hmac_sha1(key: bytes, message: bytes) -> bytes: | |
# Standard HMAC-SHA1 implementation | |
block_size = 64 | |
if len(key) > block_size: | |
key = hashlib.sha1(key).digest() | |
key = key + b'\x00' * (block_size - len(key)) | |
o_key_pad = bytes(x ^ 0x5c for x in key) | |
i_key_pad = bytes(x ^ 0x36 for x in key) | |
return hashlib.sha1(o_key_pad + hashlib.sha1(i_key_pad + message).digest()).digest() | |
def hotp(key: bytes, counter: int, digits: int = 6) -> int: | |
# HMAC-based One-Time Password Algorithm | |
counter_bytes = struct.pack('>Q', counter) # 8-byte counter | |
hmac_result = hmac_sha1(key, counter_bytes) | |
return int(dt(hmac_result) % (10 ** digits)) | |
def totp(key: bytes, time_step: int = 30, digits: int = 6) -> int: | |
# Time-based One-Time Password Algorithm | |
counter = int(time.time() // time_step) | |
return hotp(key, counter, digits) | |
def main() -> None: | |
base32_key = "JBSWY3DPEHPK3PXP" | |
key = base64.b32decode(base32_key) | |
while True: | |
current_time = time.time() | |
time_step = 30 | |
seconds_remaining = time_step - (current_time % time_step) | |
token = totp(key) | |
print(f'Token at {datetime.datetime.now()}: {token:06d}') | |
time.sleep(seconds_remaining) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment