540 lines
26 KiB
Markdown
540 lines
26 KiB
Markdown
# GPT-5.6 Audit Mitigation and Redesign Plan
|
||
|
||
**Companion to:** [`audit.md`](audit.md:1)
|
||
**Purpose:** turn the current research prototype into a defensible post-quantum key pre-commitment/link system and define the additional work required for a complete Nostr migration.
|
||
|
||
## 1. Target outcome
|
||
|
||
The immediate target should **not** be marketed as “Nostr is now quantum-resistant.” The achievable first production milestone is:
|
||
|
||
> A client can prove that, before a verified Bitcoin block, an existing Nostr identity authorized and demonstrated possession of a canonical set of PQ keys; all clients deterministically resolve competing claims to the same earliest valid authorization; future PQ protocols can use that key set as their migration root.
|
||
|
||
A complete migration is a later milestone requiring standardized PQ-signed events, PQ encryption, identity mapping, revocation, rotation, recovery, and client adoption.
|
||
|
||
## 2. Non-negotiable security invariants
|
||
|
||
Implement these before addressing UI polish. Every design and test should trace back to at least one invariant.
|
||
|
||
| Invariant | Required property |
|
||
|---|---|
|
||
| I-01 Canonical authorization | All authenticators sign one versioned, domain-separated deterministic byte representation—not free-form prose or partially overlapping objects. |
|
||
| I-02 Strict policy | Missing, duplicated, malformed, downgraded, or unknown evidence cannot accidentally count as successful evidence. |
|
||
| I-03 Identity binding | Query author, wrapper author, embedded author, and old-identity field are equal. |
|
||
| I-04 Immutable history | An attacker cannot hide the initial candidate by publishing a newer replaceable event. |
|
||
| I-05 Earliest-anchor resolution | Every conforming client chooses the same earliest fully valid Bitcoin anchor under defined tie-break rules. |
|
||
| I-06 PQ possession | Each PQ signature key proves possession; KEM ownership is either proven by a specified mechanism or described only as authorization. |
|
||
| I-07 Post-break authority | After migration, secp-only claims cannot replace or revoke the canonical PQ root. |
|
||
| I-08 Availability | The canonical authorization and proof can be exported, mirrored, and verified without the original website or one relay. |
|
||
| I-09 Trust transparency | “Bitcoin verified,” “API checked,” and “structurally present” are separate states. |
|
||
| I-10 Secure key boundary | Production seed derivation and PQ signing occur in a dedicated signer/hardware boundary where feasible. |
|
||
|
||
## 3. Recommended protocol redesign
|
||
|
||
This redesign should precede patching the existing two-event format, because several critical flaws arise from format semantics.
|
||
|
||
### 3.1 Define a canonical authorization object
|
||
|
||
Add a protocol module—for example, a new `www/js/nip-qr-schema.mjs` file—rather than embedding schema logic in [`pq-crypto.mjs`](../../www/js/pq-crypto.mjs:1).
|
||
|
||
Use deterministic CBOR or an explicitly specified canonical JSON encoding. A conceptual structure is:
|
||
|
||
```text
|
||
{
|
||
protocol: "nostr-pq-link",
|
||
version: 1,
|
||
network: "nostr-main",
|
||
old_pubkey: <32 bytes>,
|
||
successor_pubkey: <optional 32-byte seed-derived Schnorr key>,
|
||
created_at: <integer>,
|
||
nonce: <32 random bytes>,
|
||
keys: [
|
||
{ algorithm: "ml-dsa-44", public_key: <bytes>, role: "sign" },
|
||
{ algorithm: "ml-dsa-65", public_key: <bytes>, role: "sign" },
|
||
{ algorithm: "slh-dsa-sha2-128s", public_key: <bytes>, role: "sign" },
|
||
{ algorithm: "falcon-512", public_key: <bytes>, role: "sign" },
|
||
{ algorithm: "ml-kem-768", public_key: <bytes>, role: "encrypt" }
|
||
],
|
||
policy: {
|
||
required_signature_algorithms: [...],
|
||
rotation_threshold: <defined rule>
|
||
}
|
||
}
|
||
```
|
||
|
||
Normative signing input:
|
||
|
||
```text
|
||
UTF8("NOSTR-PQ-LINK\0") || protocol_version || canonical_encode(object)
|
||
```
|
||
|
||
Requirements:
|
||
|
||
- Sort key entries by registered algorithm ID.
|
||
- Reject duplicate algorithms.
|
||
- Pin exact public-key and signature lengths by version.
|
||
- PQ signatures sign the complete object, including all sibling keys and policy.
|
||
- The old secp identity signs a Nostr event committing to the exact canonical object digest.
|
||
- If common seed origin/recoverability is claimed, a seed-derived successor key signs the same object.
|
||
- Human-readable content is generated from the object and is explicitly non-normative.
|
||
|
||
### 3.2 Separate immutable authorization from mutable proof transport
|
||
|
||
Do not make the canonical authorization only a replaceable kind 11112 event.
|
||
|
||
Recommended structure:
|
||
|
||
1. **Immutable authorization event:** a regular, non-replaceable event containing or committing to the canonical authorization and all possession proofs.
|
||
2. **OTS proof-carrier event:** may be parameterized/replaceable by authorization ID, but every version must reference the same immutable authorization event ID and target digest.
|
||
3. **Archive package:** downloadable canonical object, signed authorization event, OTS proof, and verification metadata.
|
||
|
||
If kind allocation is not standardized, use an experimental range and clearly version it. Do not overload kind 1 as the sole normative migration record merely for feed visibility; a separate optional kind 1 announcement can reference the immutable authorization.
|
||
|
||
### 3.3 Define canonical conflict resolution
|
||
|
||
Specify the algorithm before coding it:
|
||
|
||
1. Gather all immutable authorization candidates for `old_pubkey` from all configured relays, local archives, and user-provided packages.
|
||
2. Validate schema, old secp signature, identity equality, PQ proofs, algorithm policy, and OTS target binding.
|
||
3. Require a Bitcoin attestation with configured confirmation depth.
|
||
4. Sort valid candidates by:
|
||
- lowest verified Bitcoin block height,
|
||
- then lowest authorization digest lexicographically for deterministic same-block ties.
|
||
5. The first candidate becomes the genesis PQ root.
|
||
6. Later rotation is valid only if authorized under the canonical root’s PQ rotation policy.
|
||
7. A later secp-only authorization never supersedes the genesis root after the defined transition policy activates.
|
||
|
||
Document the limitation that a previously unknown earlier proof can change resolution. Mitigate this with archive discovery, checkpointing, and proof gossip. Consider defining a stabilization period before treating a candidate as final.
|
||
|
||
### 3.4 Define PQ rotation and future event authentication
|
||
|
||
A linked key with no use protocol is only a commitment. Create separate specifications for:
|
||
|
||
- PQ authorization envelopes for future Nostr events;
|
||
- replay/domain separation and binding to the NIP-01 event ID;
|
||
- key rotation and revocation thresholds;
|
||
- algorithm deprecation/emergency removal;
|
||
- recovery after seed loss;
|
||
- PQ encryption and KEM key rotation;
|
||
- how clients map the old npub to a successor key without splitting the social graph.
|
||
|
||
A practical compatibility design is a normal NIP-01 event plus a standardized tag or companion event containing a PQ signature over a domain-separated NIP-01 event ID. Relays can remain unaware initially; PQ-aware clients enforce the companion proof. This needs independent protocol review before adoption.
|
||
|
||
## 4. Staged remediation plan
|
||
|
||
## Phase 0 — Freeze unsafe production claims
|
||
|
||
**Goal:** prevent real-user harm while redesign proceeds.
|
||
|
||
1. Add a prominent research-prototype warning to [`index.html`](../../www/index.html:320) and [`verify.html`](../../www/verify.html:254).
|
||
2. Replace “Make your Nostr identity quantum-resistant” with “Create an experimental pre-quantum PQ key commitment.”
|
||
3. Disable or gate real relay publication behind an explicit experimental acknowledgement.
|
||
4. Warn users never to enter a valuable existing mnemonic into the web page.
|
||
5. Restore the 12-word PQ-strength warning and default new generation to 24 words.
|
||
6. Change every success state to distinguish:
|
||
- event signatures valid,
|
||
- PQ policy sufficient,
|
||
- OTS pending,
|
||
- OTS API-checked,
|
||
- canonical earliest candidate selected.
|
||
7. Mark [`README.md`](../../README.md:1), [`nip_proposal.md`](../../nip_proposal.md:1), and research documents with implementation-status banners.
|
||
|
||
**Release gate:** the public deployment cannot imply complete quantum resistance.
|
||
|
||
## Phase 1 — Replace the verifier with strict schema/policy validation
|
||
|
||
**Findings addressed:** G56-01, G56-03, G56-07, G56-17, G56-19, part of G56-20.
|
||
|
||
### Implementation instructions
|
||
|
||
1. Create a schema validator that checks before cryptography:
|
||
- object type and exact required fields,
|
||
- integer ranges,
|
||
- exact kind values,
|
||
- arrays and tag element types,
|
||
- exact lowercase hex lengths,
|
||
- strict canonical base64 with decoded byte limits,
|
||
- maximum content/event/proof sizes,
|
||
- exact tag cardinality,
|
||
- no duplicate recognized tags/algorithms.
|
||
2. Refactor [`verifyNostrEvent()`](../../www/js/pq-crypto.mjs:400) to require `id`, `sig`, `pubkey`, `created_at`, `kind`, `tags`, and `content` by default. Return `{ valid, errors }`; do not throw on untrusted malformed input.
|
||
3. Replace the fail-open branch at [`verifyNIPQRContent()`](../../www/js/pq-crypto.mjs:683):
|
||
- only `ml-kem-768` may omit a signature;
|
||
- all signature algorithms require signatures;
|
||
- unknown algorithms are `ignored` evidence, never successful evidence;
|
||
- mandatory algorithms are evaluated after parsing all tags.
|
||
4. Pass the full outer event and expected query author into the verifier. Require:
|
||
- `outer.kind` is the expected wrapper kind,
|
||
- `outer.pubkey === embedded.pubkey`,
|
||
- `outer.pubkey === expectedAuthor` when supplied,
|
||
- `e` and SHA-256 tags are unique and exact.
|
||
5. Return a structured result:
|
||
|
||
```text
|
||
{
|
||
structurallyValid,
|
||
secpValid,
|
||
pqProofsValid,
|
||
policySufficient,
|
||
otsTargetBound,
|
||
otsBitcoinStatus,
|
||
canonicalSelectionStatus,
|
||
validForMigration,
|
||
errors,
|
||
warnings
|
||
}
|
||
```
|
||
|
||
6. Make [`verify.html`](../../www/verify.html:530) display these separate states and never infer overall migration validity from `results.every(...)`.
|
||
7. Validate the claimed block height against the verified OTS height or remove it from the normative authorization.
|
||
|
||
### Required tests
|
||
|
||
Add tests for:
|
||
|
||
- zero algorithm tags,
|
||
- each missing mandatory algorithm,
|
||
- missing signature on every signature algorithm,
|
||
- arbitrary unknown no-signature algorithm,
|
||
- duplicate algorithm tags,
|
||
- duplicate `e`, SHA-256, and OTS tags in both orders,
|
||
- wrong decoded key/signature lengths,
|
||
- malformed base64/hex,
|
||
- null/wrong-shaped objects,
|
||
- missing event ID,
|
||
- wrong wrapper kind,
|
||
- wrapper/embedded/query-author mismatch,
|
||
- oversized event and proof,
|
||
- valid optional unknown algorithm ignored without policy credit,
|
||
- exact valid fixture passing all layers.
|
||
|
||
**Acceptance criterion:** malformed input never throws, and no event without the full configured PQ policy can be labeled migration-valid.
|
||
|
||
## Phase 2 — Implement immutable candidate discovery and earliest-anchor selection
|
||
|
||
**Findings addressed:** G56-02, G56-12, G56-16.
|
||
|
||
### Implementation instructions
|
||
|
||
1. Replace `limit: 1` queries at [`verify.html`](../../www/verify.html:487) and [`index.html`](../../www/index.html:1201) with candidate collection until EOSE from each relay.
|
||
2. Do not select by `created_at`.
|
||
3. Verify every candidate independently and retain relay provenance.
|
||
4. Introduce a pure function such as:
|
||
|
||
```text
|
||
selectCanonicalAuthorization(candidates, policy)
|
||
```
|
||
|
||
It must sort only fully valid confirmed candidates by verified block height and deterministic digest tie-break.
|
||
5. Store immutable complete artifacts locally:
|
||
- old identity,
|
||
- authorization event JSON/bytes,
|
||
- canonical authorization digest,
|
||
- OTS target digest,
|
||
- pending/confirmed proof,
|
||
- proof-carrier event(s),
|
||
- relay receipts.
|
||
6. Replace the overloaded `eventId` metadata in [`savePendingOts()`](../../www/js/pq-crypto.mjs:1362) with explicit `authorizationEventId`, `wrapperEventId`, and `targetDigest`.
|
||
7. On resume, require every persisted/fetched binding to match before signing or publishing.
|
||
8. Add export/import of a self-contained archive package.
|
||
|
||
### Required tests
|
||
|
||
- later secp-valid forged candidate loses to earlier valid anchor,
|
||
- earlier invalid PQ candidate is rejected,
|
||
- earlier proof with wrong target is rejected,
|
||
- same-block tie resolves identically regardless of relay order,
|
||
- malicious relay sends events outside subscription/filter,
|
||
- one relay hides the earlier candidate but another supplies it,
|
||
- resume rejects a different event ID/digest/owner,
|
||
- upgraded proof preserves immutable authorization target.
|
||
|
||
**Acceptance criterion:** candidate input ordering and relay response ordering cannot change the selected canonical root.
|
||
|
||
## Phase 3 — Validate signer output and bind seed continuity
|
||
|
||
**Findings addressed:** G56-05, G56-06, G56-09, G56-10.
|
||
|
||
### Implementation instructions
|
||
|
||
1. Add a single signer-validation helper used after every [`window.nostr.signEvent()`](../../www/index.html:1078) call:
|
||
- verify returned object has exact fields,
|
||
- verify no approved template field changed,
|
||
- verify returned pubkey equals authenticated identity,
|
||
- recompute ID,
|
||
- verify Schnorr signature,
|
||
- reject extra/missing semantic fields.
|
||
2. Never reconstruct an event from a mixture of signer-returned and local fields. Validate and retain one exact signed object.
|
||
3. Split onboarding into explicit modes:
|
||
- **Existing mnemonic identity:** entered seed must derive the connected NIP-06 pubkey; otherwise stop.
|
||
- **Raw-nsec identity:** generate a new 24-word seed and require a successor proof over the canonical authorization.
|
||
4. Run a recovery self-test before publication: discard derived objects, rederive from mnemonic, and compare all public keys/deterministic identifiers.
|
||
5. Prefer moving derivation/signing into a dedicated signer API. Define capabilities such as:
|
||
- `pq_get_public_keys(protocolVersion, account)`,
|
||
- `pq_sign_authorization(protocolVersion, digest, algorithms)`,
|
||
- `pq_rotate_keys(...)`.
|
||
6. Until signer support exists:
|
||
- scope secrets locally rather than globals,
|
||
- do not derive the unused secp key except for continuity/proof,
|
||
- overwrite secret typed arrays where possible,
|
||
- clear seed DOM/input immediately after confirmation,
|
||
- avoid clipboard as the primary backup path,
|
||
- warn about extensions/devtools/clipboard managers.
|
||
|
||
### Required tests
|
||
|
||
- signer changes content/tag/timestamp/pubkey,
|
||
- signer returns wrong ID,
|
||
- signer returns invalid signature,
|
||
- connected pubkey changes between requests,
|
||
- existing mnemonic derives wrong identity,
|
||
- raw-nsec successor proof missing/invalid,
|
||
- recovery rederivation mismatch,
|
||
- cancellation at each signer prompt leaves no publishable partial state.
|
||
|
||
**Acceptance criterion:** nothing is persisted or published unless the exact signer-returned object is locally valid and bound to the intended identity/template.
|
||
|
||
## Phase 4 — Make OTS and Bitcoin verification trust-explicit and robust
|
||
|
||
**Findings addressed:** G56-04, G56-13, G56-17, part of G56-08.
|
||
|
||
### Parser hardening
|
||
|
||
In [`OtsReader`](../../www/js/pq-crypto.mjs:1014) and [`parseOtsFile()`](../../www/js/pq-crypto.mjs:1078):
|
||
|
||
1. Set maximum detached-proof size, for example 1 MiB unless evidence supports another bound.
|
||
2. Track and cap:
|
||
- recursion depth,
|
||
- operation count,
|
||
- branches,
|
||
- cumulative append/prepend bytes,
|
||
- cumulative hash input bytes,
|
||
- attestations.
|
||
3. Replace 32-bit bitwise varuint parsing at [`readVaruint()`](../../www/js/pq-crypto.mjs:1032) with safe arithmetic/`BigInt` plus maximum value and canonical-encoding checks.
|
||
4. Require complete stream consumption.
|
||
5. Reject unsupported operation/hash types explicitly.
|
||
6. Fuzz with random, truncated, deeply branched, and overlong inputs.
|
||
|
||
### Bitcoin trust modes
|
||
|
||
Expose one of these explicit modes:
|
||
|
||
- `local-node-verified`: query a user-controlled Bitcoin node and validate block data;
|
||
- `header-chain-verified`: validate raw headers, PoW, linkage, difficulty, chainwork, and checkpoints;
|
||
- `multi-explorer-checked`: compare independent APIs and reject disagreement;
|
||
- `single-explorer-checked`: current behavior, clearly labeled trusted API;
|
||
- `structural-only`: no Bitcoin validity claim.
|
||
|
||
Do not use “cryptographically verified on Bitcoin” for explorer-only modes. Modify [`verifyOtsProof()`](../../www/js/pq-crypto.mjs:1284) to return the trust mode and evidence source.
|
||
|
||
### Upgrade helper
|
||
|
||
1. Add the `/ots-upgrade` server source and deployment config to the repository.
|
||
2. Enforce body size, content type, timeouts, process isolation, rate limits, no arbitrary URI access, and sanitized output.
|
||
3. The browser must still bind and validate returned proofs; never trust the helper’s `confirmed` boolean alone.
|
||
4. Add server unit/integration tests and deployment documentation.
|
||
|
||
**Acceptance criterion:** the UI accurately states the Bitcoin trust model, malicious proofs stay within resource budgets, and a helper/API compromise cannot validate a proof for the wrong digest.
|
||
|
||
## Phase 5 — Fix publishing, relay, and recovery operations
|
||
|
||
**Findings addressed:** G56-11, G56-12, G56-18, G56-20.
|
||
|
||
### Implementation instructions
|
||
|
||
1. Validate relay URLs with `URL`; allow only `wss:` in production and `ws://localhost` under explicit development mode.
|
||
2. Cap relay count, URL length, event size, and concurrent sockets.
|
||
3. Require relay `OK` acceptance for both immutable authorization and proof carrier.
|
||
4. Set a publication policy, for example:
|
||
- at least two independent relays accept the authorization,
|
||
- at least two accept the proof carrier,
|
||
- at least one read-back query returns each exact event.
|
||
5. Keep retry controls after partial failure; never disable publication as “complete” with zero acceptance.
|
||
6. Bind subscription IDs and verify returned events match requested authors/kinds.
|
||
7. Test representative 30–50 KiB events against target relays and document limits.
|
||
8. Export the archive package before/after publish and encourage independent storage.
|
||
9. Add multi-tab locking using `BroadcastChannel` or Web Locks so two tabs cannot race upgrades/publication.
|
||
10. Prevent overlapping OTS polls with an in-flight guard or recursive timeout.
|
||
11. Replace origin-wide `localStorage.clear()`/`sessionStorage.clear()` with deletion of application-owned keys only.
|
||
|
||
**Acceptance criterion:** the app cannot report completion without durable, read-back-confirmed publication and a user-exportable verification package.
|
||
|
||
## Phase 6 — Harden the browser deployment and supply chain
|
||
|
||
**Findings addressed:** G56-08, G56-10, G56-14, G56-15.
|
||
|
||
### Application/deployment instructions
|
||
|
||
1. Move inline JavaScript from [`index.html`](../../www/index.html:580) and [`verify.html`](../../www/verify.html:353) into reviewed module files.
|
||
2. Enforce a CSP without script `'unsafe-inline'`; use hashes/nonces only where unavoidable.
|
||
3. Add:
|
||
- `frame-ancestors 'none'`,
|
||
- HSTS with an appropriate rollout,
|
||
- `X-Content-Type-Options: nosniff`,
|
||
- strict Referrer-Policy,
|
||
- restrictive Permissions-Policy,
|
||
- appropriate COOP/CORP where compatible.
|
||
4. Vendor and audit nostr-login-lite assets or pin reviewed immutable files with release hashes/SRI.
|
||
5. Remove unnecessary permissive CORS headers from static pages/helper responses.
|
||
6. Stop exposing avoidable server version information.
|
||
7. Add deployment configuration and a security-header integration test.
|
||
|
||
### Dependency instructions
|
||
|
||
1. Remove [`package-lock.json`](../../package-lock.json) from [`.gitignore`](../../.gitignore:10), regenerate it so root version matches [`package.json`](../../package.json:3), and commit it.
|
||
2. Use `npm ci` in CI/releases.
|
||
3. Pin security-sensitive dependencies and review upgrades deliberately.
|
||
4. Generate an SBOM and signed provenance with bundle SHA-256.
|
||
5. Repair `.gitmodules` for [`resources/javascript-opentimestamps/`](../../resources/javascript-opentimestamps/) or replace the broken gitlink with a documented immutable test-vector snapshot.
|
||
6. Run tests and advisories in CI; do not claim the old OTS package is integrated when it is only a fixture source.
|
||
|
||
**Acceptance criterion:** a clean clone can reproduce the bundle and tests from tracked metadata, and the deployed asset hashes can be tied to a source revision.
|
||
|
||
## Phase 7 — Documentation and protocol standardization
|
||
|
||
**Findings addressed:** G56-16 and conceptual completeness.
|
||
|
||
1. Rewrite [`nip_proposal.md`](../../nip_proposal.md:1) around the canonical authorization, immutable history, exact verification state machine, trust modes, and rotation semantics.
|
||
2. Clearly separate:
|
||
- implemented,
|
||
- experimental,
|
||
- design-only,
|
||
- required companion standards.
|
||
3. Replace “identity is quantum-resistant” with precise claims about linked keys and anchor status.
|
||
4. Do not claim pending calendar proofs establish an external time.
|
||
5. Publish test vectors:
|
||
- canonical object bytes/digest,
|
||
- BIP39/BIP32 child seeds,
|
||
- PQ public keys/signatures,
|
||
- valid Nostr authorization event,
|
||
- pending and confirmed OTS proof,
|
||
- candidate conflict-resolution examples.
|
||
6. Obtain at least two independent implementations for canonical encoding and verification before standardization.
|
||
7. Seek independent review from Nostr protocol experts, Bitcoin/OTS experts, and PQ cryptographers.
|
||
|
||
**Acceptance criterion:** two independent implementations produce identical vectors and canonical candidate selection.
|
||
|
||
## 5. Finding-to-work mapping
|
||
|
||
| Finding | Primary mitigation phase | Release-blocking? |
|
||
|---|---|---|
|
||
| G56-01 | Phase 1 | Yes |
|
||
| G56-02 | Phase 2 / protocol redesign | Yes |
|
||
| G56-03 | Phase 1 | Yes |
|
||
| G56-04 | Phase 4 | Yes for strong Bitcoin claims |
|
||
| G56-05 | Phase 3 | Yes |
|
||
| G56-06 | Protocol redesign / Phase 3 | Yes |
|
||
| G56-07 | Phase 1 | Yes |
|
||
| G56-08 | Phase 0 and Phase 6 | Yes for real seeds |
|
||
| G56-09 | Phase 0 / Phase 3 | Yes for PQ-strength claim |
|
||
| G56-10 | Phase 3 / Phase 6 | Yes for production web custody |
|
||
| G56-11 | Phase 5 | Yes |
|
||
| G56-12 | Phase 2 / Phase 5 | Yes |
|
||
| G56-13 | Phase 4 | Yes for untrusted proofs |
|
||
| G56-14 | Phase 6 | Yes for reproducible release |
|
||
| G56-15 | Phase 6 | Required for clean clone/tests |
|
||
| G56-16 | Phase 0 / Phase 7 | Yes for public claims |
|
||
| G56-17 | Phase 1 / Phase 4 | No if removed from claims |
|
||
| G56-18 | Phase 5 | No for local research; yes for production |
|
||
| G56-19 | Phase 1 | Yes |
|
||
| G56-20 | Phase 5 / protocol redesign | Yes if target relays reject events |
|
||
|
||
## 6. Test strategy
|
||
|
||
### Unit tests
|
||
|
||
- canonical encoding and rejection of non-canonical forms,
|
||
- strict event/schema validation,
|
||
- exact algorithm policy,
|
||
- signer-return validation,
|
||
- candidate sorting/tie-breaks,
|
||
- persistence binding,
|
||
- OTS parser limits and varints,
|
||
- trust-mode labeling.
|
||
|
||
### Property and fuzz tests
|
||
|
||
- arbitrary JSON/tag shapes never throw,
|
||
- arbitrary OTS bytes stay within CPU/memory/time budgets,
|
||
- candidate ordering does not alter selection,
|
||
- encode/decode round trips preserve canonical bytes,
|
||
- mutation of any signed canonical field invalidates all applicable proofs.
|
||
|
||
### Integration tests
|
||
|
||
- mock signer approves/cancels/mutates events,
|
||
- mock relays reorder, hide, duplicate, reject, and inject events,
|
||
- mock calendars partially fail and return malformed fragments,
|
||
- mock upgrade helper returns wrong-target or oversized proofs,
|
||
- explorer disagreement and outage behavior,
|
||
- resume after every workflow checkpoint,
|
||
- multiple browser tabs.
|
||
|
||
### End-to-end tests
|
||
|
||
Use throwaway identities against controlled relays:
|
||
|
||
1. create 24-word seed,
|
||
2. derive and recover keys,
|
||
3. sign canonical authorization,
|
||
4. publish and read back,
|
||
5. timestamp and upgrade,
|
||
6. verify through each trust mode,
|
||
7. inject a later forged candidate,
|
||
8. confirm earliest valid anchor remains canonical,
|
||
9. rotate using PQ authorization,
|
||
10. verify archive import with the website offline.
|
||
|
||
### Independent vectors
|
||
|
||
At least one second implementation in another language must reproduce the canonical encoding, derivation, signatures where deterministic, event IDs, and selection result. Self-comparison is insufficient.
|
||
|
||
## 7. Release gates
|
||
|
||
### Gate A — Research demo
|
||
|
||
- Phase 0 warnings complete.
|
||
- No real-security claims.
|
||
- Existing and adversarial tests pass.
|
||
- Throwaway identities only.
|
||
|
||
### Gate B — Experimental key commitment
|
||
|
||
- Protocol redesign, Phases 1–5 complete.
|
||
- Strict verification and earliest-anchor selection independently tested.
|
||
- Archive export/import works.
|
||
- Explorer trust is labeled accurately.
|
||
- No valuable mnemonic entry into the web origin.
|
||
|
||
### Gate C — Production pre-commitment registry
|
||
|
||
- Dedicated signer/hardware integration.
|
||
- Phase 6 reproducibility/deployment hardening.
|
||
- Independent implementation and security audit.
|
||
- Kind/format coordination with Nostr community.
|
||
- Durable relay/archive strategy.
|
||
|
||
### Gate D — Complete PQ migration
|
||
|
||
All Gate C requirements plus standardized and deployed:
|
||
|
||
- PQ authentication for future events,
|
||
- PQ key rotation/revocation/recovery,
|
||
- client identity mapping/social-graph behavior,
|
||
- PQ encryption protocol,
|
||
- algorithm deprecation governance,
|
||
- broad client adoption.
|
||
|
||
Only at Gate D should the project claim to make ongoing Nostr identity use post-quantum secure.
|
||
|
||
## 8. Conceptual conclusion
|
||
|
||
The project is **not doomed by its premise**. Pre-quantum authorization plus independently ordered timestamping is a reasonable bootstrap for migration away from a signature algorithm that may later become forgeable. Multi-algorithm commitments can provide useful hedging.
|
||
|
||
The original implementation does, however, conflate three different achievements:
|
||
|
||
1. generating PQ keys,
|
||
2. historically linking those keys to an old identity,
|
||
3. making the identity’s future Nostr activity post-quantum secure.
|
||
|
||
Only the first is currently strong; the second is incomplete; the third requires companion protocols. Implementing this document would make the second defensible and create a valid foundation for the third. It would not eliminate the need to standardize and deploy future PQ event, rotation, encryption, availability, and client identity semantics.
|