@smartledger/bsv 8.2.0 → 8.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,132 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [8.3.1] - 2026-08-20
11
+
12
+ ### Fixed — BRC-220 signature verification was not interoperable
13
+
14
+ `lib/notaryhash/suites.js` set `endian: 'little'` before verifying, which made
15
+ ECDSA reverse the 32-byte `payloadHash` before reducing it to a scalar. That is
16
+ Bitcoin's message-signing convention, not this protocol's.
17
+
18
+ The effect was on the **verify** side. A signature produced the way BRC-220
19
+ describes — over the `payloadHash` directly — was **rejected**, and the only
20
+ signatures accepted were ones made with bsv's own byte-reversed convention. In
21
+ practice 8.3.0 could not verify a certificate from any other implementation.
22
+
23
+ Signing was never affected: `ECDSA.sign(payloadHash, key)` already produced a
24
+ conformant signature. Only the verifier disagreed with it.
25
+
26
+ **If you issued certificates with 8.3.0**, they are fine — the signatures in them
27
+ are whatever your signer produced. If you signed via `ECDSA` with
28
+ `endian: 'little'` to satisfy the old verifier, those signatures are
29
+ non-conformant and must be re-issued; 8.3.1 rejects them, deliberately, because
30
+ accepting both conventions would mean two valid signatures exist for one signing
31
+ act and both are inside `proofHash`.
32
+
33
+ ### Why the tests did not catch it
34
+
35
+ Every NotaryHash test signed through `lib/crypto/ecdsa.js` and verified through a
36
+ suite that used the same file, so the module and its tests were self-consistent
37
+ and wrong together — the failure shape `test/notaryhash/encoding.js` warns about
38
+ in its own header comment.
39
+
40
+ `test/notaryhash/interop.js` is new and verifies against `@noble/curves`, which
41
+ shares no verification code with ours. Reverting the one-line fix fails 9 tests.
42
+ It also records the trap that made this slow to diagnose: noble v2 **prehashes by
43
+ default**, so `secp256k1.sign(digest, key)` signs `sha256(digest)` and looks
44
+ self-consistent while disagreeing with everyone; every call in that file passes
45
+ `{ prehash: false }`.
46
+
47
+ ### Documentation
48
+
49
+ - The README's NotaryHash example now shows the signing step explicitly, and says
50
+ why no `endian` option belongs there.
51
+
52
+ ## [8.3.0] - 2026-08-16
53
+
54
+ ### BRC-220 (NotaryHash)
55
+
56
+ `bsv.NotaryHash` implements [BRC-220](https://github.com/bitcoin-sv/BRCs/blob/master/apps/0220.md)
57
+ — privacy-preserving signed-hash notarization with SPV-verifiable certificates. A signer
58
+ proves they signed a specific hash; the on-chain anchor fixes that proof in time; the
59
+ document itself is never disclosed.
60
+
61
+ Four of the five things the spec needs already existed here, which is why it lives in this
62
+ library rather than a separate package: RFC 8785 canonicalization (added in 8.2.0 for an
63
+ unrelated reason), SPV inclusion proofs in TSC format, a header-first trust model that
64
+ already refused to take a provider's word, and `OP_FALSE OP_RETURN` with length-prefixed
65
+ binary.
66
+
67
+ ```js
68
+ var report = bsv.NotaryHash.verify(certificate, { header: independentlyObtainedHeader })
69
+ // { valid, signature, proofIntegrity, anchor, batchInclusion, errors }
70
+ ```
71
+
72
+ All three of the spec's validity checks, plus a fourth for batched certificates:
73
+
74
+ 1. **Signature** — offline, against the registered suite
75
+ 2. **Proof integrity** — the recomputed `proofHash` matches the certificate
76
+ 3. **Anchor** — SPV, against a block header the *caller* supplies
77
+ 4. **Batch inclusion** — the proof is one the on-chain root commits to
78
+
79
+ `verify()` returns a report naming which check failed, because a bad signature and an
80
+ unmined transaction need different fixes. `isValid()` is the strict boolean for `if (...)`.
81
+
82
+ **This library never fetches a block header.** The spec says the verifier trusts only a
83
+ header "obtained from any source it chooses"; quietly choosing one on the caller's behalf
84
+ would restore exactly the trust the protocol removes. `verifyAnchorSPV` refuses to pass
85
+ without one.
86
+
87
+ **Post-quantum is deliberately not bundled.** The spec names ML-DSA and SLH-DSA, but
88
+ `ECDSA-secp256k1` is first-class alongside them, so an ECDSA-only implementation is
89
+ conformant. `@noble/post-quantum` is the one Noble package with no independent audit — its
90
+ README says so — and is 0.x, 669 KB, and does not claim constant-time execution. Depending
91
+ on it would forfeit the "primitives are already audited" property that `docs/AUDIT_SCOPE.md`
92
+ relies on. Suites are registered instead:
93
+
94
+ ```js
95
+ bsv.NotaryHash.registerSuite('ML-DSA-65', { verify: function (hash, sig, key) { … } })
96
+ ```
97
+
98
+ An unregistered algorithm returns **false** — it does not fall through to a default, which
99
+ for an `ML-DSA-65` certificate would be catastrophic. A suite's result is coerced with
100
+ `=== true`, so one returning a truthy object cannot smuggle a pass through.
101
+
102
+ **`encoding` is `"raw"`.** The spec requires the field but never enumerates its values, so
103
+ this library defines them and proposes the definition upstream in
104
+ `docs/BRC220_ENCODING_AMENDMENT.md`. For `ECDSA-secp256k1` that is 64 bytes of `r ‖ s`,
105
+ because `proofHash` covers the signature bytes and DER is not canonical — measured over 200
106
+ signatures from one key, DER came out at 69, 70 and 71 bytes, all of it legal. Low-S is
107
+ required and rejected rather than normalised, since normalising changes bytes that are
108
+ inside `proofHash`.
109
+
110
+ **Batch mode uses RFC 6962**, which is a *third* Merkle tree in this repository and differs
111
+ from both others — `lib/spv` is Bitcoin's (double-SHA, rightmost leaf duplicated) and
112
+ `lib/gdaf/zk-prover.js` is single-SHA without domain separation. RFC 6962 domain-separates
113
+ (`leaf = SHA256(0x00‖d)`, `node = SHA256(0x01‖l‖r)`) and never duplicates. All six published
114
+ Certificate Transparency roots match.
115
+
116
+ Design decisions are recorded in `docs/BRC220_PLAN.md`, including two open questions the
117
+ spec does not answer: what a batch leaf contains (`proofHash` is the reading taken), and
118
+ confirmation against the reference implementation's golden vector.
119
+
120
+ ### Dependencies
121
+
122
+ `@noble/curves`, `@noble/hashes` and `@noble/ciphers` to 2.3.0. These are the signing and
123
+ hashing primitives, so the evidence is a differential rather than a green suite: 1,638
124
+ observable outputs were captured on 2.2.0 and recomputed on 2.3.0 — hashes over every input
125
+ length 0–200, HMACs across four key shapes, 40 keys' worth of RFC-6979 signatures,
126
+ addresses, DER encodings, BIP-32 derivations and point multiplication. All identical. The
127
+ comparison was confirmed capable of failing: perturbing sha256 by one byte moves 695 lines.
128
+
129
+ ### Also
130
+
131
+ RFC 8785 canonicalization moves from `lib/gdaf/attestation-signer.js` to `lib/util/jcs.js`,
132
+ since two unrelated modules now need it and a notarization module reaching into the
133
+ credentials module would be the wrong direction. `attestation-signer` delegates; behaviour
134
+ unchanged.
135
+
10
136
  ## [8.2.0] - 2026-08-15
11
137
 
12
138
  Two independent reviews of 8.1.0, both acted on after reproducing every claim. Between