fix(events): ArmStateFSM uses PBKDF2 via alerts.pin (issue #2)
Was: unsalted SHA-256 read from [system] arm_pin_hash. Now: PBKDF2-SHA256 600k iterations read from [security] pin_hash, matching the web arm/disarm path and the alerts/pin module. Also drops the redundant pin re-hash on the arm_state_log audit row (a fresh PBKDF2 salt made the column valueless for traceability). Part of issue #2 PIN hashing unification. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
"""Tests for the Phase 6 events subsystem: rules, arm state FSM, history."""
|
||||
|
||||
import hashlib
|
||||
import time
|
||||
|
||||
import pytest
|
||||
@@ -19,7 +18,7 @@ from vigilar.storage.queries import insert_event
|
||||
|
||||
def _make_config(rules=None, pin_hash=""):
|
||||
return VigilarConfig(
|
||||
system={"arm_pin_hash": pin_hash},
|
||||
security={"pin_hash": pin_hash},
|
||||
cameras=[],
|
||||
sensors=[],
|
||||
rules=rules or [],
|
||||
@@ -27,7 +26,8 @@ def _make_config(rules=None, pin_hash=""):
|
||||
|
||||
|
||||
def _pin_hash(pin: str) -> str:
|
||||
return hashlib.sha256(pin.encode()).hexdigest()
|
||||
from vigilar.alerts.pin import hash_pin
|
||||
return hash_pin(pin)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
"""Arm state finite state machine."""
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import logging
|
||||
import time
|
||||
|
||||
from sqlalchemy.engine import Engine
|
||||
|
||||
from vigilar.alerts.pin import verify_pin as _verify_pin_hash
|
||||
from vigilar.config import VigilarConfig
|
||||
from vigilar.constants import ArmState, EventType, Severity, Topics
|
||||
from vigilar.storage.queries import get_current_arm_state, insert_arm_state, insert_event
|
||||
@@ -19,7 +18,7 @@ class ArmStateFSM:
|
||||
|
||||
def __init__(self, engine: Engine, config: VigilarConfig):
|
||||
self._engine = engine
|
||||
self._pin_hash = config.system.arm_pin_hash
|
||||
self._pin_hash = config.security.pin_hash
|
||||
self._state = ArmState.DISARMED
|
||||
self._bus = None
|
||||
self._load_initial_state()
|
||||
@@ -43,12 +42,11 @@ class ArmStateFSM:
|
||||
return self._state
|
||||
|
||||
def verify_pin(self, pin: str) -> bool:
|
||||
"""Verify a PIN against the stored hash using HMAC comparison."""
|
||||
"""Verify a PIN against the stored PBKDF2 hash."""
|
||||
if not self._pin_hash:
|
||||
# No PIN configured — allow all transitions
|
||||
return True
|
||||
candidate = hashlib.sha256(pin.encode()).hexdigest()
|
||||
return hmac.compare_digest(candidate, self._pin_hash)
|
||||
return _verify_pin_hash(pin, self._pin_hash)
|
||||
|
||||
def transition(
|
||||
self,
|
||||
@@ -68,9 +66,8 @@ class ArmStateFSM:
|
||||
old_state = self._state
|
||||
self._state = new_state
|
||||
|
||||
# Log to database
|
||||
pin_hash = hashlib.sha256(pin.encode()).hexdigest() if pin else None
|
||||
insert_arm_state(self._engine, new_state.value, triggered_by, pin_hash)
|
||||
# Log to database (pin_hash column is no longer populated — see #2)
|
||||
insert_arm_state(self._engine, new_state.value, triggered_by, None)
|
||||
|
||||
# Log event
|
||||
insert_event(
|
||||
|
||||
Reference in New Issue
Block a user