Skip to content

Instantly share code, notes, and snippets.

@tsumarios
Last active October 21, 2022 10:06
Show Gist options
  • Save tsumarios/ca1647dad192d8558940e0d30b6e4f1b to your computer and use it in GitHub Desktop.
Save tsumarios/ca1647dad192d8558940e0d30b6e4f1b to your computer and use it in GitHub Desktop.
Simple script to generate a TOTP token. (source https://github.com/susam/mintotp). See https://tsumarios.github.io/blog/2022/10/07/how-totp-works/ for more.
#!/usr/bin/env python3
import base64
import datetime
import hmac
import os
import struct
import time
import sys
# This key should be kept hidden (e.g., in env variables, here supposed to be OTP_SHARED_KEY). The string here provided as default value is Base32 encoded and is just for the sake of demo.
KEY = os.environ.get('OTP_SHARED_KEY', 'ORZXK3LBOJUW643CNRXWO2LTMF3WK43PNVSQ====')
def hotp(key, counter, digits=6, digest='sha1'):
key = base64.b32decode(key.upper() + '=' * ((8 - len(key)) % 8))
counter = struct.pack('>Q', counter)
mac = hmac.new(key, counter, digest).digest()
offset = mac[-1] & 0x0f
binary = struct.unpack('>L', mac[offset:offset+4])[0] & 0x7fffffff
return str(binary)[-digits:].zfill(digits)
def totp(key, time_step=30, digits=6, digest='sha1'):
return hotp(key, int(time.time() / time_step), digits, digest)
def main():
while True:
# Generate a new OTP every second by using TOTP.
# NOTE: every 30s the OTP value will change :D
try:
secs = 30 - datetime.datetime.utcnow().second % 30
otp_code = totp(KEY.strip())
timer = '{:02d}s'.format(secs)
print(f"OTP: {otp_code} (expires in {timer})", end="\r")
time.sleep(1)
except KeyboardInterrupt:
print('', end="\r")
sys.exit(0)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment