feat(F4): add PIN hashing utilities with PBKDF2-SHA256
This commit is contained in:
parent
2c72743bec
commit
3f2a59c11e
39
tests/unit/test_pin.py
Normal file
39
tests/unit/test_pin.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"""Tests for PIN hashing and verification."""
|
||||||
|
|
||||||
|
from vigilar.alerts.pin import hash_pin, verify_pin
|
||||||
|
|
||||||
|
|
||||||
|
def test_hash_pin_returns_formatted_string():
|
||||||
|
result = hash_pin("1234")
|
||||||
|
parts = result.split("$")
|
||||||
|
assert len(parts) == 3
|
||||||
|
assert parts[0] == "pbkdf2_sha256"
|
||||||
|
assert len(parts[1]) == 32 # 16 bytes hex = 32 chars
|
||||||
|
assert len(parts[2]) == 64 # 32 bytes hex = 64 chars
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_pin_correct():
|
||||||
|
stored = hash_pin("5678")
|
||||||
|
assert verify_pin("5678", stored) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_pin_wrong():
|
||||||
|
stored = hash_pin("5678")
|
||||||
|
assert verify_pin("0000", stored) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_pin_empty_hash_returns_true():
|
||||||
|
assert verify_pin("1234", "") is True
|
||||||
|
assert verify_pin("", "") is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_hash_pin_different_salts():
|
||||||
|
h1 = hash_pin("1234")
|
||||||
|
h2 = hash_pin("1234")
|
||||||
|
assert h1 != h2
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_pin_handles_unicode():
|
||||||
|
stored = hash_pin("p@ss!")
|
||||||
|
assert verify_pin("p@ss!", stored) is True
|
||||||
|
assert verify_pin("p@ss?", stored) is False
|
||||||
22
vigilar/alerts/pin.py
Normal file
22
vigilar/alerts/pin.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"""PIN hashing and verification using PBKDF2-SHA256."""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def hash_pin(pin: str) -> str:
|
||||||
|
salt = os.urandom(16)
|
||||||
|
dk = hashlib.pbkdf2_hmac("sha256", pin.encode(), salt, iterations=600_000)
|
||||||
|
return f"pbkdf2_sha256${salt.hex()}${dk.hex()}"
|
||||||
|
|
||||||
|
|
||||||
|
def verify_pin(pin: str, stored_hash: str) -> bool:
|
||||||
|
if not stored_hash:
|
||||||
|
return True
|
||||||
|
parts = stored_hash.split("$")
|
||||||
|
if len(parts) != 3 or parts[0] != "pbkdf2_sha256":
|
||||||
|
return False
|
||||||
|
salt = bytes.fromhex(parts[1])
|
||||||
|
expected = parts[2]
|
||||||
|
dk = hashlib.pbkdf2_hmac("sha256", pin.encode(), salt, iterations=600_000)
|
||||||
|
return dk.hex() == expected
|
||||||
Loading…
Reference in New Issue
Block a user