Full pipeline proving a WebAuthn PRF assertion can anchor a stable Nostr keypair. Core derivation in nostr_passkey/derivation.py (pure, unit-tested), WebAuthn ceremony glue in webauthn_flow.py, FastAPI surface in app.py, single-page WebAuthn client in web/index.html, and an end-to-end simulation in scripts/demo.py for running without a real authenticator. Verified working against Firefox 149 + macOS Touch ID over HTTPS on https://localhost:8000 with a self-signed loopback cert. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
51 lines
1.5 KiB
Python
51 lines
1.5 KiB
Python
"""Tests for the PRF -> HKDF -> Nostr key derivation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from nostr_passkey.derivation import PRF_OUTPUT_BYTES, derive_nostr_key
|
|
|
|
PRF_A = bytes.fromhex("11" * 32)
|
|
PRF_B = bytes.fromhex("22" * 32)
|
|
|
|
|
|
def test_deterministic_for_same_inputs() -> None:
|
|
a = derive_nostr_key(PRF_A, salt="alpha")
|
|
b = derive_nostr_key(PRF_A, salt="alpha")
|
|
assert a == b
|
|
|
|
|
|
def test_different_salts_produce_different_keys() -> None:
|
|
a = derive_nostr_key(PRF_A, salt="alpha")
|
|
b = derive_nostr_key(PRF_A, salt="beta")
|
|
assert a.privkey_hex != b.privkey_hex
|
|
assert a.npub != b.npub
|
|
|
|
|
|
def test_different_prf_outputs_produce_different_keys() -> None:
|
|
a = derive_nostr_key(PRF_A, salt="alpha")
|
|
b = derive_nostr_key(PRF_B, salt="alpha")
|
|
assert a.privkey_hex != b.privkey_hex
|
|
|
|
|
|
def test_no_salt_differs_from_nonempty_salt() -> None:
|
|
no_salt = derive_nostr_key(PRF_A)
|
|
salted = derive_nostr_key(PRF_A, salt="x")
|
|
assert no_salt.privkey_hex != salted.privkey_hex
|
|
|
|
|
|
def test_rejects_wrong_prf_length() -> None:
|
|
with pytest.raises(ValueError):
|
|
derive_nostr_key(b"too-short")
|
|
with pytest.raises(ValueError):
|
|
derive_nostr_key(bytes(PRF_OUTPUT_BYTES + 1))
|
|
|
|
|
|
def test_output_shapes() -> None:
|
|
ident = derive_nostr_key(PRF_A)
|
|
assert len(ident.privkey_hex) == 64 # 32 bytes hex
|
|
assert len(ident.pubkey_hex) == 64 # x-only, 32 bytes hex
|
|
assert ident.nsec.startswith("nsec1")
|
|
assert ident.npub.startswith("npub1")
|