{"id":"53dc1943ddf0faa69d5d4ce0aa30e6f20ac8ef908d7c4a8a9aeea427bf6185fa","pubkey":"da19f1cd34beca44be74da4b306d9d1dd86b6343cef94ce22c49c6f59816e5bd","created_at":1786281055,"kind":30817,"tags":[["d","nip-nonce-commitment"],["title","NIP-NONCE-COMMITMENT: Host-Committed Nonces for NIP-46 Remote Signing"]],"content":"# NIP-NONCE-COMMITMENT: Host-Committed Nonces for NIP-46 Remote Signing\n\n`draft` `optional`\n\nTwo additional NIP-46 methods, `sign_event_commit` and `sign_event_reveal`,\nthat let a client force a remote signer to commit to its BIP-340 signing nonce\nbefore the client reveals randomness of its own. The final nonce is a function\nof both, so a signer cannot choose its nonces freely, and the covert channel by\nwhich malicious signer firmware could leak key material through signature\nnonces is reduced from a broadcast to, at worst, a low-rate abort channel.\n\nThe key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are used as defined in\nRFC 2119.\n\n## Motivation\n\nA NIP-46 signer is trusted with an nsec precisely so that nothing else has to\nbe. That trust has one channel left open: the signature itself. BIP-340 lets a\nsigner add 32 bytes of auxiliary randomness to its nonce derivation, and\nnothing in the protocol constrains that choice. Malicious firmware can grind\n`aux_rand` until the resulting signature encodes a few chosen bits, and leak an\nentire seed over a few dozen perfectly valid signatures. The user sees normal\nevents, normally signed. No log, no display, no policy engine notices anything.\n\nThis is not hypothetical caution. The hardware wallet world has already been\nthrough it: Blockstream's Anti-Exfil protocol exists because a compromised\nsigning device leaking through nonces was judged a realistic threat for\nBitcoin, and hardware Nostr signers now hold keys of comparable value to their\nowners. Open source and reproducible builds shrink the window for malicious\nfirmware but cannot close it; a verifier can only check the build they\nverified, not the one that shipped.\n\nSign-to-contract closes the channel cryptographically. The signer commits to\nits nonce point before it learns randomness the client contributes; the final\nnonce is the committed point tweaked by a hash of both. The signer cannot\ngrind what it cannot predict.\n\n## Protocol\n\nBoth methods travel as ordinary NIP-46 requests inside kind 24133 envelopes.\n`P` is the signer's x-only public key, `m` the event id to be signed, `n` the\nsecp256k1 group order, and `G` its generator. `TaggedHash` is as defined in\nBIP-340.\n\n### Round one: `sign_event_commit`\n\nThe client draws 32 fresh random bytes `rho` and sends its hash, keeping `rho`\nsecret:\n\n```json\n{\n  \"id\": \"<request-id>\",\n  \"method\": \"sign_event_commit\",\n  \"params\": [\"<event-json>\", \"<hex SHA256(rho)>\"]\n}\n```\n\nThe signer derives its nonce `k0` per BIP-340 nonce derivation, with the\nreceived commitment `t` included in the hashed input alongside the message, so\nthat `k0` is bound to both. It computes `R0 = k0·G` and replies with the\n33-byte compressed encoding:\n\n```json\n{\n  \"id\": \"<request-id>\",\n  \"result\": \"{\\\"commitment\\\": \\\"<hex R0>\\\", \\\"ref\\\": \\\"<opaque>\\\"}\"\n}\n```\n\nThe signer MUST NOT produce a signature for `m` at this stage, MUST bind the\npending state to `(m, t)`, and MUST discard the pending state after a timeout\n(60 seconds is RECOMMENDED) or after a single reveal attempt, successful or\nnot. `k0` MUST be fresh per request and MUST never be reused.\n\n### Round two: `sign_event_reveal`\n\nThe client reveals its randomness:\n\n```json\n{\n  \"id\": \"<request-id-2>\",\n  \"method\": \"sign_event_reveal\",\n  \"params\": [\"<ref>\", \"<hex rho>\"]\n}\n```\n\nThe signer verifies `SHA256(rho) = t` for the pending request, and MUST refuse\nwith an error otherwise. It then computes\n\n```\ne_c = int(TaggedHash(\"NonceCommit/tweak\", bytes(R0) || rho)) mod n\nk   = (k0 + e_c) mod n\n```\n\nand completes an otherwise standard BIP-340 signature for `m` using `k` as the\nsecret nonce (including the even-Y normalisation BIP-340 requires). The result\nis the signed event, exactly as `sign_event` would return it.\n\n### Client verification\n\nThe signature is valid BIP-340; any verifier accepts it. The client that ran\nthe protocol additionally checks the commitment:\n\n```\nr == x(R0 + e_c·G)\n```\n\nwhere `r` is the first 32 bytes of the signature and `e_c` is recomputed from\n`R0` and `rho`. Negation for even-Y does not change the x-coordinate, so the\nequation holds for a compliant signer regardless of parity. If the check\nfails, the client MUST treat the signer as malicious: discard the signature,\nrefuse further requests over that connection, and surface the failure to the\nuser. A commitment mismatch is not an error condition to retry; it is the\nattack this protocol exists to catch.\n\n## What this does and does not achieve\n\nThe signer chooses `k0` knowing `t` but not `rho`, so it cannot predict the\nfinal nonce at commitment time; grinding signatures into a covert channel\nstops working. What remains is an abort channel: a malicious signer that\ndislikes the final nonce can refuse to answer the reveal, leaking at most a\nfraction of a bit per interaction at the cost of visible failures. This is the\nsame residual accepted by Anti-Exfil, and no interactive scheme does better\nwithout a second signing party.\n\nThe protection is only as good as the client's `rho`. A client whose\nrandomness the signer can predict has rebuilt the original problem, and a\nclient that reuses `rho` MUST be considered broken.\n\nThreshold signing (FROST-style bunkers) also removes unilateral nonce choice,\nby ensuring no single party holds the key at all. The two approaches serve\ndifferent deployments: threshold schemes need multiple keyholders and a\ncoordination protocol, while this one protects the common case of a single\nhardware signer with two extra messages and no new keys.\n\nClients MUST fall back to plain `sign_event` when a signer does not implement\nthese methods, and SHOULD indicate to the user that nonce commitment is not in\neffect. A signer that advertises the methods and then fails commitment checks\nis worse than one that never offered them; see above.\n\n## Costs\n\nOne extra round trip per signature. Over relays this doubles signing latency,\nwhich is material for interactive use and immaterial for anything batched;\nover local transports (USB-bridged signers, LAN bunkers) it is microseconds.\nClients MAY reserve the protocol for high-value events and MAY apply it per\nconnection rather than per signature. Signers carry one pending `(m, t, k0)`\nper in-flight request; the state is small, bounded and expiring.\n\n## Reference implementation\n\nPlanned for Heartwood (heartwood-esp32) as a `heartwood_`-prefixed method pair\nfirst, promoted to the unprefixed names here if adopted more widely. The\nprotocol requires nothing exotic of a signer: one point multiplication and one\ntagged hash beyond a standard BIP-340 signature.\n","sig":"a4d37586cbd34ac7dd980f96263c5eadb2bf0de7b79fe9fad6153154018eeab53812c5e2c87b1989448a0b63ab76156ba88decb0b8fb5093f7bfcce4b0ca3d62"}