Files

79 lines
2.0 KiB
Python

"""Модуль TOTP 2FA"""
import base64
import os
import pyotp
import qrcode
# Настройкт из переменных окружения
TOTP_ISSUER = os.getenv("TOTP_ISSUER", "LiB")
TOTP_VALID_WINDOW = int(os.getenv("TOTP_VALID_WINDOW", "1"))
def generate_secret() -> str:
"""Генерация нового TOTP секрета"""
return pyotp.random_base32()
def get_provisioning_uri(secret: str, username: str) -> str:
"""Получение URI для QR-кода"""
totp = pyotp.TOTP(secret)
return totp.provisioning_uri(name=username, issuer_name=TOTP_ISSUER)
def verify_totp_code(secret: str, code: str) -> bool:
"""Проверка TOTP кода"""
if not secret or not code:
return False
totp = pyotp.TOTP(secret)
return totp.verify(code, valid_window=TOTP_VALID_WINDOW)
def qr_to_bitmap_b64(data: str) -> dict:
"""Конвертирует данные в QR-код и возвращает как base64 bitmap"""
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=1,
border=0,
)
qr.add_data(data)
qr.make(fit=True)
matrix = qr.get_matrix()
size = len(matrix)
bits = []
for row in matrix:
for cell in row:
bits.append(0 if cell else 1)
padding = (8 - len(bits) % 8) % 8
bits.extend([0] * padding)
bytes_array = bytearray()
for i in range(0, len(bits), 8):
byte = 0
for j in range(8):
byte = (byte << 1) | bits[i + j]
bytes_array.append(byte)
b64 = base64.b64encode(bytes_array).decode("ascii")
return {"size": size, "padding": padding, "bitmap_b64": b64}
def generate_totp_setup(username: str) -> dict:
"""Генерация данных для настройки TOTP"""
secret = generate_secret()
uri = get_provisioning_uri(secret, username)
bitmap_data = qr_to_bitmap_b64(uri)
return {
"secret": secret,
"username": username,
"issuer": TOTP_ISSUER,
**bitmap_data,
}