fix: critical review findings — constant-time PIN compare, redact security config, sunset sign

- Use hmac.compare_digest() in verify_pin() to prevent timing-based PIN oracle attacks
- Redact entire [security] section (pin_hash, recovery_passphrase_hash) from /api/config response
- Sunset sign fix was skipped: existing longitude - ha formula is correct per NOAA equations and verified by test_sunset_equator; longitude + ha produces sunrise, not sunset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee 2026-04-03 19:18:14 -04:00
parent bdfadbb829
commit 3289f874ab
2 changed files with 3 additions and 1 deletions

View File

@ -1,6 +1,7 @@
"""PIN hashing and verification using PBKDF2-SHA256.""" """PIN hashing and verification using PBKDF2-SHA256."""
import hashlib import hashlib
import hmac
import os import os
@ -19,4 +20,4 @@ def verify_pin(pin: str, stored_hash: str) -> bool:
salt = bytes.fromhex(parts[1]) salt = bytes.fromhex(parts[1])
expected = parts[2] expected = parts[2]
dk = hashlib.pbkdf2_hmac("sha256", pin.encode(), salt, iterations=600_000) dk = hashlib.pbkdf2_hmac("sha256", pin.encode(), salt, iterations=600_000)
return dk.hex() == expected return hmac.compare_digest(dk.hex(), expected)

View File

@ -106,6 +106,7 @@ def get_config_api():
data.get("system", {}).pop("arm_pin_hash", None) data.get("system", {}).pop("arm_pin_hash", None)
data.get("alerts", {}).get("webhook", {}).pop("secret", None) data.get("alerts", {}).get("webhook", {}).pop("secret", None)
data.get("storage", {}).pop("key_file", None) data.get("storage", {}).pop("key_file", None)
data.pop("security", None)
return jsonify(data) return jsonify(data)