fix(cli): set-pin emits PBKDF2 under [security] pin_hash (issue #2)
Was: HMAC-SHA256(random, pin) written to [system] arm_pin_hash — no verifier in the codebase accepted this output. Now: PBKDF2-SHA256 via alerts.pin.hash_pin written to [security] pin_hash, matching what the web and FSM paths verify against. Also fixes show_cmd to redact the new location.
This commit is contained in:
31
tests/unit/test_cli_set_pin.py
Normal file
31
tests/unit/test_cli_set_pin.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""Tests for `vigilar config set-pin`."""
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
from vigilar.alerts.pin import verify_pin
|
||||
from vigilar.cli.cmd_config import config_cmd
|
||||
|
||||
|
||||
def test_set_pin_outputs_pbkdf2_hash_under_security_section():
|
||||
"""The CLI must emit a hash that alerts.pin.verify_pin can validate,
|
||||
and direct the user to [security] pin_hash (not [system] arm_pin_hash)."""
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(config_cmd, ["set-pin"], input="1234\n1234\n")
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
# The output must direct the user to the [security] section
|
||||
assert "[security]" in result.output
|
||||
assert "arm_pin_hash" not in result.output
|
||||
assert "pin_hash" in result.output
|
||||
|
||||
# Extract the emitted hash (line starts with `pin_hash = "..."`)
|
||||
hash_line = next(
|
||||
line for line in result.output.splitlines() if line.strip().startswith("pin_hash")
|
||||
)
|
||||
hash_value = hash_line.split('"')[1]
|
||||
|
||||
# Round-trip: the emitted hash must accept the plaintext PIN
|
||||
assert verify_pin("1234", hash_value) is True
|
||||
assert verify_pin("0000", hash_value) is False
|
||||
# And it must be in PBKDF2 format (not the legacy HMAC "secret:mac" format)
|
||||
assert hash_value.startswith("pbkdf2_sha256$")
|
||||
@@ -47,8 +47,8 @@ def show_cmd(config_path: str | None) -> None:
|
||||
# Redact sensitive fields
|
||||
if data.get("web", {}).get("password_hash"):
|
||||
data["web"]["password_hash"] = "***"
|
||||
if data.get("system", {}).get("arm_pin_hash"):
|
||||
data["system"]["arm_pin_hash"] = "***"
|
||||
if data.get("security", {}).get("pin_hash"):
|
||||
data["security"]["pin_hash"] = "***"
|
||||
if data.get("alerts", {}).get("webhook", {}).get("secret"):
|
||||
data["alerts"]["webhook"]["secret"] = "***"
|
||||
click.echo(json.dumps(data, indent=2))
|
||||
@@ -82,14 +82,10 @@ def set_password_cmd(config_path: str | None) -> None:
|
||||
|
||||
@config_cmd.command("set-pin")
|
||||
def set_pin_cmd() -> None:
|
||||
"""Generate an HMAC hash for the arm/disarm PIN."""
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
"""Generate a PBKDF2 hash for the arm/disarm PIN."""
|
||||
from vigilar.alerts.pin import hash_pin
|
||||
|
||||
pin = click.prompt("Enter arm/disarm PIN", hide_input=True, confirmation_prompt=True)
|
||||
secret = os.urandom(32)
|
||||
mac = hmac.new(secret, pin.encode(), hashlib.sha256).hexdigest()
|
||||
hash_str = secret.hex() + ":" + mac
|
||||
click.echo(f"\nAdd this to your vigilar.toml [system] section:")
|
||||
click.echo(f'arm_pin_hash = "{hash_str}"')
|
||||
hash_str = hash_pin(pin)
|
||||
click.echo("\nAdd this to your vigilar.toml [security] section:")
|
||||
click.echo(f'pin_hash = "{hash_str}"')
|
||||
|
||||
Reference in New Issue
Block a user