mirror of
https://github.com/PR0M3TH3AN/bitgate.git
synced 2026-11-08 22:41:08 +00:00
feat(verify): optional NIP-01 signature verification, on by default in the widget
Closes the gap the audit left open: administrative state fails closed without a verifier, and BitGate shipped none. @bitgate/verify is a separate package so core, nostr, and runtime keep no crypto dependency and can be audited without one. The widget bundles it, so the drop-in path is verified with no extra step; verify="off" disables it, sets data-unverified on the element, and emits bitgate:unverified — meant for local development, and meant to be visible. Verification recomputes the event id before checking the signature. Checking only the signature would let a forged event pass by reusing a genuine signature over an unrelated id, which is the failure mode that matters here. createVerifier also rejects events dated far in the future. A far-future created_at wins replaceable-event selection indefinitely and pins stale state in place; the signature is perfectly valid, so only a clock check catches it. Found while testing: #adminIngestAllowed checked that a verifier *exists*, not that this event was verified — so the public ingestEvent path, which "bring your own relays" integrations use, bypassed verification entirely. Verification now runs inside ingestEvent, with an already-verified flag for the loaders. An async verifier cannot be awaited there and is treated as unverified; ingestVerified() awaits properly. Conformance validated against 120 live events from a public relay across kinds 0, 1, 3 and 10002 — 59 with non-ASCII content and 71 containing quotes, newlines or tabs, which is where a serialization mismatch would surface. All 120 verified, zero id mismatches. Bundle grows 72K -> 120K. 855 tests; audit clean.
This commit is contained in:
1 parent
94ef6a72f5
commit
3b95a63058
14 files changed
+596
-12
No files matched your search
@@ -0,0 +1,65 @@
|
||||
# @bitgate/verify
|
||||
|
||||
NIP-01 event signature verification for BitGate. Optional package, so the rest
|
||||
of BitGate carries no crypto dependency and can be audited without one.
|
||||
|
||||
```bash
|
||||
npm install @bitgate/verify
|
||||
```
|
||||
|
||||
```js
|
||||
import { createBitGate } from "@bitgate/runtime";
|
||||
import { createVerifier } from "@bitgate/verify";
|
||||
|
||||
const runtime = createBitGate({
|
||||
root: ROOT_PUBKEY,
|
||||
verifySignature: createVerifier(),
|
||||
…
|
||||
});
|
||||
```
|
||||
|
||||
`<bitgate-provider>` uses this automatically — the widget bundle includes it, so
|
||||
the drop-in path is verified without any extra step.
|
||||
|
||||
## What verification means here
|
||||
|
||||
Two checks, and both matter:
|
||||
|
||||
1. **The event id is the hash of the event's own contents.** Without this, an
|
||||
attacker could take a genuine signature over one id and attach it to
|
||||
different content.
|
||||
2. **The signature is valid for that id under the claimed pubkey.**
|
||||
|
||||
Checking only the signature would let a forged event pass by reusing a real
|
||||
signature over an unrelated id. `verifyEvent` recomputes the id and refuses on
|
||||
mismatch before touching the curve.
|
||||
|
||||
## Clock skew
|
||||
|
||||
`createVerifier({ maxFutureSeconds })` (default 900) rejects events dated
|
||||
further ahead than the allowance. A far-future `created_at` wins
|
||||
replaceable-event selection indefinitely and pins stale state in place — the
|
||||
signature is perfectly valid, so only a clock check catches it. Pass `0` to
|
||||
disable.
|
||||
|
||||
## It never throws
|
||||
|
||||
Malformed input, bad hex, and curve errors are all "not verified". A verifier
|
||||
that threw would be a denial-of-service vector: one hostile event would break
|
||||
the whole ingestion batch.
|
||||
|
||||
## Conformance
|
||||
|
||||
Validated against **120 live events** from a public relay across kinds 0, 1, 3,
|
||||
and 10002 — including 59 with non-ASCII content and 71 containing quotes,
|
||||
newlines, or tabs, which is where serialization mismatches would surface. All
|
||||
120 verified with zero id mismatches.
|
||||
|
||||
## API
|
||||
|
||||
| Export | Purpose |
|
||||
| --- | --- |
|
||||
| `createVerifier(options)` | Build a verifier for `verifySignature` |
|
||||
| `verifyEvent(event)` | Verify one event; never throws |
|
||||
| `computeEventId(event)` | NIP-01 event id |
|
||||
| `isVerifiable(event)` | Structural check, before any crypto |
|
||||
Reference in new issue
Block a user