Class NostrEventSignatures
Why this is here rather than in the library
nostr-java signs events but ships no verifier, so a reader has nothing to call. The voucher
ledger read path had a corresponding hole: it trusted the pubkey field and the status
carried by whatever the relay returned (audit H-13). A relay is an untrusted transport, and
the status is what the online double-spend check depends on, so a hostile relay could flip a
REDEEMED voucher back to ACTIVE and enable a second redemption.
What NIP-01 requires
The event id is the SHA-256 of a canonical serialisation of the event, and
sig is a BIP-340 Schnorr signature over those 32 id bytes by the pubkey.
Why the id must be recomputed
An earlier version of this class took the id straight off the wire and checked only that the signature matched it, on the argument that callers also pin the author. That argument is wrong, and the two checks are orthogonal: pinning the author says who signed, recomputing the id says what they signed.
The ledger is public, so a genuine issuer event for a voucher is world-readable. An attacker
copies its id, sig and pubkey verbatim onto an event whose tags say
status=ACTIVE and whose created_at is bumped. Every field the old check looked
at is genuine, so it verified; the status and content, which no check covered, are the
attacker's. Because the read path takes the newest authentic event, the forgery wins, and a
REDEEMED voucher is spendable again. That is the double-spend this class exists to prevent.
So the id is recomputed from pubkey, created_at, kind, tags
and content and compared against the carried id before the signature is checked. The
computation is nostr-java's own EventSerializer, not a reimplementation, so it cannot
drift from the form the publish path produces. Note that GenericEvent.update() is
unusable here: it overwrites created_at with the current time, which would both destroy
the field being authenticated and make the check trivially pass.
-
Method Summary
Modifier and TypeMethodDescriptionstatic booleanverify(@NonNull nostr.event.impl.GenericEvent event) Whether the event's id matches its contents and its signature is a valid BIP-340 signature over that id by its author.
-
Method Details
-
verify
public static boolean verify(@NonNull @NonNull nostr.event.impl.GenericEvent event) Whether the event's id matches its contents and its signature is a valid BIP-340 signature over that id by its author.- Parameters:
event- the event to check- Returns:
- true when the event is authentic in both senses
-