110 lines
5.4 KiB
Markdown
110 lines
5.4 KiB
Markdown
# 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
|
||
|
||
```bash
|
||
# 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)
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
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:**
|
||
```bash
|
||
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:**
|
||
```bash
|
||
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:**
|
||
```bash
|
||
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_client` → `k_proxy` → `k_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:9771` → `k_proxy:8771`
|
||
- `k_proxy localhost:9780` → `k_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, 3–32 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.
|