42 lines
1.2 KiB
Python
42 lines
1.2 KiB
Python
#!/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())
|