forked from akerl-archived/totp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
totp.py
executable file
·85 lines (71 loc) · 2.44 KB
/
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
from signal import signal, setitimer as alarm, pause, SIGINT, SIGALRM, ITIMER_REAL as ALARM
from os import path
from time import time
from base64 import b32decode
from struct import pack, unpack
from hmac import HMAC as hmac
from hashlib import sha1
from yaml import safe_load as load, dump
def catch (sig, frame):
raise Exception
signal(SIGINT, catch)
signal(SIGALRM, catch)
def setCatch(timer):
alarm(ALARM, timer)
class Config:
def __init__ (self, confFile):
setCatch(5)
confFile = path.expanduser(confFile)
self.file = confFile
with open(confFile) as handle:
self.options = load(handle)
if not 'secret' in self.options:
raise Exception
if 'ratelimit' in self.options:
self.options['ratelimit'].setdefault('limit', 3)
self.options['ratelimit'].setdefault('window', 0)
self.options['ratelimit'].setdefault('counter', 0)
self.options.setdefault('window', 1)
self.options.setdefault('timer', 30)
self.time = int(time() /30)
Secret = b32decode(self.options['secret'].encode('ascii'))
Drift = int(self.options['window'])
self.keys = []
for SkewedTime in range(self.time-Drift,self.time+Drift+1):
ByteTime = pack(">q", SkewedTime)
Hash = hmac(Secret, ByteTime, sha1).digest()
Offset = Hash[-1] & 0x0F
ShortHash = Hash[Offset:Offset+4]
Code = unpack('>L', ShortHash)[0]
Code &= 0x7FFFFFFF
Code %= 1000000
Code = "%06d" % Code
self.keys.append(Code)
def authenticate(self, Code):
authed = False
if 'ratelimit' in self.options:
if self.options['ratelimit']['window'] == self.time:
if self.options['ratelimit']['counter'] >= self.options['ratelimit']['limit']:
raise Exception
else:
writeConf = True
self.options['ratelimit']['counter'] += 1
else:
writeConf = True
self.options['ratelimit']['window'] = self.time
self.options['ratelimit']['counter'] = 0
if 'last' in self.options and Code == self.options['last']:
raise Exception
if Code in self.keys:
authed = True
elif 'scratch' in self.options and Code in self.options['scratch']:
authed = True
writeConf = True
self.options['scratch'].remove(Code)
if authed and 'last' in self.options:
writeConf = True
self.options['last'] = Code
if writeConf:
with open(self.file, 'w') as handle:
dump(self.options, handle)
return authed