diff --git a/docs/FUZZING.md b/docs/FUZZING.md index d2a97cc..569d069 100644 --- a/docs/FUZZING.md +++ b/docs/FUZZING.md @@ -99,8 +99,16 @@ Install the `honggfuzz` binary (distro package or source). Point it at a binary ## CI smoke -Short runs can use `-runs=1000` instead of time limits: +GitHub Actions (`.github/workflows/fuzz-smoke.yml`) builds all targets with sanitizer `none`, then runs **`scripts/fuzz-libfuzzer.sh`** with **`FUZZ_RUNS=2000`** so every target gets a short fixed-iteration smoke (same idea as below). + +Locally you can match that with: ```bash -cargo fuzz run -s none handshake_respond -- -runs=1000 -print_final_stats=1 +FUZZ_RUNS=2000 ./scripts/fuzz-libfuzzer.sh +``` + +Or smoke a single target with a run budget instead of time: + +```bash +cargo fuzz run -s none handshake_respond -- -runs=2000 -print_final_stats=1 ``` diff --git a/docs/PYTHON_API.md b/docs/PYTHON_API.md index 55bd9c2..318c85a 100644 --- a/docs/PYTHON_API.md +++ b/docs/PYTHON_API.md @@ -52,6 +52,8 @@ New random issuer. `from_secret_key(bytes)` restores from 32-byte secret. ### `Credential.finalize(blind_sig, member_secret, prover_blind, role_id, epoch, pk) -> Credential` +`role_id` and `prover_blind` must be 32 bytes. + - `present(nonce) -> bytes` - `role_id() -> bytes`, `epoch() -> int` @@ -66,14 +68,21 @@ New random issuer. `from_secret_key(bytes)` restores from 32-byte secret. ## Node and session +### `PendingConnect` + +Opaque handle returned from `connect()`; passed once into a `complete_*` method (then consumed). + ### `Node(keypair)` — consumes `Keypair` - `public_key() -> PublicKey` - `connect() -> (PendingConnect, bytes)` — 32-byte init message - `accept(init_msg) -> (Session, bytes)` — `init_msg` 32 bytes - `prove_identity(session) -> bytes` — server produces encrypted identity proof (Schnorr signature over transcript) -- `complete_connect(pending, response_msg, identity_proof, expected_server_pk, credential) -> (Session, bytes)` — verifies server identity, then produces BBS+ auth packet -- `verify_auth(session, encrypted_auth, registry) -> bytes` — returns 32-byte `role_id` +- `complete_connect(pending, response_msg, identity_proof, expected_server_pk, credential) -> (Session, bytes)` — verifies server identity, then produces BBS+ auth packet (static `RoleRegistry`) +- `complete_connect_anon(pending, response_msg, identity_proof, expected_server_pk) -> Session` — server identity only; no BBS+ auth packet +- `complete_connect_managed(pending, response_msg, identity_proof, expected_server_pk, credential, registry_id) -> (Session, bytes)` — auth packet includes 32-byte registry id (client-managed registries) +- `verify_auth(session, encrypted_auth, registry) -> bytes` — returns 32-byte `role_id` for static `RoleRegistry` +- `verify_auth_managed(session, encrypted_auth, manager) -> (registry_id, role_id)` — for `RegistryManager` ### `Session` @@ -81,12 +90,20 @@ New random issuer. `from_secret_key(bytes)` restores from 32-byte secret. - `encrypt(plaintext) -> bytes` - `decrypt(packet) -> bytes` +## Managed registries (optional) + +Server-side state for multiple registries and admin proofs lives in **`RegistryManager`**. Opaque **`RegistryState`** blobs are built and certified client-side, then **`registry_id(admin_issuer_pk)`** / **`admin_role_id()`** identify a registry and the fixed admin role. Methods on **`RegistryManager`** include `create` / `update` / `get` / `has_registry`, `verify_admin`, `verify_presentation(registry_id, role_id, …)`, and issuance-queue helpers (`queue_issuance_request`, `take_pending_requests`, `grant_credential`, `take_granted_credential`). **`RegistryState`** exposes `build`, `serialize` / `deserialize`, `registry_id`, `version`, `state_hash`, `certify`, and `verify_cert`. + +## E2E issuance encryption (optional) + +**`IssuanceKeypair`** (with `encrypt` / `decrypt` on ephemeral keys) and module helpers **`encrypt_for_admin`** / **`decrypt_from_admin`** support encrypting blind-commitment material to an admin’s issuance public key. See `src/python.rs` for exact parameters. + ## Typical flow -1. Issuer creates `BbsIssuer()`; server `register_role(role_id, issuer.public_key(), epoch)`. +1. Issuer creates `BbsIssuer()`; server `register_role(role_id, issuer.public_key(), epoch)` (static registry), or use **`RegistryManager`** + certified **`RegistryState`** for multi-tenant / managed flows. 2. Member: `prepare_blind_request` → issuer `issue_blind` → `Credential.finalize`. 3. **Out-of-band:** client obtains server's `PublicKey` (static config, pinning, etc.). -4. Client: `connect` → server `accept` → server `prove_identity` → client `complete_connect` (verifies server identity + sends BBS+ auth) → server `verify_auth`. +4. Client: `connect` → server `accept` → server `prove_identity` → client `complete_connect` or `complete_connect_managed` (verifies server identity + sends BBS+ auth) → server `verify_auth` or `verify_auth_managed`. Use `complete_connect_anon` when no credential auth is needed. 5. Use `Session.encrypt` / `decrypt` for data. ## Errors