@smartledger/bsv 9.0.0 → 9.1.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 +164 -0
- package/README.md +152 -1041
- package/STABILITY.md +134 -0
- package/bsv-gdaf.min.js +53 -48
- package/bsv-ltp.min.js +23 -18
- package/bsv-smartcontract.min.js +18 -18
- package/bsv.bundle.js +53 -48
- package/bsv.min.js +53 -48
- 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/docs/proposals/10.0.0-package-split.md +131 -0
- package/index.js +5 -0
- package/lib/block/merkleblock.js +53 -9
- package/lib/hdprivatekey.js +15 -0
- package/lib/script/interpreter.js +109 -1
- package/lib/smart_contract/builder.js +14 -0
- package/lib/util/deprecate.js +159 -0
- package/package.json +19 -82
- package/version.js +1 -1
- package/.mocharc.json +0 -5
- package/test/address.js +0 -629
- package/test/block/block.js +0 -239
- package/test/block/blockheader.js +0 -270
- package/test/block/merkleblock.js +0 -207
- package/test/build/bundle_crypto_shim.js +0 -104
- package/test/build/bundle_externals.js +0 -125
- package/test/build/bundle_smoke.js +0 -55
- package/test/build/esbuild_main.js +0 -65
- package/test/build/esm_wrapper.js +0 -97
- package/test/build/exports_resolution.js +0 -84
- package/test/build/version_sync.js +0 -14
- package/test/cli/smoke.js +0 -261
- package/test/consensus/base58-vectors.js +0 -120
- package/test/consensus/sv-script-vectors.js +0 -81
- package/test/consensus/sv-sighash-vectors.js +0 -51
- package/test/consensus/sv-tx-vectors.js +0 -48
- package/test/credentials-test.js +0 -332
- package/test/crypto/backend_selection.js +0 -126
- package/test/crypto/bn.js +0 -168
- package/test/crypto/ecdsa.js +0 -426
- package/test/crypto/elliptic-fixed.js +0 -61
- package/test/crypto/hash.browser.js +0 -121
- package/test/crypto/hash.js +0 -122
- package/test/crypto/point.js +0 -193
- package/test/crypto/random.js +0 -105
- package/test/crypto/security.js +0 -176
- package/test/crypto/shamir.js +0 -177
- package/test/crypto/shamir_rngcap.js +0 -29
- package/test/crypto/signature.js +0 -399
- package/test/data/bip69.json +0 -215
- package/test/data/bitcoin-sv/README.md +0 -170
- package/test/data/bitcoin-sv/base58_encode_decode.json +0 -14
- package/test/data/bitcoin-sv/base58_keys_invalid.json +0 -152
- package/test/data/bitcoin-sv/base58_keys_valid.json +0 -452
- package/test/data/bitcoin-sv/script_tests.json +0 -2591
- package/test/data/bitcoin-sv/sighash.json +0 -1003
- package/test/data/bitcoin-sv/tx_invalid.json +0 -285
- package/test/data/bitcoin-sv/tx_valid.json +0 -367
- package/test/data/bitcoind/base58_keys_invalid.json +0 -152
- package/test/data/bitcoind/base58_keys_valid.json +0 -452
- package/test/data/bitcoind/blocks.json +0 -27
- package/test/data/bitcoind/script_tests.json +0 -2244
- package/test/data/bitcoind/sig_canonical.json +0 -7
- package/test/data/bitcoind/sig_noncanonical.json +0 -22
- package/test/data/bitcoind/tx_invalid.json +0 -177
- package/test/data/bitcoind/tx_valid.json +0 -224
- package/test/data/blk86756-testnet.dat +0 -0
- package/test/data/blk86756-testnet.js +0 -12
- package/test/data/blk86756-testnet.json +0 -684
- package/test/data/brc220-batch-vector.json +0 -129
- package/test/data/ecdsa.json +0 -230
- package/test/data/merkleblocks.js +0 -486
- package/test/data/messages.json +0 -22
- package/test/data/sighash.json +0 -1004
- package/test/data/tx_creation.json +0 -85
- package/test/didweb/relationships.js +0 -144
- package/test/ecies/bitcore-ecies.js +0 -178
- package/test/ecies/electrum-ecies.js +0 -206
- package/test/encoding/base58.js +0 -131
- package/test/encoding/base58check.js +0 -145
- package/test/encoding/bufferreader.js +0 -328
- package/test/encoding/bufferwriter.js +0 -160
- package/test/encoding/varint.js +0 -104
- package/test/gdaf/anchor_no_key_leak.js +0 -146
- package/test/gdaf/anchor_spv.js +0 -163
- package/test/gdaf/canonicalization.js +0 -106
- package/test/gdaf/canonicalize.js +0 -140
- package/test/gdaf/zk_prover.js +0 -204
- package/test/hdkeys.js +0 -365
- package/test/hdprivatekey.js +0 -339
- package/test/hdpublickey.js +0 -293
- package/test/index.js +0 -16
- package/test/ltp/ids.js +0 -74
- package/test/ltp/right.js +0 -198
- package/test/ltp/verify_failclosed.js +0 -134
- package/test/message/message.js +0 -190
- package/test/mnemonic/data/fixtures.json +0 -300
- package/test/mnemonic/mnemonic.js +0 -277
- package/test/mnemonic/mocha.opts +0 -1
- package/test/mnemonic/pbkdf2.test.js +0 -43
- package/test/networks.js +0 -207
- package/test/notaryhash/batch_leaf.js +0 -140
- package/test/notaryhash/batch_vector.js +0 -282
- package/test/notaryhash/certificate.js +0 -249
- package/test/notaryhash/encoding.js +0 -186
- package/test/notaryhash/interop.js +0 -112
- package/test/notaryhash/merkle.js +0 -181
- package/test/notaryhash/script.js +0 -270
- package/test/notaryhash/verify.js +0 -342
- package/test/opcode.js +0 -186
- package/test/ordinals/bsv20.js +0 -337
- package/test/ordinals/inscription.js +0 -329
- package/test/ordinals/ordlock.js +0 -567
- package/test/privatekey.js +0 -540
- package/test/publickey.js +0 -411
- package/test/regressions.js +0 -215
- package/test/script/chronicle.js +0 -543
- package/test/script/defaults.js +0 -160
- package/test/script/genesis_limits.js +0 -203
- package/test/script/interpreter.js +0 -776
- package/test/script/script.js +0 -1259
- package/test/script/string_ops.js +0 -88
- package/test/security/fail_closed_contracts.js +0 -104
- package/test/security/threat_model_coverage.js +0 -31
- package/test/smart_contract/covenants.js +0 -207
- package/test/smart_contract/dsl_debugger.js +0 -92
- package/test/smart_contract/extract_field.js +0 -61
- package/test/smart_contract/nonenforcing_guard.js +0 -44
- package/test/smart_contract/ordinal_transfer.js +0 -71
- package/test/smart_contract/preimage.js +0 -98
- package/test/smart_contract/sighash_marketplace.js +0 -98
- package/test/smart_contract/token_generalized.js +0 -175
- package/test/spv/headerchain.js +0 -86
- package/test/spv/merkleproof.js +0 -133
- package/test/statuslist/failclosed.js +0 -78
- package/test/transaction/deserialize.js +0 -33
- package/test/transaction/input/input.js +0 -92
- package/test/transaction/input/multisig.js +0 -174
- package/test/transaction/input/multisigscripthash.js +0 -111
- package/test/transaction/input/publickey.js +0 -68
- package/test/transaction/input/publickeyhash.js +0 -59
- package/test/transaction/output.js +0 -185
- package/test/transaction/sighash.js +0 -91
- package/test/transaction/signature.js +0 -127
- package/test/transaction/transaction.js +0 -1299
- package/test/transaction/unspentoutput.js +0 -97
- package/test/types/dts_drift.js +0 -124
- package/test/types/surface_honesty.js +0 -102
- package/test/util/id.js +0 -72
- package/test/util/js.js +0 -76
- package/test/util/preconditions.js +0 -79
- package/test/vcjwt/interop.js +0 -126
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
var expect = require('chai').expect
|
|
6
|
-
var bsv = require('../../')
|
|
7
|
-
var AttestationSigner = require('../../lib/gdaf/attestation-signer')
|
|
8
|
-
var AttestationVerifier = require('../../lib/gdaf/attestation-verifier')
|
|
9
|
-
|
|
10
|
-
// A stable test key (do NOT use in production / do not fund this address).
|
|
11
|
-
var TEST_WIF = 'KxRd1XXYZYuQc7uDdLN418Ds3NCxRLghQrcYecPPcB6ka5Z5dNZT'
|
|
12
|
-
var VICTIM_WIF = 'L5EBpijRWptdvC7gfTRGfTuEBwLKQvPgqgSvn4Bi4ps5CqxLP7ow'
|
|
13
|
-
|
|
14
|
-
function baseCredential () {
|
|
15
|
-
return {
|
|
16
|
-
'@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
17
|
-
id: 'urn:uuid:00000000-0000-0000-0000-000000000001',
|
|
18
|
-
type: ['VerifiableCredential', 'UniversityDegreeCredential'],
|
|
19
|
-
issuer: 'did:smartledger:placeholder',
|
|
20
|
-
issuanceDate: '2025-01-01T00:00:00.000Z',
|
|
21
|
-
credentialSubject: {
|
|
22
|
-
id: 'did:smartledger:subject',
|
|
23
|
-
degree: { type: 'BachelorDegree', name: 'Bachelor of Science' },
|
|
24
|
-
gpa: '2.0'
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
describe('GDAF canonicalization', function () {
|
|
30
|
-
describe('_canonicalizeJSON', function () {
|
|
31
|
-
it('includes nested object keys in the serialization', function () {
|
|
32
|
-
var canonical = AttestationSigner._canonicalizeJSON(baseCredential())
|
|
33
|
-
// Regression guard for the replacer-array bug: credentialSubject MUST NOT
|
|
34
|
-
// collapse to "{}", and the nested claims must appear in the output.
|
|
35
|
-
expect(canonical).to.not.contain('"credentialSubject":{}')
|
|
36
|
-
expect(canonical).to.contain('Bachelor of Science')
|
|
37
|
-
expect(canonical).to.contain('"gpa":"2.0"')
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('is independent of input key order', function () {
|
|
41
|
-
var a = { issuer: 'did:x', credentialSubject: { gpa: '4.0', degree: 'PhD' } }
|
|
42
|
-
var b = { credentialSubject: { degree: 'PhD', gpa: '4.0' }, issuer: 'did:x' }
|
|
43
|
-
expect(AttestationSigner._canonicalizeJSON(a))
|
|
44
|
-
.to.equal(AttestationSigner._canonicalizeJSON(b))
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('preserves array order (arrays are order-significant)', function () {
|
|
48
|
-
var a = AttestationSigner._canonicalizeJSON({ type: ['A', 'B'] })
|
|
49
|
-
var b = AttestationSigner._canonicalizeJSON({ type: ['B', 'A'] })
|
|
50
|
-
expect(a).to.not.equal(b)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('produces a different hash when a nested claim is tampered', function () {
|
|
54
|
-
var original = baseCredential()
|
|
55
|
-
var tampered = JSON.parse(JSON.stringify(original))
|
|
56
|
-
tampered.credentialSubject.degree.name = 'Doctor of Philosophy'
|
|
57
|
-
tampered.credentialSubject.gpa = '4.0'
|
|
58
|
-
|
|
59
|
-
var h1 = AttestationSigner._hashCredential(original).toString('hex')
|
|
60
|
-
var h2 = AttestationSigner._hashCredential(tampered).toString('hex')
|
|
61
|
-
expect(h1).to.not.equal(h2)
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
describe('sign / verify round-trip', function () {
|
|
66
|
-
it('verifies an untampered credential', async function () {
|
|
67
|
-
var signer = AttestationSigner(bsv.PrivateKey.fromWIF(TEST_WIF))
|
|
68
|
-
var credential = signer.createCredential({
|
|
69
|
-
id: 'did:smartledger:subject',
|
|
70
|
-
degree: { type: 'BachelorDegree', name: 'Bachelor of Science' },
|
|
71
|
-
gpa: '2.0'
|
|
72
|
-
}, { type: 'UniversityDegreeCredential' })
|
|
73
|
-
|
|
74
|
-
var signed = signer.signCredential(credential)
|
|
75
|
-
var result = await AttestationVerifier.verifyCredential(signed)
|
|
76
|
-
expect(result.checks.signature).to.equal(true)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('rejects an issuer-spoofed credential (signer DID != issuer)', async function () {
|
|
80
|
-
// Attacker signs with their own key but claims a different issuer.
|
|
81
|
-
var attacker = AttestationSigner(bsv.PrivateKey.fromWIF(TEST_WIF))
|
|
82
|
-
var credential = attacker.createCredential({
|
|
83
|
-
id: 'did:smartledger:subject',
|
|
84
|
-
degree: { type: 'BachelorDegree', name: 'Bachelor of Science' }
|
|
85
|
-
}, { type: 'UniversityDegreeCredential' })
|
|
86
|
-
|
|
87
|
-
// A different "trusted authority" the attacker wants to impersonate.
|
|
88
|
-
var victim = AttestationSigner(bsv.PrivateKey.fromWIF(VICTIM_WIF))
|
|
89
|
-
expect(victim.did).to.not.equal(attacker.did)
|
|
90
|
-
|
|
91
|
-
var signed = attacker.signCredential(credential)
|
|
92
|
-
// Forge the issuer field. The proof (verificationMethod) still points at
|
|
93
|
-
// the attacker's DID, but the credential now claims the victim issued it.
|
|
94
|
-
signed.issuer = victim.did
|
|
95
|
-
|
|
96
|
-
var result = await AttestationVerifier.verifyCredential(signed)
|
|
97
|
-
expect(result.checks.signature).to.equal(false)
|
|
98
|
-
expect(result.valid).to.equal(false)
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('enforces the trustedIssuers allow-list (rejects untrusted issuer)', async function () {
|
|
102
|
-
var signer = AttestationSigner(bsv.PrivateKey.fromWIF(TEST_WIF))
|
|
103
|
-
var signed = signer.signCredential(signer.createCredential(
|
|
104
|
-
{ id: 'did:smartledger:subject', degree: 'BSc' },
|
|
105
|
-
{ type: 'UniversityDegreeCredential' }
|
|
106
|
-
))
|
|
107
|
-
|
|
108
|
-
// Allow-list contains a different DID than the actual issuer.
|
|
109
|
-
var other = AttestationSigner(bsv.PrivateKey.fromWIF(VICTIM_WIF))
|
|
110
|
-
var rejected = await AttestationVerifier.verifyCredential(signed, {
|
|
111
|
-
trustedIssuers: [other.did]
|
|
112
|
-
})
|
|
113
|
-
expect(rejected.checks.issuer).to.equal(false)
|
|
114
|
-
expect(rejected.valid).to.equal(false)
|
|
115
|
-
|
|
116
|
-
// Same credential passes when its issuer IS on the allow-list.
|
|
117
|
-
var accepted = await AttestationVerifier.verifyCredential(signed, {
|
|
118
|
-
trustedIssuers: [signer.did]
|
|
119
|
-
})
|
|
120
|
-
expect(accepted.checks.issuer).to.equal(true)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('rejects a credential whose nested claims were tampered after signing', async function () {
|
|
124
|
-
var signer = AttestationSigner(bsv.PrivateKey.fromWIF(TEST_WIF))
|
|
125
|
-
var credential = signer.createCredential({
|
|
126
|
-
id: 'did:smartledger:subject',
|
|
127
|
-
degree: { type: 'BachelorDegree', name: 'Bachelor of Science' },
|
|
128
|
-
gpa: '2.0'
|
|
129
|
-
}, { type: 'UniversityDegreeCredential' })
|
|
130
|
-
|
|
131
|
-
var signed = signer.signCredential(credential)
|
|
132
|
-
// Forge the subject's claims while keeping the original signature/proof.
|
|
133
|
-
signed.credentialSubject.degree.name = 'Doctor of Philosophy'
|
|
134
|
-
signed.credentialSubject.gpa = '4.0'
|
|
135
|
-
|
|
136
|
-
var result = await AttestationVerifier.verifyCredential(signed)
|
|
137
|
-
expect(result.checks.signature).to.equal(false)
|
|
138
|
-
})
|
|
139
|
-
})
|
|
140
|
-
})
|
package/test/gdaf/zk_prover.js
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
// This module had ZERO test coverage, and carried three defects that coverage would have
|
|
6
|
-
// caught immediately. Each is pinned below as the attack that worked, not as a paraphrase
|
|
7
|
-
// of the fix, so a regression reproduces the exploit rather than merely failing a check.
|
|
8
|
-
//
|
|
9
|
-
// It is also not zero-knowledge, and the tests say so where it matters: the range and age
|
|
10
|
-
// commitments can only be verified by opening them, which reveals the committed value.
|
|
11
|
-
|
|
12
|
-
require('chai').should()
|
|
13
|
-
var bsv = require('../..')
|
|
14
|
-
var ZK = require('../../lib/gdaf/zk-prover')
|
|
15
|
-
var Hash = require('../../lib/crypto/hash')
|
|
16
|
-
|
|
17
|
-
var CRED = {
|
|
18
|
-
id: 'urn:x',
|
|
19
|
-
credentialSubject: { name: 'Alice', partyAffiliation: 'DEM', eligible: true }
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe('ZKProver', function () {
|
|
23
|
-
describe('range proofs', function () {
|
|
24
|
-
// The original read `return proof.inRange === true` — a boolean the prover writes
|
|
25
|
-
// about itself. valueCommitment was never consulted, so this forgery verified.
|
|
26
|
-
it('rejects a forged proof that merely claims inRange', function () {
|
|
27
|
-
var forged = {
|
|
28
|
-
type: 'RangeProof',
|
|
29
|
-
range: { min: 18, max: 120 },
|
|
30
|
-
valueCommitment: '00'.repeat(32),
|
|
31
|
-
proofHash: 'de'.repeat(32),
|
|
32
|
-
inRange: true
|
|
33
|
-
}
|
|
34
|
-
ZK.verifyRangeProof(forged, 18, 120).should.equal(false)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('rejects an honest proof presented without its opening', function () {
|
|
38
|
-
var proof = ZK.generateRangeProof(41, 18, 120)
|
|
39
|
-
ZK.verifyRangeProof(proof, 18, 120).should.equal(false)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('accepts an honest proof with its opening', function () {
|
|
43
|
-
var proof = ZK.generateRangeProof(41, 18, 120)
|
|
44
|
-
ZK.verifyRangeProof(proof, 18, 120, proof.opening).should.equal(true)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
// The commitment must actually bind the value: a lie about what was committed to
|
|
48
|
-
// has to fail even though the range and structure are right.
|
|
49
|
-
it('rejects an opening that does not match the commitment', function () {
|
|
50
|
-
var proof = ZK.generateRangeProof(41, 18, 120)
|
|
51
|
-
var lie = { value: 12, salt: proof.opening.salt }
|
|
52
|
-
ZK.verifyRangeProof(proof, 18, 120, lie).should.equal(false)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('rejects a value outside the stated range even if the commitment opens', function () {
|
|
56
|
-
var proof = ZK.generateRangeProof(41, 18, 120)
|
|
57
|
-
ZK.verifyRangeProof(proof, 50, 120, proof.opening).should.equal(false)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('returns a strict boolean', function () {
|
|
61
|
-
var proof = ZK.generateRangeProof(41, 18, 120)
|
|
62
|
-
ZK.verifyRangeProof(proof, 18, 120, proof.opening).should.be.a('boolean')
|
|
63
|
-
ZK.verifyRangeProof({}, 18, 120).should.be.a('boolean')
|
|
64
|
-
})
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
describe('age proofs', function () {
|
|
68
|
-
var BIRTH = new Date('1985-01-01T00:00:00Z')
|
|
69
|
-
|
|
70
|
-
it('rejects a forged proof that merely claims meetsRequirement', function () {
|
|
71
|
-
var forged = {
|
|
72
|
-
type: 'AgeProof',
|
|
73
|
-
minimumAge: 18,
|
|
74
|
-
meetsRequirement: true,
|
|
75
|
-
birthDateCommitment: '00'.repeat(32),
|
|
76
|
-
challengeResponse: 'anything-non-empty'
|
|
77
|
-
}
|
|
78
|
-
ZK.verifyAgeProof(forged, 18).should.equal(false)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('rejects an honest proof presented without its opening', function () {
|
|
82
|
-
var proof = ZK.generateAgeProof(BIRTH, 18)
|
|
83
|
-
ZK.verifyAgeProof(proof, 18).should.equal(false)
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('accepts an honest proof with its opening', function () {
|
|
87
|
-
var proof = ZK.generateAgeProof(BIRTH, 18)
|
|
88
|
-
ZK.verifyAgeProof(proof, 18, proof.opening).should.equal(true)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('rejects an opening that does not match the commitment', function () {
|
|
92
|
-
var proof = ZK.generateAgeProof(BIRTH, 18)
|
|
93
|
-
ZK.verifyAgeProof(proof, 18, { birthDate: '2015-01-01', salt: proof.opening.salt })
|
|
94
|
-
.should.equal(false)
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
describe('selective disclosure', function () {
|
|
99
|
-
// THE LEAK. One salt covered every leaf and travelled in the proof, so the sibling
|
|
100
|
-
// hashes on the Merkle path could be brute-forced back into the withheld fields.
|
|
101
|
-
// Disclosing only the name recovered id, partyAffiliation and eligible.
|
|
102
|
-
it('does not leak the withheld fields to someone holding the proof', function () {
|
|
103
|
-
var proof = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'aa'.repeat(16))
|
|
104
|
-
|
|
105
|
-
// Everything an attacker gets: the proof's salts and the Merkle path.
|
|
106
|
-
var salts = proof.disclosedFields.map(function (f) { return f.salt })
|
|
107
|
-
var siblings = []
|
|
108
|
-
proof.merkleProofs.forEach(function (mp) {
|
|
109
|
-
mp.proof.forEach(function (n) { siblings.push(n.hash) })
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
function leaf (p, v, s) {
|
|
113
|
-
return Hash.sha256(Buffer.from(p + ':' + JSON.stringify(v) + ':' + s, 'utf8')).toString('hex')
|
|
114
|
-
}
|
|
115
|
-
function node (a, b) {
|
|
116
|
-
return Hash.sha256(Buffer.from(a + b, 'hex')).toString('hex')
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
var paths = ['id', 'credentialSubject.partyAffiliation', 'credentialSubject.eligible']
|
|
120
|
-
var values = ['urn:x', 'DEM', 'REP', 'IND', true, false]
|
|
121
|
-
var recovered = []
|
|
122
|
-
|
|
123
|
-
salts.forEach(function (s) {
|
|
124
|
-
// Direct leaf siblings.
|
|
125
|
-
paths.forEach(function (p) {
|
|
126
|
-
values.forEach(function (v) {
|
|
127
|
-
if (siblings.indexOf(leaf(p, v, s)) !== -1) recovered.push(p)
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
// Internal nodes: brute-force the pair beneath them.
|
|
131
|
-
paths.forEach(function (p1) {
|
|
132
|
-
values.forEach(function (v1) {
|
|
133
|
-
paths.forEach(function (p2) {
|
|
134
|
-
values.forEach(function (v2) {
|
|
135
|
-
if (siblings.indexOf(node(leaf(p1, v1, s), leaf(p2, v2, s))) !== -1) {
|
|
136
|
-
recovered.push(p1, p2)
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
})
|
|
140
|
-
})
|
|
141
|
-
})
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
recovered.should.deep.equal([], 'recovered withheld fields: ' + recovered.join(', '))
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('never ships the master salt, which would reopen every leaf', function () {
|
|
148
|
-
var proof = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'aa'.repeat(16))
|
|
149
|
-
;(proof.salt === undefined).should.equal(true)
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
// The generated salt used to be dropped on the floor: the proof returned the
|
|
153
|
-
// (undefined) `salt` ARGUMENT rather than the one createMerkleTree produced, so a
|
|
154
|
-
// proof made without an explicit salt could never verify.
|
|
155
|
-
it('verifies a proof generated without an explicit salt', function () {
|
|
156
|
-
var proof = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'])
|
|
157
|
-
var result = ZK.verifySelectiveProof(proof, proof.credentialRoot)
|
|
158
|
-
result.valid.should.equal(true, JSON.stringify(result.errors))
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it('verifies a proof generated with an explicit salt', function () {
|
|
162
|
-
var proof = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'aa'.repeat(16))
|
|
163
|
-
ZK.verifySelectiveProof(proof, proof.credentialRoot).valid.should.equal(true)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('rejects a tampered disclosed value', function () {
|
|
167
|
-
var proof = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'aa'.repeat(16))
|
|
168
|
-
proof.disclosedFields[0].value = 'Mallory'
|
|
169
|
-
ZK.verifySelectiveProof(proof, proof.credentialRoot).valid.should.equal(false)
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('rejects a proof checked against the wrong root', function () {
|
|
173
|
-
var proof = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'aa'.repeat(16))
|
|
174
|
-
ZK.verifySelectiveProof(proof, '00'.repeat(32)).valid.should.equal(false)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
it('rejects a field whose salt has been stripped', function () {
|
|
178
|
-
var proof = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'aa'.repeat(16))
|
|
179
|
-
delete proof.disclosedFields[0].salt
|
|
180
|
-
ZK.verifySelectiveProof(proof, proof.credentialRoot).valid.should.equal(false)
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
// Deterministic trees are still available for callers that need them, without the
|
|
184
|
-
// single-salt weakness that made them dangerous.
|
|
185
|
-
it('is deterministic given the same master salt', function () {
|
|
186
|
-
var a = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'bb'.repeat(16))
|
|
187
|
-
var b = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'bb'.repeat(16))
|
|
188
|
-
a.credentialRoot.should.equal(b.credentialRoot)
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
it('gives different roots for different master salts', function () {
|
|
192
|
-
var a = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'bb'.repeat(16))
|
|
193
|
-
var b = ZK.generateSelectiveProof(CRED, ['credentialSubject.name'], 'cc'.repeat(16))
|
|
194
|
-
a.credentialRoot.should.not.equal(b.credentialRoot)
|
|
195
|
-
})
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
// The module is reachable from the public GDAF surface, so the fixes above are not
|
|
199
|
-
// internal-only.
|
|
200
|
-
it('is exposed on the GDAF instance', function () {
|
|
201
|
-
var g = new bsv.GDAF()
|
|
202
|
-
;(typeof g.zkProver.verifyRangeProof).should.equal('function')
|
|
203
|
-
})
|
|
204
|
-
})
|