@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 +126 -0
- package/README.md +349 -207
- package/bsv-ecies.min.js +1 -1
- package/bsv-gdaf.min.js +62 -62
- package/bsv-ltp.min.js +48 -48
- package/bsv-smartcontract.min.js +1 -1
- package/bsv.bundle.js +62 -62
- package/bsv.min.js +62 -62
- package/docs/AUDIT_SCOPE.md +8 -8
- package/docs/BRC220_BATCH_LEAF_AMENDMENT.md +119 -0
- package/docs/BRC220_ENCODING_AMENDMENT.md +100 -0
- package/docs/BRC220_PLAN.md +233 -0
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +23 -23
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/index.js +5 -0
- package/index.mjs +1 -0
- package/lib/gdaf/attestation-signer.js +2 -38
- package/lib/notaryhash/certificate.js +282 -0
- package/lib/notaryhash/encoding.js +150 -0
- package/lib/notaryhash/index.js +354 -0
- package/lib/notaryhash/merkle.js +217 -0
- package/lib/notaryhash/script.js +261 -0
- package/lib/notaryhash/suites.js +172 -0
- package/lib/util/jcs.js +75 -0
- package/package.json +9 -6
- package/test/notaryhash/batch_leaf.js +140 -0
- package/test/notaryhash/certificate.js +249 -0
- package/test/notaryhash/encoding.js +186 -0
- package/test/notaryhash/interop.js +112 -0
- package/test/notaryhash/merkle.js +181 -0
- package/test/notaryhash/script.js +270 -0
- package/test/notaryhash/verify.js +342 -0
- package/version.js +1 -1
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
|