Use pyotp for validating TOTP codes
* also implements resynchronisation support via `pyotp`'s `valid_window option
This commit is contained in:
parent
3c3683429b
commit
f205c48564
|
@ -4,20 +4,16 @@ import io
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
from urllib.parse import quote
|
import pyotp
|
||||||
import qrcode
|
import qrcode
|
||||||
|
|
||||||
def get_secret():
|
def get_secret():
|
||||||
return base64.b32encode(os.urandom(20)).decode('utf-8')
|
return base64.b32encode(os.urandom(20)).decode('utf-8')
|
||||||
|
|
||||||
def get_otp_uri(secret, email):
|
def get_otp_uri(secret, email):
|
||||||
site_name = 'mailinabox'
|
return pyotp.TOTP(secret).provisioning_uri(
|
||||||
|
name=email,
|
||||||
return 'otpauth://totp/{}:{}?secret={}&issuer={}'.format(
|
issuer_name='mailinabox'
|
||||||
quote(site_name),
|
|
||||||
quote(email),
|
|
||||||
secret,
|
|
||||||
quote(site_name)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_qr_code(data):
|
def get_qr_code(data):
|
||||||
|
@ -32,20 +28,6 @@ def validate(secret, token):
|
||||||
"""
|
"""
|
||||||
@see https://tools.ietf.org/html/rfc6238#section-4
|
@see https://tools.ietf.org/html/rfc6238#section-4
|
||||||
@see https://tools.ietf.org/html/rfc4226#section-5.4
|
@see https://tools.ietf.org/html/rfc4226#section-5.4
|
||||||
@see https://git.sr.ht/~sircmpwn/meta.sr.ht/tree/master/metasrht/totp.py
|
|
||||||
@see https://github.com/susam/mintotp/blob/master/mintotp.py
|
|
||||||
TODO: resynchronisation
|
|
||||||
"""
|
"""
|
||||||
key = base64.b32decode(secret)
|
totp = pyotp.TOTP(secret)
|
||||||
tm = int(time.time() / 30)
|
return totp.verify(token, valid_window=2)
|
||||||
digits = 6
|
|
||||||
|
|
||||||
step = 0
|
|
||||||
counter = struct.pack('>Q', tm + step)
|
|
||||||
|
|
||||||
hm = hmac.HMAC(key, counter, 'sha1').digest()
|
|
||||||
offset = hm[-1] &0x0F
|
|
||||||
binary = struct.unpack(">L", hm[offset:offset + 4])[0] & 0x7fffffff
|
|
||||||
|
|
||||||
code = str(binary)[-digits:].rjust(digits, '0')
|
|
||||||
return token == code
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ hide_output $venv/bin/pip install --upgrade pip
|
||||||
hide_output $venv/bin/pip install --upgrade \
|
hide_output $venv/bin/pip install --upgrade \
|
||||||
rtyaml "email_validator>=1.0.0" "exclusiveprocess" \
|
rtyaml "email_validator>=1.0.0" "exclusiveprocess" \
|
||||||
flask dnspython python-dateutil \
|
flask dnspython python-dateutil \
|
||||||
qrcode[pil] \
|
qrcode[pil] pyotp \
|
||||||
"idna>=2.0.0" "cryptography==2.2.2" boto psutil postfix-mta-sts-resolver
|
"idna>=2.0.0" "cryptography==2.2.2" boto psutil postfix-mta-sts-resolver
|
||||||
|
|
||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
|
|
Loading…
Reference in New Issue