#!/usr/bin/env python from __future__ import print_function import base64 import hashlib import hmac import struct import sys import time class TOTP(): class TOTPException(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return repr(self.msg) def __init__(self, secret): if not secret: raise TOTP.TOTPException('Invalid secret') self.secret = secret def generate(self): try: key = base64.b32decode(self.secret) num = int(time.time()) // 30 msg = struct.pack('>Q', num) # Take a SHA1 HMAC of key and binary-packed time value digest = hmac.new(key, msg, hashlib.sha1).digest() # Last 4 bits of the digest tells which 4 bytes to use offset = ord(digest[19]) & 15 token_base = digest[offset : offset+4] # Unpack that into an integer and strip it down token_val = struct.unpack('>I', token_base)[0] & 0x7fffffff token_num = token_val % 1000000 # Pad with leading zeroes token = '{0:06d}'.format(token_num) return token except: raise TOTP.TOTPException('Invalid secret') def main(args): if len(args): token = sys.stdin.readline().strip() if args[0] == '-' else args[0] elif not sys.stdin.isatty(): token = sys.stdin.readline().strip() else: return 'Usage: totp ' try: print(TOTP(token).generate()) except TOTP.TOTPException as e: return 'Error: ' + e.msg if __name__ == '__main__': try: sys.exit(main(sys.argv[1:])) except KeyboardInterrupt: sys.exit(1)