Development

This commit is contained in:
Matthew Grotke 2026-06-13 10:02:51 -04:00
parent 8a8e947fcf
commit 450c0081f7
9 changed files with 59 additions and 28 deletions

View file

@ -1,6 +1,7 @@
from pathlib import Path
from flask import Blueprint, request, session, redirect, flash
import bcrypt, secrets, smtplib
import secrets, smtplib
import settings
import time
from email.message import EmailMessage
import auth
@ -78,8 +79,7 @@ def form_create():
flash('This account is already set up. Please log in instead.', 'error')
return redirect(f'/{_PAGE}')
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password.encode('utf-8'), salt).decode('utf-8')
hashed = settings.hash_password(password)
code = f'{secrets.randbelow(1000000):06d}'
try:

View file

@ -1,6 +1,5 @@
from pathlib import Path
from flask import Blueprint, request, session, redirect, flash
import bcrypt
import auth
import config_utils
import sanitize
@ -29,7 +28,7 @@ def form_login():
if email != settings.get_initial_manager_email() or not stored_hash:
flash('Invalid email address or password.', 'error')
return redirect(f'/{_PAGE}')
if not bcrypt.checkpw(password.encode('utf-8'), stored_hash.encode('utf-8')):
if not settings.verify_password(password, stored_hash):
flash('Invalid email address or password.', 'error')
return redirect(f'/{_PAGE}')
session.clear()
@ -76,7 +75,7 @@ def form_login():
flash('Account setup is not complete. Please use Create Account to set your password first.', 'error')
return redirect(f'/{_PAGE}')
if not bcrypt.checkpw(password.encode('utf-8'), account['hashed_password'].encode('utf-8')):
if not settings.verify_password(password, account['hashed_password']):
flash('Invalid email address or password.', 'error')
return redirect(f'/{_PAGE}')

View file

@ -1,7 +1,6 @@
import sqlite3
import time
import bcrypt
from cryptography.fernet import Fernet
from flask import Blueprint, request, redirect, flash
import auth
@ -19,12 +18,12 @@ USER_TYPE_CAPTIVE = 0
USER_TYPE_SUPPLICANT = 1
DIGEST_CYPHERTEXT_FERNET = 0
DIGEST_HASH_BCRYPT = 2
DIGEST_HASH_SCRYPT = 2
VALID_USER_TYPES = {USER_TYPE_CAPTIVE, USER_TYPE_SUPPLICANT}
HASH_FOR_USER_TYPE = {
USER_TYPE_CAPTIVE: DIGEST_HASH_BCRYPT,
USER_TYPE_CAPTIVE: DIGEST_HASH_SCRYPT,
USER_TYPE_SUPPLICANT: DIGEST_CYPHERTEXT_FERNET,
}
@ -99,8 +98,8 @@ def _get_by_index(conn, row_index):
def _hash_password(plaintext, digest_type):
if digest_type == DIGEST_CYPHERTEXT_FERNET:
return encrypt_password(plaintext)
if digest_type == DIGEST_HASH_BCRYPT:
return bcrypt.hashpw(plaintext.encode(), bcrypt.gensalt()).decode()
if digest_type == DIGEST_HASH_SCRYPT:
return settings.hash_password(plaintext)
raise ValueError(f"Unknown digest_type: {digest_type}")

View file

@ -1,6 +1,5 @@
from pathlib import Path
from flask import Blueprint, request, session, redirect, flash
import bcrypt
import auth
import config_utils
import sanitize
@ -131,11 +130,11 @@ def changepassword_save():
flash('New password must be at least 8 characters.', 'error')
return redirect(f'/{_PAGE}')
hashed = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
hashed = settings.hash_password(new_password)
if settings.is_single_user():
stored_hash = settings.get_initial_manager_password_hash()
if not stored_hash or not bcrypt.checkpw(current_password.encode('utf-8'), stored_hash.encode('utf-8')):
if not stored_hash or not settings.verify_password(current_password, stored_hash):
flash('Current password is incorrect.', 'error')
return redirect(f'/{_PAGE}')
try:
@ -156,7 +155,7 @@ def changepassword_save():
flash('Account not found. Please log in again.', 'error')
return redirect('/accountlogin')
if not bcrypt.checkpw(current_password.encode('utf-8'), account['hashed_password'].encode('utf-8')):
if not settings.verify_password(current_password, account['hashed_password']):
flash('Current password is incorrect.', 'error')
return redirect(f'/{_PAGE}')

View file

@ -100,6 +100,28 @@ def get_credentials_key():
return base64.urlsafe_b64encode(raw)
def hash_password(plaintext):
import hashlib, os as _os
salt = _os.urandom(16)
h = hashlib.scrypt(plaintext.encode('utf-8'), salt=salt, n=16384, r=8, p=1, dklen=32)
return f'scrypt:16384:8:1:{salt.hex()}:{h.hex()}'
def verify_password(plaintext, stored):
import hashlib, hmac
try:
tag, n, r, p, salt_hex, hash_hex = stored.split(':')
if tag != 'scrypt':
return False
salt = bytes.fromhex(salt_hex)
expected = bytes.fromhex(hash_hex)
h = hashlib.scrypt(plaintext.encode('utf-8'), salt=salt,
n=int(n), r=int(r), p=int(p), dklen=len(expected))
return hmac.compare_digest(h, expected)
except Exception:
return False
def get_smtp_config():
"""Return SMTP settings from app_config.json, falling back to env vars."""
cfg = _load_app_config()

View file

@ -1,5 +1,4 @@
flask
bcrypt
cryptography
manuf
mac-vendor-lookup