@smartledger/bsv 9.20.0 → 9.22.0

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,109 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.22.0] - 2026-10-06
11
+
12
+ ### Security — the membership commitment was over a non-injective encoding
13
+
14
+ 9.20.0 fixed the headline forgery in `ZKProver.verifyMembershipProof` — a verifier that compared
15
+ only prover-supplied values with each other — and **left a second forgery in place.** The
16
+ commitment was `sha256(JSON.stringify(member) + ':' + salt)`, and `JSON.stringify` is not
17
+ injective:
18
+
19
+ ```js
20
+ JSON.stringify(null) === JSON.stringify(NaN) === JSON.stringify(Infinity) // '"null"'
21
+ JSON.stringify({ a: 1, b: undefined }) === JSON.stringify({ a: 1 }) // '{"a":1}'
22
+ ```
23
+
24
+ So a prover could claim `NaN` was a member of `[null]`, or `{a:1,b:undefined}` a member of
25
+ `[{a:1}]`, and **every recomputed commitment matched.** Key order was the mirror of the same flaw:
26
+ two spellings of one object committed differently, so a legitimate caller could be refused.
27
+
28
+ Members and claimed values are now encoded with **RFC 8785 canonical JSON** (`bsv.JCS`, already in
29
+ this library) over a value domain validated recursively — null, boolean, finite number, string,
30
+ array, and plain objects of those. JCS sorts keys and refuses non-finite numbers and `undefined`
31
+ outright; the recursive check adds the one case JCS does not catch, an `undefined` **property
32
+ value**, which it drops exactly as `JSON.stringify` does. Anything outside the domain throws, and
33
+ the verifier answers `false` — the honest verdict for a value this construction cannot commit to
34
+ unambiguously.
35
+
36
+ `generateMembershipProof` also no longer refuses object members. It compared with
37
+ `set.includes(value)`, which is reference equality, so generating a proof for a structurally equal
38
+ object threw `Value not in set` and the function was unusable with object members at all.
39
+ Membership is now structural, over the same canonical form the commitment uses.
40
+
41
+ **Found by red-teaming the 9.20.0 fix, not by a test.** That is the second time in two releases
42
+ that a fix closed the stated problem and left an adjacent one standing, and both were found by
43
+ someone looking at the fix rather than at the original report.
44
+
45
+ ### Still open, and stated rather than fixed
46
+
47
+ An external review of these releases raised three limits worth recording, none of which this
48
+ release closes:
49
+
50
+ - **A `null` deletes a claim rather than editing it.** `anchor.blockTime` and `anchor.blockHeight`
51
+ are checked only when non-null, because a certificate issued before confirmation legitimately
52
+ carries nulls. So a tamper can *remove* a field to avoid its check. A strict mode that requires
53
+ them, or a verdict that distinguishes "not verified" from "verified", is the proper answer.
54
+ - **Several checks establish consistency, not authenticity.** `anchor.blockHeight` equalling
55
+ `spv.blockHeight` can be satisfied by editing both; `createdAt` being canonically formatted says
56
+ nothing about when the certificate was issued; `opts.network` compares a name. These bound what a
57
+ tamper can do without a trusted chain source — they do not authenticate the claims.
58
+ - **The TSC `index < 2^nodes.length` bound does not prove the index is below the real transaction
59
+ count.** A tighter bound needs a trustworthy count, which the certificate does not carry.
60
+
61
+ ## [9.21.0] - 2026-10-05
62
+
63
+ ### Added — `opts.network` on `NotaryHash.verify`, opt-in
64
+
65
+ `anchor.network` is the last field in a certificate that no signature covers. A mainnet certificate
66
+ relabelled `"bsv-testnet"` verified, and so did one relabelled `"not-a-chain"`.
67
+
68
+ BRC-220 calls the field descriptive — the headers decide the chain, not the label — so accepting
69
+ any value is conformant, and this check is therefore **opt-in**: a default would break every
70
+ testnet caller for no gain. Pass `opts.network` and a certificate naming another chain is refused
71
+ by name; omit it and nothing changes.
72
+
73
+ Worth exactly what it claims: **it compares a label against the caller's own setting, and proves
74
+ nothing about which chain the header source serves.** A caller who needs that must obtain headers
75
+ from a source it trusts for the chain it means — the same reason this library never fetches one.
76
+
77
+ Raised by the NotaryHash SDK session after a third session relabelled a mainnet certificate and
78
+ both verifiers accepted it. Their verifier takes the same option, so the two agree.
79
+
80
+ ### Documented — 8.3.1 was a signature-convention break, and it orphaned certificates
81
+
82
+ **If you anchored NotaryHash certificates with 8.3.0, some of them can no longer be verified by any
83
+ later version, and there was no symptom until the fix landed.**
84
+
85
+ 8.3.0 reversed the payload digest in **both** the signer and the verifier. The two cancelled, so
86
+ certificates it produced verified perfectly under 8.3.0. 8.3.1 corrected both halves at once —
87
+ which was right — and in doing so made every 8.3.0-signed certificate unverifiable, because the
88
+ signature is genuinely over `reverse(payloadHash)`.
89
+
90
+ Measured on five mainnet certificates, three library versions, nothing else changed:
91
+
92
+ | | 8.3.0 | 8.3.1 | 9.18–9.21 |
93
+ |---|---|---|---|
94
+ | four certificates signed 2026-08-16 | **verify** | fail | fail |
95
+ | one control signed 2026-08-20 | fail | **verify** | **verify** |
96
+
97
+ The two eras are mutually unverifiable, which is only possible if both sides of 8.3.0 reversed.
98
+ Confirmed four ways: in Python with no SDK, through `NotaryHash.verify`, through
99
+ `crypto.ECDSA.verify` directly, and here against the reporter's committed fixture — the four
100
+ failing certificates verify over `reverse(payloadHash)` and not over `payloadHash`, and the control
101
+ is the exact inverse.
102
+
103
+ **No reversing path will be added.** Accepting two derivations of one value is how the 8.3.0–9.8.0
104
+ certificate-format split happened, and a compatibility mode would reintroduce it. These
105
+ certificates also cannot be rescued by re-anchoring: the signature does not survive, so the key
106
+ holder would have to sign again — a new statement made today, not a restoration of an old one. The
107
+ remedy is to label them with the version that verifies them.
108
+
109
+ Recorded because the general shape is worth more than the incident: **a convention that is wrong
110
+ but self-consistent leaves no symptom until the day it is corrected.** Reported and measured by the
111
+ ordinals session that holds the affected certificates.
112
+
10
113
  ## [9.20.0] - 2026-10-05
11
114
 
12
115
  A security release. One verifier accepted a forged proof outright; another accepted eight tampered
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Bitcoin SV library with an interpreter-verified script engine.
4
4
 
5
- [![Version](https://img.shields.io/badge/version-9.20.0-blue.svg)](https://www.npmjs.com/package/@smartledger/bsv)
5
+ [![Version](https://img.shields.io/badge/version-9.22.0-blue.svg)](https://www.npmjs.com/package/@smartledger/bsv)
6
6
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
7
  [![Stability](https://img.shields.io/badge/9.x%20stable%20until-2027--09--01-brightgreen.svg)](STABILITY.md)
8
8
 
@@ -155,44 +155,44 @@ const bsv = require('@smartledger/bsv') // 128 modules
155
155
  ### **Core Modules**
156
156
  | Module | Size | Use Case | CDN |
157
157
  |--------|------|----------|-----|
158
- | **bsv.min.js** | 1068KB | Core BSV + SmartContract | `unpkg.com/@smartledger/bsv@9.20.0/bsv.min.js` |
159
- | **bsv.bundle.js** | 1068KB | Everything in one file | `unpkg.com/@smartledger/bsv@9.20.0/bsv.bundle.js` |
158
+ | **bsv.min.js** | 1069KB | Core BSV + SmartContract | `unpkg.com/@smartledger/bsv@9.22.0/bsv.min.js` |
159
+ | **bsv.bundle.js** | 1069KB | Everything in one file | `unpkg.com/@smartledger/bsv@9.22.0/bsv.bundle.js` |
160
160
 
161
161
  ### **W3C Verifiable Credentials**
162
162
  | Module | Size | Use Case | CDN |
163
163
  |--------|------|----------|-----|
164
- | **🟢 bsv-didweb.min.js** | 166KB | **DID:web generation** | `unpkg.com/@smartledger/bsv@9.20.0/bsv-didweb.min.js` |
165
- | **🟢 bsv-vcjwt.min.js** | 166KB | **VC-JWT issue/verify** | `unpkg.com/@smartledger/bsv@9.20.0/bsv-vcjwt.min.js` |
166
- | **🟢 bsv-statuslist.min.js** | 256KB | **StatusList2021 revocation** | `unpkg.com/@smartledger/bsv@9.20.0/bsv-statuslist.min.js` |
167
- | **🟢 bsv-anchor.min.js** | 164KB | **BSV anchoring (hash-only)** | `unpkg.com/@smartledger/bsv@9.20.0/bsv-anchor.min.js` |
164
+ | **🟢 bsv-didweb.min.js** | 166KB | **DID:web generation** | `unpkg.com/@smartledger/bsv@9.22.0/bsv-didweb.min.js` |
165
+ | **🟢 bsv-vcjwt.min.js** | 166KB | **VC-JWT issue/verify** | `unpkg.com/@smartledger/bsv@9.22.0/bsv-vcjwt.min.js` |
166
+ | **🟢 bsv-statuslist.min.js** | 256KB | **StatusList2021 revocation** | `unpkg.com/@smartledger/bsv@9.22.0/bsv-statuslist.min.js` |
167
+ | **🟢 bsv-anchor.min.js** | 164KB | **BSV anchoring (hash-only)** | `unpkg.com/@smartledger/bsv@9.22.0/bsv-anchor.min.js` |
168
168
 
169
169
  ### **Smart Contract & Development**
170
170
  | Module | Size | Use Case | CDN |
171
171
  |--------|------|----------|-----|
172
- | **bsv-smartcontract.min.js** | 141KB | Complete covenant framework | `unpkg.com/@smartledger/bsv@9.20.0/bsv-smartcontract.min.js` |
173
- | **bsv-covenant.min.js** | 35KB | Covenant operations | `unpkg.com/@smartledger/bsv@9.20.0/bsv-covenant.min.js` |
174
- | **bsv-script-helper.min.js** | 33KB | Custom script tools | `unpkg.com/@smartledger/bsv@9.20.0/bsv-script-helper.min.js` |
175
- | **bsv-security.min.js** | 32KB | Security enhancements | `unpkg.com/@smartledger/bsv@9.20.0/bsv-security.min.js` |
172
+ | **bsv-smartcontract.min.js** | 141KB | Complete covenant framework | `unpkg.com/@smartledger/bsv@9.22.0/bsv-smartcontract.min.js` |
173
+ | **bsv-covenant.min.js** | 35KB | Covenant operations | `unpkg.com/@smartledger/bsv@9.22.0/bsv-covenant.min.js` |
174
+ | **bsv-script-helper.min.js** | 33KB | Custom script tools | `unpkg.com/@smartledger/bsv@9.22.0/bsv-script-helper.min.js` |
175
+ | **bsv-security.min.js** | 32KB | Security enhancements | `unpkg.com/@smartledger/bsv@9.22.0/bsv-security.min.js` |
176
176
 
177
177
  ### **Legal & Compliance**
178
178
  | Module | Size | Use Case | CDN |
179
179
  |--------|------|----------|-----|
180
- | **bsv-ltp.min.js** | 544KB | Legal Token Protocol | `unpkg.com/@smartledger/bsv@9.20.0/bsv-ltp.min.js` |
181
- | **bsv-gdaf.min.js** | 1068KB | Digital Identity & Attestation | `unpkg.com/@smartledger/bsv@9.20.0/bsv-gdaf.min.js` |
180
+ | **bsv-ltp.min.js** | 544KB | Legal Token Protocol | `unpkg.com/@smartledger/bsv@9.22.0/bsv-ltp.min.js` |
181
+ | **bsv-gdaf.min.js** | 1069KB | Digital Identity & Attestation | `unpkg.com/@smartledger/bsv@9.22.0/bsv-gdaf.min.js` |
182
182
 
183
183
  ### **Advanced Cryptography**
184
184
  | Module | Size | Use Case | CDN |
185
185
  |--------|------|----------|-----|
186
- | **bsv-shamir.min.js** | 177KB | Threshold Cryptography | `unpkg.com/@smartledger/bsv@9.20.0/bsv-shamir.min.js` |
186
+ | **bsv-shamir.min.js** | 177KB | Threshold Cryptography | `unpkg.com/@smartledger/bsv@9.22.0/bsv-shamir.min.js` |
187
187
 
188
188
  ### **Utilities**
189
189
  | Module | Size | Use Case | CDN |
190
190
  |--------|------|----------|-----|
191
- | **bsv-ecies.min.js** | 139KB | Encryption | `unpkg.com/@smartledger/bsv@9.20.0/bsv-ecies.min.js` |
192
- | **bsv-message.min.js** | 34KB | Message signing | `unpkg.com/@smartledger/bsv@9.20.0/bsv-message.min.js` |
193
- | **bsv-mnemonic.min.js** | 320KB | HD wallets | `unpkg.com/@smartledger/bsv@9.20.0/bsv-mnemonic.min.js` |
191
+ | **bsv-ecies.min.js** | 139KB | Encryption | `unpkg.com/@smartledger/bsv@9.22.0/bsv-ecies.min.js` |
192
+ | **bsv-message.min.js** | 34KB | Message signing | `unpkg.com/@smartledger/bsv@9.22.0/bsv-message.min.js` |
193
+ | **bsv-mnemonic.min.js** | 320KB | HD wallets | `unpkg.com/@smartledger/bsv@9.22.0/bsv-mnemonic.min.js` |
194
194
  ```html
195
- <script src="https://unpkg.com/@smartledger/bsv@9.20.0/bsv.min.js"></script>
195
+ <script src="https://unpkg.com/@smartledger/bsv@9.22.0/bsv.min.js"></script>
196
196
  <script>
197
197
  const key = bsv.PrivateKey.fromRandom()
198
198
  </script>
@@ -240,4 +240,4 @@ MIT
240
240
 
241
241
  ---
242
242
 
243
- **SmartLedger-BSV v9.20.0**
243
+ **SmartLedger-BSV v9.22.0**