#!/usr/bin/env python import base64 import hmac import re import struct import sys import time from dataclasses import dataclass import defl from defl import cl from defl import log from dataclasses import dataclass, field @dataclass(slots=True, kw_only=True, frozen=False) class OTP: cert: str = field(kw_only=False, repr=True) def __post_init__(_): _.cert = re.compile(r'\s', flags=re.I).sub(r'', _.cert) assert re.compile(r'^[0-9A-Za-z]+$', flags=re.I).search(_.cert) @staticmethod 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:].rjust(digits, '0') @staticmethod def _totp(key, time_step=30, digits=6, digest='sha1'): return OTP._hotp(key, int(time.time() / time_step), digits, digest) def generate(_): return OTP._totp(_.cert) if __name__ == '__main__': res = OTP(cert=sys.argv[1]) log.info(res.generate())