Skip to content

Instantly share code, notes, and snippets.

@MineRobber9000
Created March 4, 2020 00:54
Show Gist options
  • Select an option

  • Save MineRobber9000/d468c3d1dfc6ac20d0314600ca765bc4 to your computer and use it in GitHub Desktop.

Select an option

Save MineRobber9000/d468c3d1dfc6ac20d0314600ca765bc4 to your computer and use it in GitHub Desktop.
TOTP and HOTP implemented in Python

totp.py

A simple library. Despite its name, it does TOTP and HOTP. To do a TOTP:

import totp, base64

secret = base64.b32decode("ABCDEFGHIJKLMNOP")
code = totp.totp(secret)
if code==input("Insert TOTP here:").strip(): print("Success")
else: print("Failure!")

# alternatively,  you can give the secret as a base32 string and the
# library will decode it for you.
secret = "ABCDEFGHIJKLMNOP"
code = totp.totp(secret)
if code==input("Insert TOTP here:").strip(): print("Success")
else: print("Failure!")

To do HOTP:

import totp, base64

secret = base64.b32decode("ABCDEFGHIJKLMNOP")
counter = 0
code = totp.hotp(secret,counter)
if code==input("Insert HOTP here:").strip(): print("Success")
else: print("Failure!")

# alternatively,  you can give the secret as a base32 string and the
# library will decode it for you.
secret = "ABCDEFGHIJKLMNOP"
counter = 0
code = totp.hotp(secret,counter)
if code==input("Insert HOTP here:").strip(): print("Success")
else: print("Failure!")
import time, hmac, base64, hashlib, struct
def pack_counter(t):
return struct.pack(">Q", t)
def dynamic_truncate(raw_bytes, length):
offset = raw_bytes[19] & 0x0f
decimal_value = ((raw_bytes[offset] & 0x7f) << 24) | (
(raw_bytes[offset + 1] & 0xff) << 16
) | ((raw_bytes[offset + 2] & 0xFF) << 8) | (raw_bytes[offset + 3] & 0xFF)
return str(decimal_value)[-length:]
def hotp(secret, counter, length=6):
if type(counter) != bytes: counter = pack_counter(int(counter))
if type(secret) != bytes: secret = base64.b32decode(secret)
digest = hmac.new(secret, counter, hashlib.sha1).digest()
return dynamic_truncate(digest, length)
def totp(secret, length=6):
"""TOTP is implemented as HOTP, but with the counter being the floor of
the division of the Unix timestamp by 30."""
counter = pack_counter(round(time.time() // 30))
return hotp(secret, counter, length)
@sudoker0
Copy link

sudoker0 commented Dec 5, 2022

Nice and simple library.
I do have one question tho: Is this library safe to use in production? Like can I use this to do 2FA safely?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment