k_card/CLAUDE.md

5.4 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository policy

  • CR_SDK_CK-main/ is the firmware SDK source tree. Treat it as read-only. Do not add or edit files there.
  • All host-side scripts live at the workspace root (/home/user/chromecard).
  • Setup.md and Workplan.md are the canonical living docs. After each meaningful execution step, update Setup.md for environment/runtime state and Workplan.md for phase progress and next blocking action.

Running tests

# Python unit tests (no VMs or card required, 122 tests)
python3 -m unittest tests/test_k_proxy.py

# Playwright browser regression (requires services running and forwarded portal)
PORTAL_BASE_URL=http://127.0.0.1:18766 npm run test:k-client

# Split-VM end-to-end regression helper (runs from host via SSH into k_client)
./phase5_chain_regression.sh
./phase5_chain_regression.sh --interactive-card --expect-auth-mode fido2_assertion

Probing the card (on k_proxy)

python3 /home/user/chromecard/fido2_probe.py --list
python3 /home/user/chromecard/fido2_probe.py --json
python3 /home/user/chromecard/raw_ctap_probe.py info
python3 /home/user/chromecard/raw_ctap_probe.py make-credential --rp-id localhost
python3 /home/user/chromecard/webauthn_local_demo.py   # opens http://localhost:8765

Generating TLS certificates

python3 generate_phase2_certs.py
# Writes tls/phase2/ca.crt, k_proxy.crt/.key, k_server.crt/.key

Starting services (split-VM shape)

k_server VM:

python3 /home/user/chromecard/k_server_app.py \
  --host 127.0.0.1 --port 8780 \
  --proxy-token dev-proxy-token \
  --tls-certfile /home/user/chromecard/tls/phase2/k_server.crt \
  --tls-keyfile /home/user/chromecard/tls/phase2/k_server.key

k_proxy VM:

qvm-connect-tcp 9780:k_server:8780

python3 /home/user/chromecard/k_proxy_app.py \
  --host 127.0.0.1 --port 8771 --session-ttl 300 \
  --server-base-url https://127.0.0.1:9780 \
  --server-ca-file /home/user/chromecard/tls/phase2/ca.crt \
  --proxy-token dev-proxy-token \
  --tls-certfile /home/user/chromecard/tls/phase2/k_proxy.crt \
  --tls-keyfile /home/user/chromecard/tls/phase2/k_proxy.key
  # Add --auth-mode fido2-direct for real CTAP2 (default is probe mode)

k_client VM:

qvm-connect-tcp 9771:k_proxy:8771

python3 /home/user/chromecard/k_client_portal.py \
  --proxy-base-url https://127.0.0.1:9771
  # Browser demo page at http://127.0.0.1:8766

Files are deployed to VMs via scp <file> <host>:~ and run via ssh <host> <cmd>.

Architecture

Qubes 3-VM topology: k_clientk_proxyk_server, each a Debian 13 AppVM.

Inter-VM transport uses qvm-connect-tcp localhost forwarding (not raw VM-IP routing). Validated chain:

  • k_client localhost:9771k_proxy:8771
  • k_proxy localhost:9780k_server:8780

k_proxy_app.py — session gateway and FIDO2 auth bridge. Two auth modes:

  • probe (default): validates card presence by subprocess-calling fido2_probe.py --json
  • fido2-direct: performs real CTAP2 makeCredential/getAssertion against the physical card via python-fido2; auto-detects the FIDO hidraw device

ProxyState holds all server-side state: in-memory session store (guarded by one lock), enrollment DB (JSON file), and an UpstreamPool of persistent TLS connections to k_server. Sessions are lost on restart.

k_server_app.py — protected resource backend. Exposes a monotonic counter behind X-Proxy-Token auth. Counter state is in-memory only; resets on restart. Lock guards counter increments.

k_client_portal.py — thin browser-facing portal in k_client. Delegates all auth and resource calls to k_proxy. Holds only a local preferred username; enrollment and session state live in k_proxy.

FIDO2 transport: Card communicates over USB HID (CTAPHID) on /dev/hidraw0 (FIDO interface, usage page 0xF1D0). /dev/hidraw1 is a separate vendor HID interface. If the card re-enumerates, k_proxy auto-detects the correct node. If CTAPHID stops responding, a full USB power cycle is the recovery path.

CardEmulator (tests/card_emulator.py) — software emulator of the card for unit tests. Implements make_credential/get_assertion with real P-256 crypto; user_confirms=False simulates card rejection. Wire it into tests by patching _with_direct_ctap2 and _drop_direct_device on ProxyState. See the module docstring for the exact patch pattern.

Key enrollment endpoints on k_proxy: POST /enroll/register, GET /enroll/status, POST /enroll/update, POST /enroll/delete, GET /enroll/list. Usernames are normalized to lowercase, 332 chars [a-z0-9._-].

Key session endpoints on k_proxy: POST /session/login, POST /session/status, POST /session/logout, POST /resource/counter.

Known limits and blockers

  • Concurrency ceiling on the browser-facing forwarded path is ~10 in-flight requests; higher fan-out triggers Qubes vchan failures (xs_transaction_start: No space left on device).
  • If CTAPHID INIT packets get no reply after a card reattach, a full USB power cycle recovers the transport.
  • CR_SDK_CK-main is missing role directories (mvp, setup, components, samples) required for the firmware build/flash flow (./scripts/build_flash_mvp.sh). west and nrfjprog must also be installed.
  • Phases 7 (firmware build) and 9 (phone-wireless transport) are externally gated.