@smartledger/bsv 7.11.0 → 7.13.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 +135 -0
- package/README.md +38 -38
- package/bsv-anchor.min.js +11 -17
- package/bsv-didweb.min.js +11 -17
- package/bsv-gdaf.min.js +69 -75
- package/bsv-ltp.min.js +55 -61
- package/bsv-mnemonic.min.js +11 -17
- package/bsv-shamir.min.js +11 -17
- package/bsv-smartcontract.min.js +1 -1
- package/bsv-statuslist.min.js +53 -59
- package/bsv-vcjwt.min.js +11 -17
- package/bsv.bundle.js +69 -75
- package/bsv.min.js +69 -75
- package/build/esbuild/crypto-shim.js +35 -0
- package/build/esbuild.js +7 -3
- package/docs/AUDIT_SCOPE.md +198 -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 +10 -2
- package/lib/crypto/hash.browser.js +13 -5
- package/lib/crypto/hash.js +58 -2
- package/lib/crypto/random.js +72 -15
- package/lib/didweb/index.js +88 -9
- package/lib/encoding/base58.js +67 -3
- package/lib/mnemonic/mnemonic.js +19 -5
- package/lib/mnemonic/pbkdf2.js +35 -2
- package/lib/script/interpreter.js +21 -2
- package/lib/transaction/input/multisig.js +1 -1
- package/lib/transaction/input/multisigscripthash.js +1 -1
- package/lib/transaction/input/publickey.js +1 -1
- package/lib/transaction/input/publickeyhash.js +1 -1
- package/lib/transaction/signature.js +1 -1
- package/lib/util/inherits.js +37 -0
- package/package.json +16 -9
- package/test/build/bundle_crypto_shim.js +104 -0
- package/test/build/bundle_externals.js +6 -1
- package/test/crypto/backend_selection.js +126 -0
- package/test/crypto/random.js +73 -0
- package/test/didweb/relationships.js +144 -0
- package/test/transaction/unspentoutput.js +6 -1
- package/version.js +1 -1
package/lib/crypto/hash.js
CHANGED
|
@@ -1,2 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Pick the hashing backend by CAPABILITY, not by `process.browser`.
|
|
4
|
+
//
|
|
5
|
+
// This file used to read `process.browser`, a Browserify-era convention that is
|
|
6
|
+
// undefined in React Native, Deno, Cloudflare Workers and Bun. All of them took the
|
|
7
|
+
// node branch and required('crypto'), which in React Native fails outright or — worse —
|
|
8
|
+
// resolves to a partial shim. See lib/crypto/random.js for the same bug in the CSPRNG.
|
|
9
|
+
//
|
|
10
|
+
// Node's implementation is kept as the preferred backend rather than always using
|
|
11
|
+
// @noble/hashes, because it is materially faster on the payload sizes this library
|
|
12
|
+
// actually hashes in bulk: measured at parity for 32 bytes (1.2x) but ~10x for 64 KB
|
|
13
|
+
// and above, which is block parsing and document anchoring.
|
|
14
|
+
//
|
|
15
|
+
// Two capability checks, not one:
|
|
16
|
+
//
|
|
17
|
+
// - The digests must be CORRECT, verified against known-answer vectors. A partial
|
|
18
|
+
// crypto shim that supplies a wrong or stubbed createHash would otherwise silently
|
|
19
|
+
// corrupt every hash in the library. Falling back to the audited pure-JS
|
|
20
|
+
// implementation is always safe; trusting an unknown shim is not.
|
|
21
|
+
// - `ripemd160` must be present. OpenSSL 3 moved it to the legacy provider, so some
|
|
22
|
+
// Node builds have sha256 but throw on ripemd160 — which is what addresses are
|
|
23
|
+
// built from, making this a real failure and not a theoretical one.
|
|
24
|
+
//
|
|
25
|
+
// The probe costs four hashes of three bytes, once, at module load.
|
|
26
|
+
|
|
27
|
+
var VECTORS = {
|
|
28
|
+
sha1: 'a9993e364706816aba3e25717850c26c9cd0d89d',
|
|
29
|
+
sha256: 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad',
|
|
30
|
+
sha512: 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a' +
|
|
31
|
+
'2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f',
|
|
32
|
+
ripemd160: '8eb208f7e05d987a9b044a8e98c6b087f15a0bfc'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function nodeHashesAreUsable () {
|
|
36
|
+
var crypto
|
|
37
|
+
try {
|
|
38
|
+
crypto = require('crypto')
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
if (!crypto || typeof crypto.createHash !== 'function') return false
|
|
43
|
+
|
|
44
|
+
var algorithms = Object.keys(VECTORS)
|
|
45
|
+
for (var i = 0; i < algorithms.length; i++) {
|
|
46
|
+
var algorithm = algorithms[i]
|
|
47
|
+
try {
|
|
48
|
+
if (crypto.createHash(algorithm).update('abc').digest('hex') !== VECTORS[algorithm]) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = nodeHashesAreUsable() ? require('./hash.node') : require('./hash.browser')
|
package/lib/crypto/random.js
CHANGED
|
@@ -1,37 +1,94 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// WebCrypto refuses more than 65,536 bytes in a single getRandomValues() call, so large
|
|
4
|
+
// requests are filled in chunks. Node's randomBytes has no such limit, which is why this
|
|
5
|
+
// only matters on the WebCrypto path.
|
|
6
|
+
var MAX_GET_RANDOM_VALUES = 65536
|
|
7
|
+
|
|
3
8
|
function Random () {
|
|
4
9
|
}
|
|
5
10
|
|
|
6
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Cryptographically secure random bytes.
|
|
13
|
+
*
|
|
14
|
+
* Backend selection is by FEATURE DETECTION, not by environment guess. This previously
|
|
15
|
+
* branched on `process.browser` — a Browserify-era convention that is undefined in React
|
|
16
|
+
* Native, Deno, Cloudflare Workers, Bun, and any bundler that does not shim it. All of
|
|
17
|
+
* those fell through to `require('crypto').randomBytes`. In React Native that is a hard
|
|
18
|
+
* failure at best; at worst a partial crypto shim (rn-nodeify and several RN starter
|
|
19
|
+
* templates register one) supplies a weak `randomBytes`, and `PrivateKey.fromRandom()`
|
|
20
|
+
* silently produces guessable keys.
|
|
21
|
+
*
|
|
22
|
+
* `globalThis.crypto.getRandomValues` covers browsers, Node >= 19, Deno, Workers, Bun,
|
|
23
|
+
* and React Native with react-native-get-random-values, so it is tried first.
|
|
24
|
+
*
|
|
25
|
+
* If no CSPRNG can be found this THROWS. That is as important as the detection: a key
|
|
26
|
+
* library must never return bytes it cannot vouch for.
|
|
27
|
+
*
|
|
28
|
+
* @param {number} size - number of bytes
|
|
29
|
+
* @returns {Buffer}
|
|
30
|
+
*/
|
|
7
31
|
Random.getRandomBuffer = function (size) {
|
|
8
|
-
|
|
32
|
+
var webcrypto = typeof globalThis !== 'undefined' ? globalThis.crypto : undefined
|
|
33
|
+
if (webcrypto && typeof webcrypto.getRandomValues === 'function') {
|
|
34
|
+
return Random._fromGetRandomValues(webcrypto, size)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Node < 19 has no global crypto. Kept as a fallback rather than the default.
|
|
38
|
+
try {
|
|
39
|
+
var nodeCrypto = require('crypto')
|
|
40
|
+
if (nodeCrypto && typeof nodeCrypto.randomBytes === 'function') {
|
|
41
|
+
return nodeCrypto.randomBytes(size)
|
|
42
|
+
}
|
|
43
|
+
} catch (e) { /* no node crypto in this environment */ }
|
|
44
|
+
|
|
45
|
+
throw new Error('bsv: no CSPRNG available in this environment (needs globalThis.crypto.getRandomValues or node crypto)')
|
|
9
46
|
}
|
|
10
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Fill `size` bytes from a WebCrypto instance, respecting the per-call limit.
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
Random._fromGetRandomValues = function (webcrypto, size) {
|
|
53
|
+
var buf = Buffer.alloc(size)
|
|
54
|
+
for (var offset = 0; offset < size; offset += MAX_GET_RANDOM_VALUES) {
|
|
55
|
+
var chunk = new Uint8Array(Math.min(MAX_GET_RANDOM_VALUES, size - offset))
|
|
56
|
+
webcrypto.getRandomValues(chunk)
|
|
57
|
+
Buffer.from(chunk).copy(buf, offset)
|
|
58
|
+
}
|
|
59
|
+
return buf
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Node-specific CSPRNG. Retained as public API; `getRandomBuffer` no longer routes
|
|
64
|
+
* here by default.
|
|
65
|
+
*/
|
|
11
66
|
Random.getRandomBufferNode = function (size) {
|
|
12
67
|
var crypto = require('crypto')
|
|
13
68
|
return crypto.randomBytes(size)
|
|
14
69
|
}
|
|
15
70
|
|
|
71
|
+
/**
|
|
72
|
+
* WebCrypto CSPRNG. Retained as public API.
|
|
73
|
+
*
|
|
74
|
+
* Reads `globalThis` rather than a bare `window`, which does not exist in Web Workers,
|
|
75
|
+
* Node, or React Native.
|
|
76
|
+
*/
|
|
16
77
|
Random.getRandomBufferBrowser = function (size) {
|
|
17
|
-
|
|
18
|
-
|
|
78
|
+
var g = typeof globalThis !== 'undefined' ? globalThis : {}
|
|
79
|
+
var crypto = null
|
|
80
|
+
|
|
81
|
+
if (g.crypto && typeof g.crypto.getRandomValues === 'function') {
|
|
82
|
+
crypto = g.crypto
|
|
83
|
+
} else if (g.msCrypto && typeof g.msCrypto.getRandomValues === 'function') { // internet explorer
|
|
84
|
+
crypto = g.msCrypto
|
|
19
85
|
}
|
|
20
|
-
var crypto
|
|
21
86
|
|
|
22
|
-
if (
|
|
23
|
-
crypto = window.crypto
|
|
24
|
-
} else if (window.msCrypto && window.msCrypto.getRandomValues) { // internet explorer
|
|
25
|
-
crypto = window.msCrypto
|
|
26
|
-
} else {
|
|
87
|
+
if (!crypto) {
|
|
27
88
|
throw new Error('window.crypto.getRandomValues not available')
|
|
28
89
|
}
|
|
29
90
|
|
|
30
|
-
|
|
31
|
-
crypto.getRandomValues(bbuf)
|
|
32
|
-
var buf = Buffer.from(bbuf)
|
|
33
|
-
|
|
34
|
-
return buf
|
|
91
|
+
return Random._fromGetRandomValues(crypto, size)
|
|
35
92
|
}
|
|
36
93
|
|
|
37
94
|
module.exports = Random
|
package/lib/didweb/index.js
CHANGED
|
@@ -58,6 +58,44 @@ async function generateIssuerKeys(opts) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// The five verification relationships DID Core defines. They exist to be DISTINCT:
|
|
62
|
+
// `authentication` proves who you are right now, `assertionMethod` makes a claim that
|
|
63
|
+
// outlives the session. Keeping them apart is what stops a low-friction login signature
|
|
64
|
+
// from being redeemed as a signed statement.
|
|
65
|
+
//
|
|
66
|
+
// This module used to cross-list every key into both, which erased the distinction and
|
|
67
|
+
// left a caller unable to express "this key may log in but may not make assertions".
|
|
68
|
+
var VERIFICATION_RELATIONSHIPS = [
|
|
69
|
+
'authentication',
|
|
70
|
+
'assertionMethod',
|
|
71
|
+
'keyAgreement',
|
|
72
|
+
'capabilityInvocation',
|
|
73
|
+
'capabilityDelegation'
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
// Preserves the historical output when a key does not say otherwise.
|
|
77
|
+
var DEFAULT_RELATIONSHIPS = ['authentication', 'assertionMethod']
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the verification relationships for a key descriptor.
|
|
81
|
+
*
|
|
82
|
+
* @param {object} key - `{ kid, jwk, relationships? }`
|
|
83
|
+
* @returns {Array<string>}
|
|
84
|
+
*/
|
|
85
|
+
function relationshipsFor (key) {
|
|
86
|
+
var rels = key.relationships || DEFAULT_RELATIONSHIPS
|
|
87
|
+
if (!Array.isArray(rels) || rels.length === 0) {
|
|
88
|
+
throw new Error('relationships must be a non-empty array of DID Core verification relationships')
|
|
89
|
+
}
|
|
90
|
+
rels.forEach(function (rel) {
|
|
91
|
+
if (VERIFICATION_RELATIONSHIPS.indexOf(rel) === -1) {
|
|
92
|
+
throw new Error('unknown verification relationship: ' + rel +
|
|
93
|
+
' (expected one of ' + VERIFICATION_RELATIONSHIPS.join(', ') + ')')
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
return rels
|
|
97
|
+
}
|
|
98
|
+
|
|
61
99
|
// Build did:web documents (did.json and jwks.json)
|
|
62
100
|
function buildDidWebDocuments(params) {
|
|
63
101
|
if (!params.domain) {
|
|
@@ -68,6 +106,7 @@ function buildDidWebDocuments(params) {
|
|
|
68
106
|
var did = 'did:web:' + domain.replace(/:/g, '%3A')
|
|
69
107
|
|
|
70
108
|
var verificationMethods = []
|
|
109
|
+
var methodRelationships = []
|
|
71
110
|
var publicKeys = []
|
|
72
111
|
|
|
73
112
|
// Add P-256 key if provided
|
|
@@ -79,6 +118,7 @@ function buildDidWebDocuments(params) {
|
|
|
79
118
|
publicKeyJwk: params.p256.jwk
|
|
80
119
|
}
|
|
81
120
|
verificationMethods.push(p256Method)
|
|
121
|
+
methodRelationships.push(relationshipsFor(params.p256))
|
|
82
122
|
publicKeys.push(params.p256.jwk)
|
|
83
123
|
}
|
|
84
124
|
|
|
@@ -91,6 +131,7 @@ function buildDidWebDocuments(params) {
|
|
|
91
131
|
publicKeyJwk: params.k1.jwk
|
|
92
132
|
}
|
|
93
133
|
verificationMethods.push(k1Method)
|
|
134
|
+
methodRelationships.push(relationshipsFor(params.k1))
|
|
94
135
|
publicKeys.push(params.k1.jwk)
|
|
95
136
|
}
|
|
96
137
|
|
|
@@ -105,11 +146,19 @@ function buildDidWebDocuments(params) {
|
|
|
105
146
|
'https://w3id.org/security/suites/jws-2020/v1'
|
|
106
147
|
],
|
|
107
148
|
id: did,
|
|
108
|
-
verificationMethod: verificationMethods
|
|
109
|
-
authentication: verificationMethods.map(function(vm) { return vm.id }),
|
|
110
|
-
assertionMethod: verificationMethods.map(function(vm) { return vm.id })
|
|
149
|
+
verificationMethod: verificationMethods
|
|
111
150
|
}
|
|
112
151
|
|
|
152
|
+
// Scope each key to the relationships it was given. Omitting `relationships`
|
|
153
|
+
// reproduces the previous output exactly (every key in both authentication and
|
|
154
|
+
// assertionMethod), so existing callers are unaffected.
|
|
155
|
+
VERIFICATION_RELATIONSHIPS.forEach(function (rel) {
|
|
156
|
+
var ids = verificationMethods
|
|
157
|
+
.filter(function (vm, i) { return methodRelationships[i].indexOf(rel) !== -1 })
|
|
158
|
+
.map(function (vm) { return vm.id })
|
|
159
|
+
if (ids.length) didDocument[rel] = ids
|
|
160
|
+
})
|
|
161
|
+
|
|
113
162
|
if (params.controllerName) {
|
|
114
163
|
didDocument.controller = params.controllerName
|
|
115
164
|
}
|
|
@@ -144,6 +193,11 @@ function rotateIssuerKey(params) {
|
|
|
144
193
|
publicKeyJwk: params.newKey.jwk
|
|
145
194
|
}
|
|
146
195
|
|
|
196
|
+
// Which relationships this rotation actually replaces. Defaults to both, which is
|
|
197
|
+
// the historical behaviour.
|
|
198
|
+
var rotating = relationshipsFor(params.newKey)
|
|
199
|
+
var current = params.currentDocument || null
|
|
200
|
+
|
|
147
201
|
// Build updated DID Document with new key as primary
|
|
148
202
|
var didDocument = {
|
|
149
203
|
'@context': [
|
|
@@ -151,13 +205,38 @@ function rotateIssuerKey(params) {
|
|
|
151
205
|
'https://w3id.org/security/suites/jws-2020/v1'
|
|
152
206
|
],
|
|
153
207
|
id: did,
|
|
154
|
-
verificationMethod: [newMethod]
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
208
|
+
verificationMethod: [newMethod]
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// A rotation must not silently reach beyond the relationship being rotated.
|
|
212
|
+
// Replacing a compromised `authentication` key used to rewrite `assertionMethod`
|
|
213
|
+
// too, which invalidates every statement the issuer has ever signed. Relationships
|
|
214
|
+
// outside `rotating` are carried across from `currentDocument` untouched; without a
|
|
215
|
+
// `currentDocument` there is nothing to carry, and the result matches the old output.
|
|
216
|
+
VERIFICATION_RELATIONSHIPS.forEach(function (rel) {
|
|
217
|
+
if (rotating.indexOf(rel) !== -1) {
|
|
218
|
+
didDocument[rel] = [newMethod.id]
|
|
219
|
+
} else if (current && Array.isArray(current[rel]) && current[rel].length) {
|
|
220
|
+
didDocument[rel] = current[rel].slice()
|
|
160
221
|
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// Keep the verification methods those carried-over relationships still point at,
|
|
225
|
+
// otherwise the document references key ids it does not define. The rotated-out key
|
|
226
|
+
// drops out here automatically: nothing references it any more.
|
|
227
|
+
if (current && Array.isArray(current.verificationMethod)) {
|
|
228
|
+
var referenced = {}
|
|
229
|
+
VERIFICATION_RELATIONSHIPS.forEach(function (rel) {
|
|
230
|
+
(didDocument[rel] || []).forEach(function (id) { referenced[id] = true })
|
|
231
|
+
})
|
|
232
|
+
current.verificationMethod.forEach(function (vm) {
|
|
233
|
+
if (referenced[vm.id] && vm.id !== newMethod.id) didDocument.verificationMethod.push(vm)
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
didDocument.rotationInfo = {
|
|
238
|
+
rotatedAt: new Date().toISOString(),
|
|
239
|
+
gracePeriodDays: keepOldForDays
|
|
161
240
|
}
|
|
162
241
|
|
|
163
242
|
var jwks = {
|
package/lib/encoding/base58.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
var _ = require('../util/_')
|
|
4
|
-
var bs58 = require('bs58')
|
|
5
4
|
var buffer = require('buffer')
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -11,6 +10,71 @@ var buffer = require('buffer')
|
|
|
11
10
|
*/
|
|
12
11
|
var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'.split('')
|
|
13
12
|
|
|
13
|
+
// Reverse lookup for decoding. Built from ALPHABET so the two cannot disagree.
|
|
14
|
+
var ALPHABET_MAP = {}
|
|
15
|
+
for (var _i = 0; _i < ALPHABET.length; _i++) ALPHABET_MAP[ALPHABET[_i]] = _i
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Base58 encode/decode, replacing the `bs58` dependency (which also pulled `base-x`).
|
|
19
|
+
*
|
|
20
|
+
* This is the standard big-number-in-base-58 conversion: repeatedly multiply the
|
|
21
|
+
* accumulator by 256 (encode) or 58 (decode) and carry. Two details are where
|
|
22
|
+
* implementations usually go wrong, and both are exercised by the differential test in
|
|
23
|
+
* test/encoding/base58.js:
|
|
24
|
+
*
|
|
25
|
+
* - Leading zero bytes are NOT part of the number — they are significant and each one
|
|
26
|
+
* maps to a literal '1'. Dropping them silently truncates an address.
|
|
27
|
+
* - The carry loops must run to completion before the next digit, or long inputs lose
|
|
28
|
+
* the high-order bytes.
|
|
29
|
+
*
|
|
30
|
+
* These functions encode and decode addresses and WIF private keys, so they were
|
|
31
|
+
* verified by differential test against bs58 across 20,000 random inputs — including
|
|
32
|
+
* every leading-zero-count from 0 to 8 — before the dependency was removed.
|
|
33
|
+
*/
|
|
34
|
+
function base58encode (buf) {
|
|
35
|
+
if (buf.length === 0) return ''
|
|
36
|
+
var digits = [0]
|
|
37
|
+
var i, j, carry
|
|
38
|
+
for (i = 0; i < buf.length; i++) {
|
|
39
|
+
for (j = 0, carry = buf[i]; j < digits.length; j++) {
|
|
40
|
+
carry += digits[j] << 8
|
|
41
|
+
digits[j] = carry % 58
|
|
42
|
+
carry = (carry / 58) | 0
|
|
43
|
+
}
|
|
44
|
+
while (carry > 0) {
|
|
45
|
+
digits.push(carry % 58)
|
|
46
|
+
carry = (carry / 58) | 0
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
var out = ''
|
|
50
|
+
// Each leading zero byte is a significant '1', not part of the number.
|
|
51
|
+
for (i = 0; buf[i] === 0 && i < buf.length - 1; i++) out += ALPHABET[0]
|
|
52
|
+
for (i = digits.length - 1; i >= 0; i--) out += ALPHABET[digits[i]]
|
|
53
|
+
return out
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function base58decode (str) {
|
|
57
|
+
if (str.length === 0) return Buffer.alloc(0)
|
|
58
|
+
var bytes = [0]
|
|
59
|
+
var i, j, carry
|
|
60
|
+
for (i = 0; i < str.length; i++) {
|
|
61
|
+
var value = ALPHABET_MAP[str[i]]
|
|
62
|
+
if (value === undefined) throw new Error('Non-base58 character')
|
|
63
|
+
for (j = 0, carry = value; j < bytes.length; j++) {
|
|
64
|
+
carry += bytes[j] * 58
|
|
65
|
+
bytes[j] = carry & 0xff
|
|
66
|
+
carry >>= 8
|
|
67
|
+
}
|
|
68
|
+
while (carry > 0) {
|
|
69
|
+
bytes.push(carry & 0xff)
|
|
70
|
+
carry >>= 8
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Mirror of the encode side: each leading '1' is a zero byte.
|
|
74
|
+
for (i = 0; str[i] === ALPHABET[0] && i < str.length - 1; i++) bytes.push(0)
|
|
75
|
+
return Buffer.from(bytes.reverse())
|
|
76
|
+
}
|
|
77
|
+
|
|
14
78
|
/**
|
|
15
79
|
* A Base58 object can encode/decoded Base 58, which is used primarily for
|
|
16
80
|
* string-formatted Bitcoin addresses and private keys. Addresses and private
|
|
@@ -54,7 +118,7 @@ Base58.encode = function (buf) {
|
|
|
54
118
|
if (!buffer.Buffer.isBuffer(buf)) {
|
|
55
119
|
throw new Error('Input should be a buffer')
|
|
56
120
|
}
|
|
57
|
-
return
|
|
121
|
+
return base58encode(buf)
|
|
58
122
|
}
|
|
59
123
|
|
|
60
124
|
/**
|
|
@@ -67,7 +131,7 @@ Base58.decode = function (str) {
|
|
|
67
131
|
if (typeof str !== 'string') {
|
|
68
132
|
throw new Error('Input should be a string')
|
|
69
133
|
}
|
|
70
|
-
return
|
|
134
|
+
return base58decode(str)
|
|
71
135
|
}
|
|
72
136
|
|
|
73
137
|
Base58.prototype.fromBuffer = function (buf) {
|
package/lib/mnemonic/mnemonic.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
var HDPrivateKey = require('../hdprivatekey')
|
|
4
4
|
var BN = require('../crypto/bn')
|
|
5
|
-
var unorm = require('unorm')
|
|
6
5
|
var _ = require('../util/_')
|
|
7
6
|
|
|
8
7
|
var pbkdf2 = require('./pbkdf2')
|
|
@@ -16,6 +15,21 @@ var Random = require('../crypto/random')
|
|
|
16
15
|
|
|
17
16
|
var $ = require('../util/preconditions')
|
|
18
17
|
|
|
18
|
+
/**
|
|
19
|
+
* NFKD-normalise a mnemonic or passphrase, as BIP-39 requires before PBKDF2.
|
|
20
|
+
*
|
|
21
|
+
* Replaces the `unorm` dependency: `String.prototype.normalize` has been in the language
|
|
22
|
+
* since ES6 and this package requires Node >= 20.19, so the shim was redundant.
|
|
23
|
+
*
|
|
24
|
+
* Non-strings return '' so that `Mnemonic.isValid(garbage)` answers false instead of
|
|
25
|
+
* throwing. unorm was inconsistent about this — `[]` and `{}` returned '' while `['a']`
|
|
26
|
+
* and a Buffer threw — and a validity predicate that fails closed for every non-string
|
|
27
|
+
* is both the safer answer and the coherent one.
|
|
28
|
+
*/
|
|
29
|
+
function nfkd (str) {
|
|
30
|
+
return typeof str === 'string' ? str.normalize('NFKD') : ''
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
/**
|
|
20
34
|
* This is an immutable class that represents a BIP39 Mnemonic code.
|
|
21
35
|
* See BIP39 specification for more info: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
|
@@ -52,7 +66,7 @@ var Mnemonic = function (data, wordlist) {
|
|
|
52
66
|
if (Buffer.isBuffer(data)) {
|
|
53
67
|
seed = data
|
|
54
68
|
} else if (_.isString(data)) {
|
|
55
|
-
phrase =
|
|
69
|
+
phrase = nfkd(data)
|
|
56
70
|
} else if (_.isNumber(data)) {
|
|
57
71
|
ent = data
|
|
58
72
|
} else if (data) {
|
|
@@ -145,7 +159,7 @@ Mnemonic.Words = require('./words')
|
|
|
145
159
|
* @returns {boolean}
|
|
146
160
|
*/
|
|
147
161
|
Mnemonic.isValid = function (mnemonic, wordlist) {
|
|
148
|
-
mnemonic =
|
|
162
|
+
mnemonic = nfkd(mnemonic)
|
|
149
163
|
wordlist = wordlist || Mnemonic._getDictionary(mnemonic)
|
|
150
164
|
|
|
151
165
|
if (!wordlist) {
|
|
@@ -179,7 +193,7 @@ Mnemonic.isValid = function (mnemonic, wordlist) {
|
|
|
179
193
|
* @returns {boolean}
|
|
180
194
|
*/
|
|
181
195
|
Mnemonic._belongsToWordlist = function (mnemonic, wordlist) {
|
|
182
|
-
var words =
|
|
196
|
+
var words = nfkd(mnemonic).split(' ')
|
|
183
197
|
for (var i = 0; i < words.length; i++) {
|
|
184
198
|
var ind = wordlist.indexOf(words[i])
|
|
185
199
|
if (ind < 0) return false
|
|
@@ -217,7 +231,7 @@ Mnemonic._getDictionary = function (mnemonic) {
|
|
|
217
231
|
*/
|
|
218
232
|
Mnemonic.prototype.toSeed = function (passphrase) {
|
|
219
233
|
passphrase = passphrase || ''
|
|
220
|
-
return pbkdf2(
|
|
234
|
+
return pbkdf2(nfkd(this.phrase), nfkd('mnemonic' + passphrase), 2048, 64)
|
|
221
235
|
}
|
|
222
236
|
|
|
223
237
|
/**
|
package/lib/mnemonic/pbkdf2.js
CHANGED
|
@@ -1,2 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Pick the PBKDF2 backend by CAPABILITY, not by `process.browser`. Same reasoning as
|
|
4
|
+
// lib/crypto/hash.js: that flag is undefined in React Native, Deno, Workers and Bun, so
|
|
5
|
+
// all of them took the node branch and required('crypto') — failing outright, or
|
|
6
|
+
// resolving to a partial shim.
|
|
7
|
+
//
|
|
8
|
+
// The node variant needs `createHmac('sha512')`, so that is what is probed, and the
|
|
9
|
+
// result is checked against RFC 4231 test case 1 rather than merely existing. A shim
|
|
10
|
+
// that returns a wrong or stubbed HMAC would otherwise silently corrupt every seed
|
|
11
|
+
// derived from a mnemonic — and unlike a crash, that failure is invisible until funds
|
|
12
|
+
// are missing. The pure-JS fallback is audited and always safe.
|
|
13
|
+
|
|
14
|
+
var RFC4231_CASE1 = '87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854'
|
|
15
|
+
|
|
16
|
+
function nodeHmacIsUsable () {
|
|
17
|
+
var crypto
|
|
18
|
+
try {
|
|
19
|
+
crypto = require('crypto')
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
if (!crypto || typeof crypto.createHmac !== 'function') return false
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
var mac = crypto.createHmac('sha512', Buffer.alloc(20, 0x0b))
|
|
27
|
+
.update('Hi There')
|
|
28
|
+
.digest('hex')
|
|
29
|
+
return mac === RFC4231_CASE1
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = nodeHmacIsUsable() ? require('./pbkdf2.node') : require('./pbkdf2.browser')
|
|
@@ -8,7 +8,26 @@ var BN = require('../crypto/bn')
|
|
|
8
8
|
var Hash = require('../crypto/hash')
|
|
9
9
|
var Signature = require('../crypto/signature')
|
|
10
10
|
var PublicKey = require('../publickey')
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Copy a script stack for the step listener, so a listener that mutates what it is
|
|
13
|
+
* handed cannot corrupt the running evaluation.
|
|
14
|
+
*
|
|
15
|
+
* Replaces the `clone-deep` dependency. The stack is an array of Buffers — every one of
|
|
16
|
+
* the 32 push sites in this file pushes a Buffer — so a shallow map with `Buffer.from`
|
|
17
|
+
* reproduces `cloneDeep(stack, true)` exactly for this input: same bytes, new backing
|
|
18
|
+
* memory, `Buffer.isBuffer` still true, empty buffers preserved.
|
|
19
|
+
*
|
|
20
|
+
* `structuredClone` is NOT a substitute: it returns Uint8Array for a Buffer input, which
|
|
21
|
+
* would silently change the type handed to every existing stepListener.
|
|
22
|
+
*
|
|
23
|
+
* Anything non-Buffer is passed through by reference. Nothing puts such a value on the
|
|
24
|
+
* stack today; if something ever does it will be a primitive, where sharing is harmless.
|
|
25
|
+
*/
|
|
26
|
+
function cloneStack (stack) {
|
|
27
|
+
return stack.map(function (item) {
|
|
28
|
+
return Buffer.isBuffer(item) ? Buffer.from(item) : item
|
|
29
|
+
})
|
|
30
|
+
}
|
|
12
31
|
|
|
13
32
|
/**
|
|
14
33
|
* Bitcoin transactions contain scripts. Each input has a script called the
|
|
@@ -653,7 +672,7 @@ Interpreter.prototype.evaluate = function () {
|
|
|
653
672
|
Interpreter.prototype._callbackStep = function (thisStep) {
|
|
654
673
|
if (typeof this.stepListener === 'function') {
|
|
655
674
|
try {
|
|
656
|
-
this.stepListener(thisStep,
|
|
675
|
+
this.stepListener(thisStep, cloneStack(this.stack), cloneStack(this.altstack))
|
|
657
676
|
} catch (err) {
|
|
658
677
|
console.log(`Error in Step callback:${err}`)
|
|
659
678
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Set up prototypal inheritance between two constructors.
|
|
5
|
+
*
|
|
6
|
+
* Replaces the `inherits` dependency, whose modern branch this reproduces exactly:
|
|
7
|
+
* `ctor.super_` is assigned and `ctor.prototype` is REPLACED with an object whose
|
|
8
|
+
* prototype is `superCtor.prototype`.
|
|
9
|
+
*
|
|
10
|
+
* Two details are load-bearing and must not be "simplified" into
|
|
11
|
+
* `Object.setPrototypeOf(ctor.prototype, superCtor.prototype)`:
|
|
12
|
+
*
|
|
13
|
+
* - `constructor` is redefined as non-enumerable. A plain assignment would make it
|
|
14
|
+
* enumerable, so it would start showing up in `for…in` and in anything that walks
|
|
15
|
+
* own+inherited keys — this library's `toObject`/`toJSON` paths among them.
|
|
16
|
+
* - `ctor.super_` is part of the observable surface; `TransactionSignature` and the
|
|
17
|
+
* four Input subclasses are public classes, so anything reading `.super_` today
|
|
18
|
+
* keeps working.
|
|
19
|
+
*
|
|
20
|
+
* The package also carried an `Object.create`-less fallback for pre-ES5 browsers. That
|
|
21
|
+
* branch is unreachable on Node >= 20.19 and in every browser this library targets.
|
|
22
|
+
*
|
|
23
|
+
* @param {Function} ctor - the subclass constructor
|
|
24
|
+
* @param {Function} superCtor - the superclass constructor
|
|
25
|
+
*/
|
|
26
|
+
module.exports = function inherits (ctor, superCtor) {
|
|
27
|
+
if (!superCtor) return
|
|
28
|
+
ctor.super_ = superCtor
|
|
29
|
+
ctor.prototype = Object.create(superCtor.prototype, {
|
|
30
|
+
constructor: {
|
|
31
|
+
value: ctor,
|
|
32
|
+
enumerable: false,
|
|
33
|
+
writable: true,
|
|
34
|
+
configurable: true
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
}
|