@smartledger/bsv 5.5.0 → 5.5.2
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/.mocharc.json +5 -0
- package/CHANGELOG.md +40 -0
- package/README.md +38 -38
- package/bsv-covenant.min.js +2 -2
- package/bsv-gdaf.min.js +2 -2
- package/bsv-ltp.min.js +2 -2
- package/bsv-smartcontract.min.js +2 -2
- package/bsv.bundle.js +2 -2
- package/bsv.min.js +2 -2
- 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/lib/smart_contract/covenant_helpers.js +3 -3
- package/lib/smart_contract/index.js +4 -5
- package/package.json +5 -1
- package/test/address.js +629 -0
- package/test/block/block.js +239 -0
- package/test/block/blockheader.js +270 -0
- package/test/block/merkleblock.js +207 -0
- package/test/cli/smoke.js +261 -0
- package/test/credentials-test.js +329 -0
- package/test/crypto/bn.js +168 -0
- package/test/crypto/ecdsa.js +328 -0
- package/test/crypto/elliptic-fixed.js +61 -0
- package/test/crypto/hash.browser.js +121 -0
- package/test/crypto/hash.js +122 -0
- package/test/crypto/point.js +193 -0
- package/test/crypto/random.js +32 -0
- package/test/crypto/security.js +127 -0
- package/test/crypto/shamir.js +169 -0
- package/test/crypto/signature.js +318 -0
- package/test/data/bip69.json +215 -0
- package/test/data/bitcoind/base58_keys_invalid.json +152 -0
- package/test/data/bitcoind/base58_keys_valid.json +452 -0
- package/test/data/bitcoind/blocks.json +27 -0
- package/test/data/bitcoind/script_tests.json +2244 -0
- package/test/data/bitcoind/sig_canonical.json +7 -0
- package/test/data/bitcoind/sig_noncanonical.json +22 -0
- package/test/data/bitcoind/tx_invalid.json +177 -0
- package/test/data/bitcoind/tx_valid.json +224 -0
- package/test/data/blk86756-testnet.dat +0 -0
- package/test/data/blk86756-testnet.js +12 -0
- package/test/data/blk86756-testnet.json +684 -0
- package/test/data/ecdsa.json +230 -0
- package/test/data/merkleblocks.js +486 -0
- package/test/data/messages.json +22 -0
- package/test/data/sighash.json +1004 -0
- package/test/data/tx_creation.json +85 -0
- package/test/ecies/bitcore-ecies.js +170 -0
- package/test/ecies/electrum-ecies.js +206 -0
- package/test/encoding/base58.js +131 -0
- package/test/encoding/base58check.js +136 -0
- package/test/encoding/bufferreader.js +328 -0
- package/test/encoding/bufferwriter.js +160 -0
- package/test/encoding/varint.js +104 -0
- package/test/gdaf/canonicalize.js +140 -0
- package/test/hdkeys.js +365 -0
- package/test/hdprivatekey.js +330 -0
- package/test/hdpublickey.js +293 -0
- package/test/index.js +16 -0
- package/test/ltp/right.js +198 -0
- package/test/message/message.js +190 -0
- package/test/mnemonic/data/fixtures.json +300 -0
- package/test/mnemonic/mnemonic.js +245 -0
- package/test/mnemonic/mocha.opts +1 -0
- package/test/mnemonic/pbkdf2.test.js +43 -0
- package/test/networks.js +162 -0
- package/test/opcode.js +161 -0
- package/test/privatekey.js +430 -0
- package/test/publickey.js +411 -0
- package/test/script/genesis_limits.js +80 -0
- package/test/script/interpreter.js +723 -0
- package/test/script/script.js +1256 -0
- package/test/script/string_ops.js +72 -0
- package/test/smart_contract/covenants.js +247 -0
- package/test/smart_contract/dsl_debugger.js +103 -0
- package/test/smart_contract/token_generalized.js +187 -0
- package/test/transaction/deserialize.js +33 -0
- package/test/transaction/input/input.js +92 -0
- package/test/transaction/input/multisig.js +174 -0
- package/test/transaction/input/multisigscripthash.js +111 -0
- package/test/transaction/input/publickey.js +68 -0
- package/test/transaction/input/publickeyhash.js +59 -0
- package/test/transaction/output.js +185 -0
- package/test/transaction/sighash.js +71 -0
- package/test/transaction/signature.js +127 -0
- package/test/transaction/transaction.js +1299 -0
- package/test/transaction/unspentoutput.js +92 -0
- package/test/util/js.js +76 -0
- package/test/util/preconditions.js +79 -0
- package/test/vcjwt/interop.js +126 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var chai = require('chai')
|
|
4
|
+
var expect = chai.expect
|
|
5
|
+
var cloneDeep = require('clone-deep')
|
|
6
|
+
|
|
7
|
+
var bsv = require('../..')
|
|
8
|
+
var UnspentOutput = bsv.Transaction.UnspentOutput
|
|
9
|
+
|
|
10
|
+
describe('UnspentOutput', function () {
|
|
11
|
+
var sampleData1 = {
|
|
12
|
+
'address': 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1',
|
|
13
|
+
'txId': 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458',
|
|
14
|
+
'outputIndex': 0,
|
|
15
|
+
'script': 'OP_DUP OP_HASH160 20 0x88d9931ea73d60eaf7e5671efc0552b912911f2a OP_EQUALVERIFY OP_CHECKSIG',
|
|
16
|
+
'satoshis': 1020000
|
|
17
|
+
}
|
|
18
|
+
var sampleData2 = {
|
|
19
|
+
'txid': 'e42447187db5a29d6db161661e4bc66d61c3e499690fe5ea47f87b79ca573986',
|
|
20
|
+
'vout': 1,
|
|
21
|
+
'address': 'mgBCJAsvzgT2qNNeXsoECg2uPKrUsZ76up',
|
|
22
|
+
'scriptPubKey': '76a914073b7eae2823efa349e3b9155b8a735526463a0f88ac',
|
|
23
|
+
'amount': 0.01080000
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
it('roundtrip from raw data', function () {
|
|
27
|
+
expect(UnspentOutput(sampleData2).toObject()).to.deep.equal(sampleData2)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('can be created without "new" operand', function () {
|
|
31
|
+
expect(UnspentOutput(sampleData1) instanceof UnspentOutput).to.equal(true)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('fails if no tx id is provided', function () {
|
|
35
|
+
expect(function () {
|
|
36
|
+
return new UnspentOutput({})
|
|
37
|
+
}).to.throw()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('fails if vout is not a number', function () {
|
|
41
|
+
var sample = cloneDeep(sampleData2)
|
|
42
|
+
sample.vout = '1'
|
|
43
|
+
expect(function () {
|
|
44
|
+
return new UnspentOutput(sample)
|
|
45
|
+
}).to.throw()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('displays nicely on the console', function () {
|
|
49
|
+
var expected = '<UnspentOutput: a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458:0' +
|
|
50
|
+
', satoshis: 1020000, address: mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1>'
|
|
51
|
+
expect(new UnspentOutput(sampleData1).inspect()).to.equal(expected)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('checking the constructor parameters', function () {
|
|
55
|
+
var notDefined = {
|
|
56
|
+
'txId': 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458',
|
|
57
|
+
'outputIndex': 0,
|
|
58
|
+
'script': 'OP_DUP OP_HASH160 20 0x88d9931ea73d60eaf7e5671efc0552b912911f2a OP_EQUALVERIFY OP_CHECKSIG'
|
|
59
|
+
}
|
|
60
|
+
var zero = {
|
|
61
|
+
'txId': 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458',
|
|
62
|
+
'outputIndex': 0,
|
|
63
|
+
'script': 'OP_DUP OP_HASH160 20 0x88d9931ea73d60eaf7e5671efc0552b912911f2a OP_EQUALVERIFY OP_CHECKSIG',
|
|
64
|
+
'amount': 0
|
|
65
|
+
}
|
|
66
|
+
it('fails when no amount is defined', function () {
|
|
67
|
+
expect(function () {
|
|
68
|
+
return new UnspentOutput(notDefined)
|
|
69
|
+
}).to.throw('Must provide an amount for the output')
|
|
70
|
+
})
|
|
71
|
+
it('does not fail when amount is zero', function () {
|
|
72
|
+
expect(function () {
|
|
73
|
+
return new UnspentOutput(zero)
|
|
74
|
+
}).to.not.throw()
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('toString returns txid:vout', function () {
|
|
79
|
+
var expected = 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458:0'
|
|
80
|
+
expect(new UnspentOutput(sampleData1).toString()).to.equal(expected)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('to/from JSON roundtrip', function () {
|
|
84
|
+
var utxo = new UnspentOutput(sampleData2)
|
|
85
|
+
var obj = UnspentOutput.fromObject(utxo.toJSON()).toObject()
|
|
86
|
+
expect(obj).to.deep.equal(sampleData2)
|
|
87
|
+
var str = JSON.stringify(UnspentOutput.fromObject(obj))
|
|
88
|
+
expect(JSON.parse(str)).to.deep.equal(sampleData2)
|
|
89
|
+
var str2 = JSON.stringify(new UnspentOutput(JSON.parse(str)))
|
|
90
|
+
expect(JSON.parse(str2)).to.deep.equal(sampleData2)
|
|
91
|
+
})
|
|
92
|
+
})
|
package/test/util/js.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
require('chai').should()
|
|
4
|
+
|
|
5
|
+
var bsv = require('../..')
|
|
6
|
+
var JSUtil = bsv.util.js
|
|
7
|
+
|
|
8
|
+
describe('js utils', function () {
|
|
9
|
+
describe('isValidJSON', function () {
|
|
10
|
+
var hexa = '8080808080808080808080808080808080808080808080808080808080808080'
|
|
11
|
+
var json = '{"key": ["value", "value2"]}'
|
|
12
|
+
var json2 = '["value", "value2", {"key": "value"}]'
|
|
13
|
+
|
|
14
|
+
it('does not mistake an integer as valid json object', function () {
|
|
15
|
+
var valid = JSUtil.isValidJSON(hexa)
|
|
16
|
+
valid.should.equal(false)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('correctly validates a json object', function () {
|
|
20
|
+
var valid = JSUtil.isValidJSON(json)
|
|
21
|
+
valid.should.equal(true)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('correctly validates an array json object', function () {
|
|
25
|
+
var valid = JSUtil.isValidJSON(json2)
|
|
26
|
+
valid.should.equal(true)
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('isNaturalNumber', function () {
|
|
31
|
+
it('false for float', function () {
|
|
32
|
+
var a = JSUtil.isNaturalNumber(0.1)
|
|
33
|
+
a.should.equal(false)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('false for string float', function () {
|
|
37
|
+
var a = JSUtil.isNaturalNumber('0.1')
|
|
38
|
+
a.should.equal(false)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('false for string integer', function () {
|
|
42
|
+
var a = JSUtil.isNaturalNumber('1')
|
|
43
|
+
a.should.equal(false)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('false for negative integer', function () {
|
|
47
|
+
var a = JSUtil.isNaturalNumber(-1)
|
|
48
|
+
a.should.equal(false)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('false for negative integer string', function () {
|
|
52
|
+
var a = JSUtil.isNaturalNumber('-1')
|
|
53
|
+
a.should.equal(false)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('false for infinity', function () {
|
|
57
|
+
var a = JSUtil.isNaturalNumber(Infinity)
|
|
58
|
+
a.should.equal(false)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('false for NaN', function () {
|
|
62
|
+
var a = JSUtil.isNaturalNumber(NaN)
|
|
63
|
+
a.should.equal(false)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('true for zero', function () {
|
|
67
|
+
var a = JSUtil.isNaturalNumber(0)
|
|
68
|
+
a.should.equal(true)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('true for positive integer', function () {
|
|
72
|
+
var a = JSUtil.isNaturalNumber(1000)
|
|
73
|
+
a.should.equal(true)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
})
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var should = require('chai').should()
|
|
4
|
+
|
|
5
|
+
var bsv = require('../..')
|
|
6
|
+
var errors = bsv.errors
|
|
7
|
+
var $ = bsv.util.preconditions
|
|
8
|
+
var PrivateKey = bsv.PrivateKey
|
|
9
|
+
|
|
10
|
+
describe('preconditions', function () {
|
|
11
|
+
it('can be used to assert state', function () {
|
|
12
|
+
(function () {
|
|
13
|
+
$.checkState(false, 'testing')
|
|
14
|
+
}).should.throw(errors.InvalidState)
|
|
15
|
+
})
|
|
16
|
+
it('throws no false negative', function () {
|
|
17
|
+
(function () {
|
|
18
|
+
$.checkState(true, 'testing')
|
|
19
|
+
}).should.not.throw()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('can be used to check an argument', function () {
|
|
23
|
+
(function () {
|
|
24
|
+
$.checkArgument(false, 'testing')
|
|
25
|
+
}).should.throw(errors.InvalidArgument);
|
|
26
|
+
|
|
27
|
+
(function () {
|
|
28
|
+
$.checkArgument(true, 'testing')
|
|
29
|
+
}).should.not.throw(errors.InvalidArgument)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('can be used to check an argument type', function () {
|
|
33
|
+
var error
|
|
34
|
+
try {
|
|
35
|
+
$.checkArgumentType(1, 'string', 'argumentName')
|
|
36
|
+
} catch (e) {
|
|
37
|
+
error = e
|
|
38
|
+
e.message.should.equal('Invalid Argument for argumentName, expected string but got number')
|
|
39
|
+
}
|
|
40
|
+
should.exist(error)
|
|
41
|
+
})
|
|
42
|
+
it('has no false negatives when used to check an argument type', function () {
|
|
43
|
+
(function () {
|
|
44
|
+
$.checkArgumentType('a String', 'string', 'argumentName')
|
|
45
|
+
}).should.not.throw()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('can be used to check an argument type for a class', function () {
|
|
49
|
+
var error
|
|
50
|
+
try {
|
|
51
|
+
$.checkArgumentType(1, PrivateKey)
|
|
52
|
+
} catch (e) {
|
|
53
|
+
error = e
|
|
54
|
+
var fail = !(~e.message.indexOf('Invalid Argument for (unknown name)'))
|
|
55
|
+
fail.should.equal(false)
|
|
56
|
+
}
|
|
57
|
+
should.exist(error)
|
|
58
|
+
})
|
|
59
|
+
it('has no false negatives when checking a type for a class', function () {
|
|
60
|
+
(function () {
|
|
61
|
+
$.checkArgumentType(new PrivateKey(), PrivateKey)
|
|
62
|
+
}).should.not.throw()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('formats correctly a message on InvalidArgument()', function () {
|
|
66
|
+
var error = new errors.InvalidArgument()
|
|
67
|
+
error.message.should.equal('Invalid Argument')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('formats correctly a message on checkArgument', function () {
|
|
71
|
+
var error
|
|
72
|
+
try {
|
|
73
|
+
$.checkArgument(null, 'parameter must be provided')
|
|
74
|
+
} catch (e) {
|
|
75
|
+
error = e
|
|
76
|
+
}
|
|
77
|
+
error.message.should.equal('Invalid Argument: parameter must be provided')
|
|
78
|
+
})
|
|
79
|
+
})
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* global describe, it */
|
|
4
|
+
|
|
5
|
+
// Proves the VC-JWT module is interoperable with the standards-compliant
|
|
6
|
+
// `jose` library in BOTH directions, for ES256 (P-256) and ES256K (secp256k1),
|
|
7
|
+
// and that algorithm pinning / curve binding reject substitution attacks.
|
|
8
|
+
|
|
9
|
+
var assert = require('assert')
|
|
10
|
+
var crypto = require('crypto')
|
|
11
|
+
var jose = require('jose')
|
|
12
|
+
var vcjwt = require('../../lib/vcjwt')
|
|
13
|
+
|
|
14
|
+
function genKeys (alg, namedCurve) {
|
|
15
|
+
var pair = crypto.generateKeyPairSync('ec', { namedCurve: namedCurve })
|
|
16
|
+
var privateJwk = pair.privateKey.export({ format: 'jwk' })
|
|
17
|
+
var publicJwk = pair.publicKey.export({ format: 'jwk' })
|
|
18
|
+
privateJwk.kid = publicJwk.kid = 'test-key-1'
|
|
19
|
+
privateJwk.alg = publicJwk.alg = alg
|
|
20
|
+
return { privateJwk: privateJwk, publicJwk: publicJwk }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolverFor (publicJwk) {
|
|
24
|
+
return function () {
|
|
25
|
+
return Promise.resolve({ jwks: { keys: [publicJwk] } })
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var CASES = [
|
|
30
|
+
{ alg: 'ES256', curve: 'prime256v1' },
|
|
31
|
+
{ alg: 'ES256K', curve: 'secp256k1' }
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
describe('vcjwt JOSE interop', function () {
|
|
35
|
+
CASES.forEach(function (c) {
|
|
36
|
+
it('issues a token that ' + c.alg + ' jose can verify (P1363 encoding)', async function () {
|
|
37
|
+
var keys = genKeys(c.alg, c.curve)
|
|
38
|
+
var issued = await vcjwt.issueVcJwt({
|
|
39
|
+
issuerDid: 'did:web:example.com',
|
|
40
|
+
subjectId: 'did:web:subject.example',
|
|
41
|
+
credentialSubject: { name: 'Alice' },
|
|
42
|
+
privateJwk: keys.privateJwk,
|
|
43
|
+
alg: c.alg
|
|
44
|
+
})
|
|
45
|
+
var joseKey = await jose.importJWK(keys.publicJwk, c.alg)
|
|
46
|
+
var result = await jose.jwtVerify(issued.jwt, joseKey) // throws if non-compliant
|
|
47
|
+
assert.strictEqual(result.payload.iss, 'did:web:example.com')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('verifies a ' + c.alg + ' token issued by jose', async function () {
|
|
51
|
+
var keys = genKeys(c.alg, c.curve)
|
|
52
|
+
var josePriv = await jose.importJWK(keys.privateJwk, c.alg)
|
|
53
|
+
var joseJwt = await new jose.SignJWT({ vc: { type: ['VerifiableCredential'] } })
|
|
54
|
+
.setProtectedHeader({ alg: c.alg, typ: 'JWT', kid: keys.publicJwk.kid })
|
|
55
|
+
.setIssuer('did:web:example.com')
|
|
56
|
+
.setSubject('did:web:subject.example')
|
|
57
|
+
.setIssuedAt()
|
|
58
|
+
.setExpirationTime('1y')
|
|
59
|
+
.sign(josePriv)
|
|
60
|
+
var res = await vcjwt.verifyVcJwt(joseJwt, { didResolver: resolverFor(keys.publicJwk) })
|
|
61
|
+
assert.strictEqual(res.valid, true, res.error)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('rejects a token whose alg is not in the allowlist', async function () {
|
|
66
|
+
var keys = genKeys('ES256', 'prime256v1')
|
|
67
|
+
var issued = await vcjwt.issueVcJwt({
|
|
68
|
+
issuerDid: 'did:web:example.com', subjectId: 's', credentialSubject: {}, privateJwk: keys.privateJwk, alg: 'ES256'
|
|
69
|
+
})
|
|
70
|
+
var res = await vcjwt.verifyVcJwt(issued.jwt, {
|
|
71
|
+
allowedAlgs: ['ES256K'],
|
|
72
|
+
didResolver: resolverFor(keys.publicJwk)
|
|
73
|
+
})
|
|
74
|
+
assert.strictEqual(res.valid, false)
|
|
75
|
+
assert.ok(/Algorithm not allowed/.test(res.error), res.error)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('rejects when the resolved key curve does not match the algorithm', async function () {
|
|
79
|
+
// ES256 token, but the JWKS serves a secp256k1 key under the same kid.
|
|
80
|
+
var p256 = genKeys('ES256', 'prime256v1')
|
|
81
|
+
var k1 = genKeys('ES256K', 'secp256k1')
|
|
82
|
+
var issued = await vcjwt.issueVcJwt({
|
|
83
|
+
issuerDid: 'did:web:example.com', subjectId: 's', credentialSubject: {}, privateJwk: p256.privateJwk, alg: 'ES256'
|
|
84
|
+
})
|
|
85
|
+
var res = await vcjwt.verifyVcJwt(issued.jwt, { didResolver: resolverFor(k1.publicJwk) })
|
|
86
|
+
assert.strictEqual(res.valid, false)
|
|
87
|
+
assert.ok(/curve does not match/.test(res.error), res.error)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('refuses to sign when the key curve does not match the algorithm', async function () {
|
|
91
|
+
var k1 = genKeys('ES256K', 'secp256k1')
|
|
92
|
+
var threw = false
|
|
93
|
+
try {
|
|
94
|
+
await vcjwt.issueVcJwt({
|
|
95
|
+
issuerDid: 'did:web:example.com', subjectId: 's', credentialSubject: {}, privateJwk: k1.privateJwk, alg: 'ES256'
|
|
96
|
+
})
|
|
97
|
+
} catch (e) {
|
|
98
|
+
threw = /does not match alg/.test(e.message)
|
|
99
|
+
}
|
|
100
|
+
assert.ok(threw, 'expected curve/alg mismatch to throw')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('accepts a legacy DER-encoded token only when allowLegacyDER is set', async function () {
|
|
104
|
+
// Simulate a <=4.6.0 token: same signing input, but DER-encoded signature.
|
|
105
|
+
var keys = genKeys('ES256', 'prime256v1')
|
|
106
|
+
var header = { alg: 'ES256', typ: 'JWT', kid: keys.publicJwk.kid }
|
|
107
|
+
var payload = { iss: 'did:web:example.com', sub: 's', iat: 1700000000, exp: 4102444800, vc: {} }
|
|
108
|
+
var headerB64 = vcjwt.base64UrlEncode(Buffer.from(JSON.stringify(header)))
|
|
109
|
+
var payloadB64 = vcjwt.base64UrlEncode(Buffer.from(JSON.stringify(payload)))
|
|
110
|
+
var signingInput = headerB64 + '.' + payloadB64
|
|
111
|
+
var derSig = crypto.sign('sha256', Buffer.from(signingInput), {
|
|
112
|
+
key: crypto.createPrivateKey({ key: keys.privateJwk, format: 'jwk' }),
|
|
113
|
+
dsaEncoding: 'der'
|
|
114
|
+
})
|
|
115
|
+
var legacyJwt = signingInput + '.' + vcjwt.base64UrlEncode(derSig)
|
|
116
|
+
|
|
117
|
+
var rejected = await vcjwt.verifyVcJwt(legacyJwt, { didResolver: resolverFor(keys.publicJwk) })
|
|
118
|
+
assert.strictEqual(rejected.valid, false, 'legacy DER token must fail by default')
|
|
119
|
+
|
|
120
|
+
var accepted = await vcjwt.verifyVcJwt(legacyJwt, {
|
|
121
|
+
allowLegacyDER: true,
|
|
122
|
+
didResolver: resolverFor(keys.publicJwk)
|
|
123
|
+
})
|
|
124
|
+
assert.strictEqual(accepted.valid, true, accepted.error)
|
|
125
|
+
})
|
|
126
|
+
})
|