fix password digests

This commit is contained in:
evilchili 2025-10-04 13:27:38 -07:00
parent 7eb06b150e
commit 76e65def2c
2 changed files with 22 additions and 2 deletions

View File

@ -1,7 +1,9 @@
from __future__ import annotations from __future__ import annotations
import hashlib import hashlib
import hmac
import os import os
import re
from collections import namedtuple from collections import namedtuple
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Dict, List from typing import Dict, List
@ -57,6 +59,21 @@ class Password(Field):
salt_size = 4 salt_size = 4
digest_size = 16 digest_size = 16
@classmethod
def is_digest(cls, passwd: str):
if not passwd:
return False
offset = 2 * cls.salt_size # each byte is 2 hex chars
try:
if passwd[offset] != ":":
return False
digest = passwd[offset + 1 :]
if len(digest) != cls.digest_size * 2:
return False
return re.match(r"^[0-9a-f]+$", digest)
except IndexError:
return False
@classmethod @classmethod
def get_digest(cls, passwd: str, salt: bytes = None): def get_digest(cls, passwd: str, salt: bytes = None):
if not salt: if not salt:
@ -68,10 +85,10 @@ class Password(Field):
def compare(cls, passwd: value_type, stored: value_type): def compare(cls, passwd: value_type, stored: value_type):
stored_salt, stored_digest = stored.split(":") stored_salt, stored_digest = stored.split(":")
input_digest, input_salt = cls.get_digest(passwd, bytes.fromhex(stored_salt)) input_digest, input_salt = cls.get_digest(passwd, bytes.fromhex(stored_salt))
return input_digest == stored_digest return hmac.compare_digest(input_digest, stored_digest)
def before_insert(self, value: value_type, db: TinyDB, record: Record) -> None: def before_insert(self, value: value_type, db: TinyDB, record: Record) -> None:
if value: if value and not self.__class__.is_digest(value):
digest, salt = self.__class__.get_digest(value) digest, salt = self.__class__.get_digest(value)
record[self.name] = f"{salt}:{digest}" record[self.name] = f"{salt}:{digest}"

View File

@ -123,6 +123,9 @@ def test_search(db):
def test_password(db): def test_password(db):
user = db.save(examples.User(name="john", email="john@foo", password="fnord")) user = db.save(examples.User(name="john", email="john@foo", password="fnord"))
# make sure we don't compute the digest on an existing digest
user = db.save(user)
assert ":" in user.password assert ":" in user.password
assert user.password != "fnord" assert user.password != "fnord"