mirror of
				https://github.com/mail-in-a-box/mailinabox.git
				synced 2025-11-03 19:30:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			52 lines
		
	
	
		
			1.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			52 lines
		
	
	
		
			1.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import base64
 | 
						|
import hmac
 | 
						|
import io
 | 
						|
import os
 | 
						|
import struct
 | 
						|
import time
 | 
						|
from urllib.parse import quote
 | 
						|
import qrcode
 | 
						|
 | 
						|
def get_secret():
 | 
						|
	return base64.b32encode(os.urandom(20)).decode('utf-8')
 | 
						|
 | 
						|
def get_otp_uri(secret, email):
 | 
						|
	site_name = 'mailinabox'
 | 
						|
 | 
						|
	return 'otpauth://totp/{}:{}?secret={}&issuer={}'.format(
 | 
						|
		quote(site_name),
 | 
						|
		quote(email),
 | 
						|
		secret,
 | 
						|
		quote(site_name)
 | 
						|
	)
 | 
						|
 | 
						|
def get_qr_code(data):
 | 
						|
	qr = qrcode.make(data)
 | 
						|
	byte_arr = io.BytesIO()
 | 
						|
	qr.save(byte_arr, format='PNG')
 | 
						|
 | 
						|
	encoded = base64.b64encode(byte_arr.getvalue()).decode('utf-8')
 | 
						|
	return 'data:image/png;base64,{}'.format(encoded)
 | 
						|
 | 
						|
def validate(secret, token):
 | 
						|
	"""
 | 
						|
	@see https://tools.ietf.org/html/rfc6238#section-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)
 | 
						|
	tm = int(time.time() / 30)
 | 
						|
	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
 |