-
Notifications
You must be signed in to change notification settings - Fork 0
/
generate_totp.py
57 lines (43 loc) · 1.34 KB
/
generate_totp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#! /usr/bin/python3
import hashlib
import datetime
import base64
import hmac
import getpass
import sys
INTERVAL = 30
DIGITS = 6
DIGEST = hashlib.sha1
def current_timecode():
current_time = datetime.datetime.now(datetime.timezone.utc)
timecode = int(current_time.timestamp()) // INTERVAL
return timecode
def get_hotp(secret, counter):
secret_val = base64.b32decode(secret, casefold = True)
secret_bytes = bytearray()
while counter:
secret_bytes.append(counter & 0xFF)
counter >>= 8
counter_bytes = bytes(bytearray(reversed(secret_bytes)).rjust(8, b'\0'))
hash_bytes = bytearray(hmac.new(secret_val, counter_bytes, DIGEST).digest())
offset = hash_bytes[-1] & 0xF
result_code = (
(hash_bytes[offset] & 0x7F) << 24 |
(hash_bytes[offset + 1] & 0xFF) << 16 |
(hash_bytes[offset + 2] & 0xFF) << 8 |
(hash_bytes[offset + 3] & 0xFF)
)
str_code = str(100000000000 + (result_code % (10 ** DIGITS)))
return str_code[-DIGITS:]
def get_totp(secret):
return get_hotp(secret, current_timecode())
def read_secret():
paswd = getpass.getpass()
if len(paswd) == 0:
raise ValueError('Secret is empty')
return paswd
if __name__ == '__main__':
try:
print(get_totp(read_secret()))
except Exception as e:
print(e, file = sys.stderr)