Files
nostr-passkey/tests/test_derivation.py
Michael Schapira - krilin 5f35195e72 Initial prototype: passkey PRF → HKDF → Nostr key
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>
2026-04-10 23:12:27 -04:00

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")