@smartledger/bsv 5.4.0 → 5.4.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/CHANGELOG.md +61 -0
- package/SECURITY.md +12 -3
- package/bsv-anchor.min.js +1 -1
- package/bsv-covenant.min.js +3 -3
- package/bsv-didweb.min.js +1 -1
- package/bsv-gdaf.min.js +3 -3
- package/bsv-ltp.min.js +3 -3
- package/bsv-mnemonic.min.js +1 -1
- package/bsv-shamir.min.js +1 -1
- package/bsv-smartcontract.min.js +3 -3
- package/bsv-statuslist.min.js +5 -5
- package/bsv-vcjwt.min.js +1 -1
- package/bsv.bundle.js +3 -3
- package/bsv.min.js +3 -3
- package/lib/ltp/index.js +34 -46
- package/lib/ltp/right.js +132 -33
- package/package.json +4 -4
package/lib/ltp/index.js
CHANGED
|
@@ -63,16 +63,11 @@ LTP.prototype.createRightToken = function(rightData, privateKey, options) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
// Add cryptographic proof
|
|
66
|
+
// Add cryptographic proof. Sign via the right-token signer (which sets
|
|
67
|
+
// proof.jws + tokenHash); the older LTPProof.createSignature entrypoint no
|
|
68
|
+
// longer exists (proof.js exposes prepareSignatureProof).
|
|
67
69
|
if (options.addProof !== false) {
|
|
68
|
-
|
|
69
|
-
purpose: 'assertionMethod'
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
if (proofResult.success) {
|
|
73
|
-
token.proof = proofResult.proof
|
|
74
|
-
token.tokenHash = proofResult.tokenHash
|
|
75
|
-
}
|
|
70
|
+
token = LTPRight._signToken(token, privateKey)
|
|
76
71
|
}
|
|
77
72
|
|
|
78
73
|
// Anchor to blockchain if requested
|
|
@@ -128,7 +123,7 @@ LTP.prototype.validateClaim = function(claimData, schemaType) {
|
|
|
128
123
|
* @returns {Object} Selective disclosure proof
|
|
129
124
|
*/
|
|
130
125
|
LTP.prototype.createSelectiveDisclosure = function(token, revealedFields, nonce) {
|
|
131
|
-
return LTPProof.
|
|
126
|
+
return LTPProof.prepareSelectiveDisclosure(token, revealedFields, nonce)
|
|
132
127
|
}
|
|
133
128
|
|
|
134
129
|
/**
|
|
@@ -144,8 +139,12 @@ LTP.prototype.verifyToken = function(token, publicKey) {
|
|
|
144
139
|
error: 'No proof found in token'
|
|
145
140
|
}
|
|
146
141
|
}
|
|
147
|
-
|
|
148
|
-
|
|
142
|
+
|
|
143
|
+
// Instance-created tokens (right/transfer/obligation) are signed by
|
|
144
|
+
// RightToken._signToken (detached ES256K JWS + tokenHash), so verification
|
|
145
|
+
// goes through the matching RightToken.verifyToken rather than the
|
|
146
|
+
// proofValue-based LTPProof path.
|
|
147
|
+
return LTPRight.verifyToken(token, publicKey)
|
|
149
148
|
}
|
|
150
149
|
|
|
151
150
|
/**
|
|
@@ -164,7 +163,7 @@ LTP.prototype.transferRight = function(token, newOwner, ownerKey, options) {
|
|
|
164
163
|
options = options || {}
|
|
165
164
|
|
|
166
165
|
try {
|
|
167
|
-
// Verify current ownership
|
|
166
|
+
// Verify current ownership against the owner's key before transferring.
|
|
168
167
|
var verification = this.verifyToken(token, ownerKey.toPublicKey().toString())
|
|
169
168
|
if (!verification.valid) {
|
|
170
169
|
return {
|
|
@@ -172,23 +171,21 @@ LTP.prototype.transferRight = function(token, newOwner, ownerKey, options) {
|
|
|
172
171
|
error: 'Invalid token or ownership proof'
|
|
173
172
|
}
|
|
174
173
|
}
|
|
175
|
-
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
|
|
175
|
+
// Build and sign the transferred token. prepareRightTokenTransfer signs
|
|
176
|
+
// the new token with the current owner's key, so no separate proof step
|
|
177
|
+
// is needed.
|
|
178
|
+
var transferResult = LTPRight.prepareRightTokenTransfer(token, newOwner, ownerKey, options)
|
|
179
|
+
if (!transferResult.success) {
|
|
180
|
+
return {
|
|
181
|
+
success: false,
|
|
182
|
+
error: transferResult.error
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
179
186
|
var transferredToken = transferResult.newToken
|
|
180
187
|
var transferProof = transferResult.transferProof
|
|
181
|
-
|
|
182
|
-
// Add new proof with new owner
|
|
183
|
-
var proofResult = LTPProof.createSignature(transferredToken, ownerKey, {
|
|
184
|
-
purpose: 'authentication'
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
if (proofResult.success) {
|
|
188
|
-
transferredToken.proof = proofResult.proof
|
|
189
|
-
transferredToken.tokenHash = proofResult.tokenHash
|
|
190
|
-
}
|
|
191
|
-
|
|
188
|
+
|
|
192
189
|
// Anchor transfer if requested
|
|
193
190
|
if (options.anchor) {
|
|
194
191
|
var anchorResult = LTPAnchor.anchorToken(transferredToken, options.anchor)
|
|
@@ -243,29 +240,20 @@ LTP.prototype.createObligation = function(rightToken, obligationData, privateKey
|
|
|
243
240
|
$.checkArgument(privateKey instanceof PrivateKey, 'Invalid private key')
|
|
244
241
|
|
|
245
242
|
try {
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
243
|
+
// Build and sign the correlative obligation token. prepareObligationToken
|
|
244
|
+
// signs inline via _signToken, so no separate proof step is needed.
|
|
245
|
+
var obligationResult = LTPRight.prepareObligationToken(rightToken, obligationData.obligor, obligationData, privateKey)
|
|
246
|
+
|
|
247
|
+
if (!obligationResult.success) {
|
|
250
248
|
return {
|
|
251
249
|
success: false,
|
|
252
|
-
error:
|
|
250
|
+
error: obligationResult.error
|
|
253
251
|
}
|
|
254
252
|
}
|
|
255
|
-
|
|
256
|
-
// Add proof
|
|
257
|
-
var proofResult = LTPProof.createSignature(obligation, privateKey, {
|
|
258
|
-
purpose: 'assertionMethod'
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
if (proofResult.success) {
|
|
262
|
-
obligation.proof = proofResult.proof
|
|
263
|
-
obligation.tokenHash = proofResult.tokenHash
|
|
264
|
-
}
|
|
265
|
-
|
|
253
|
+
|
|
266
254
|
return {
|
|
267
255
|
success: true,
|
|
268
|
-
obligation:
|
|
256
|
+
obligation: obligationResult.obligationToken,
|
|
269
257
|
rightTokenId: rightToken.id
|
|
270
258
|
}
|
|
271
259
|
|
|
@@ -360,7 +348,7 @@ LTP.prototype.getRegistryStats = function() {
|
|
|
360
348
|
* @returns {Object} Legal validity proof
|
|
361
349
|
*/
|
|
362
350
|
LTP.prototype.createLegalValidityProof = function(token, jurisdiction, nonce) {
|
|
363
|
-
return LTPProof.
|
|
351
|
+
return LTPProof.prepareLegalValidityProof(token, jurisdiction, nonce)
|
|
364
352
|
}
|
|
365
353
|
|
|
366
354
|
/**
|
package/lib/ltp/right.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var bsv = require('../../')
|
|
4
4
|
var Hash = bsv.crypto.Hash
|
|
5
5
|
var ECDSA = bsv.crypto.ECDSA
|
|
6
|
+
var Signature = bsv.crypto.Signature
|
|
6
7
|
var PrivateKey = bsv.PrivateKey
|
|
7
8
|
var $ = bsv.util.preconditions
|
|
8
9
|
var _ = require('../util/_')
|
|
@@ -40,7 +41,60 @@ var RightTypes = {
|
|
|
40
41
|
* Legal Right Token
|
|
41
42
|
*/
|
|
42
43
|
var RightToken = {
|
|
43
|
-
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Build an (unsigned) legal right token structure. Callers that need a signed
|
|
47
|
+
* token use prepareRightToken (signs inline) or add their own proof afterward
|
|
48
|
+
* (e.g. LTP#createRightToken via LTPProof).
|
|
49
|
+
* @param {String} type - Right type (from RightTypes)
|
|
50
|
+
* @param {String} issuerDID - Issuer DID
|
|
51
|
+
* @param {String} subjectDID - Subject DID
|
|
52
|
+
* @param {Object} claim - Legal claim object
|
|
53
|
+
* @param {PrivateKey} [issuerPrivateKey] - unused here; accepted for signature parity
|
|
54
|
+
* @param {Object} options - Additional options
|
|
55
|
+
* @returns {Object} Unsigned legal right token
|
|
56
|
+
*/
|
|
57
|
+
create: function(type, issuerDID, subjectDID, claim, issuerPrivateKey, options) {
|
|
58
|
+
options = options || {}
|
|
59
|
+
|
|
60
|
+
$.checkArgument(typeof type === 'string', 'Right type must be string')
|
|
61
|
+
$.checkArgument(typeof issuerDID === 'string', 'Issuer DID must be string')
|
|
62
|
+
$.checkArgument(typeof subjectDID === 'string', 'Subject DID must be string')
|
|
63
|
+
$.checkArgument(claim && typeof claim === 'object', 'Claim must be object')
|
|
64
|
+
|
|
65
|
+
var validTypes = Object.values(RightTypes)
|
|
66
|
+
$.checkArgument(validTypes.includes(type), 'Invalid right type: ' + type)
|
|
67
|
+
|
|
68
|
+
var rightToken = {
|
|
69
|
+
'@context': [
|
|
70
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
71
|
+
'https://smartledger.technology/contexts/ltp/v1'
|
|
72
|
+
],
|
|
73
|
+
id: 'urn:uuid:' + this._generateUUID(),
|
|
74
|
+
type: ['VerifiableCredential', 'LegalRightToken', type],
|
|
75
|
+
issuer: issuerDID,
|
|
76
|
+
issuanceDate: new Date().toISOString(),
|
|
77
|
+
credentialSubject: {
|
|
78
|
+
id: subjectDID,
|
|
79
|
+
rightType: type,
|
|
80
|
+
claim: claim,
|
|
81
|
+
jurisdiction: options.jurisdiction || 'US',
|
|
82
|
+
purpose: options.purpose || 'legal_right',
|
|
83
|
+
validFrom: options.validFrom || new Date().toISOString(),
|
|
84
|
+
validUntil: options.validUntil || null,
|
|
85
|
+
transferable: options.transferable !== false,
|
|
86
|
+
revocable: options.revocable !== false
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Add metadata
|
|
91
|
+
if (options.metadata) {
|
|
92
|
+
rightToken.credentialSubject.metadata = options.metadata
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return rightToken
|
|
96
|
+
},
|
|
97
|
+
|
|
44
98
|
/**
|
|
45
99
|
* Prepare legal right token for external processing
|
|
46
100
|
* @param {String} type - Right type (from RightTypes)
|
|
@@ -65,33 +119,8 @@ var RightToken = {
|
|
|
65
119
|
$.checkArgument(validTypes.includes(type), 'Invalid right type: ' + type)
|
|
66
120
|
|
|
67
121
|
try {
|
|
68
|
-
var rightToken =
|
|
69
|
-
|
|
70
|
-
'https://www.w3.org/2018/credentials/v1',
|
|
71
|
-
'https://smartledger.technology/contexts/ltp/v1'
|
|
72
|
-
],
|
|
73
|
-
id: 'urn:uuid:' + this._generateUUID(),
|
|
74
|
-
type: ['VerifiableCredential', 'LegalRightToken', type],
|
|
75
|
-
issuer: issuerDID,
|
|
76
|
-
issuanceDate: new Date().toISOString(),
|
|
77
|
-
credentialSubject: {
|
|
78
|
-
id: subjectDID,
|
|
79
|
-
rightType: type,
|
|
80
|
-
claim: claim,
|
|
81
|
-
jurisdiction: options.jurisdiction || 'US',
|
|
82
|
-
purpose: options.purpose || 'legal_right',
|
|
83
|
-
validFrom: options.validFrom || new Date().toISOString(),
|
|
84
|
-
validUntil: options.validUntil || null,
|
|
85
|
-
transferable: options.transferable !== false,
|
|
86
|
-
revocable: options.revocable !== false
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Add metadata
|
|
91
|
-
if (options.metadata) {
|
|
92
|
-
rightToken.credentialSubject.metadata = options.metadata
|
|
93
|
-
}
|
|
94
|
-
|
|
122
|
+
var rightToken = this.create(type, issuerDID, subjectDID, claim, issuerPrivateKey, options)
|
|
123
|
+
|
|
95
124
|
// Sign the token
|
|
96
125
|
var signedToken = this._signToken(rightToken, issuerPrivateKey)
|
|
97
126
|
|
|
@@ -540,6 +569,72 @@ var RightToken = {
|
|
|
540
569
|
getRightTypes: function() {
|
|
541
570
|
return RightTypes
|
|
542
571
|
},
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Verify a token signed by _signToken against a public key.
|
|
575
|
+
*
|
|
576
|
+
* Tokens produced here carry a detached ES256K JWS in proof.jws plus the
|
|
577
|
+
* canonical tokenHash. Verification recomputes the canonical hash (catching
|
|
578
|
+
* tampering of any field), confirms it matches the stored tokenHash, then
|
|
579
|
+
* checks the ECDSA signature embedded in the JWS against the supplied public
|
|
580
|
+
* key. This is the verification counterpart to _signToken and works for
|
|
581
|
+
* right, transfer and obligation tokens alike — all are signed the same way.
|
|
582
|
+
*
|
|
583
|
+
* @param {Object} token - Token with a proof.jws produced by _signToken
|
|
584
|
+
* @param {String|PublicKey} publicKey - Expected signer's public key
|
|
585
|
+
* @returns {Object} { valid, errors, tokenHash, verifiedAt }
|
|
586
|
+
*/
|
|
587
|
+
verifyToken: function(token, publicKey) {
|
|
588
|
+
var errors = []
|
|
589
|
+
try {
|
|
590
|
+
$.checkArgument(token && typeof token === 'object', 'Invalid token')
|
|
591
|
+
|
|
592
|
+
if (!token.proof || !token.proof.jws) {
|
|
593
|
+
errors.push('Missing signature proof')
|
|
594
|
+
return { valid: false, errors: errors }
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
var pubKey = publicKey instanceof bsv.PublicKey
|
|
598
|
+
? publicKey
|
|
599
|
+
: bsv.PublicKey.fromString(publicKey)
|
|
600
|
+
|
|
601
|
+
// Recompute the canonical hash and confirm integrity.
|
|
602
|
+
var canonicalToken = this._canonicalizeToken(token)
|
|
603
|
+
var tokenHash = Hash.sha256(Buffer.from(canonicalToken))
|
|
604
|
+
|
|
605
|
+
if (token.tokenHash && token.tokenHash !== tokenHash.toString('hex')) {
|
|
606
|
+
errors.push('Token hash mismatch')
|
|
607
|
+
return { valid: false, errors: errors }
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Extract the DER signature from the detached JWS (header..signature).
|
|
611
|
+
var parts = token.proof.jws.split('.')
|
|
612
|
+
if (parts.length !== 3 || !parts[2]) {
|
|
613
|
+
errors.push('Malformed JWS')
|
|
614
|
+
return { valid: false, errors: errors }
|
|
615
|
+
}
|
|
616
|
+
var signature = Signature.fromDER(Buffer.from(parts[2], 'base64url'))
|
|
617
|
+
|
|
618
|
+
var ecdsa = new ECDSA()
|
|
619
|
+
ecdsa.hashbuf = tokenHash
|
|
620
|
+
ecdsa.sig = signature
|
|
621
|
+
ecdsa.pubkey = pubKey
|
|
622
|
+
|
|
623
|
+
if (!ecdsa.verify().verified) {
|
|
624
|
+
errors.push('Signature verification failed')
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
valid: errors.length === 0,
|
|
629
|
+
errors: errors,
|
|
630
|
+
tokenHash: tokenHash.toString('hex'),
|
|
631
|
+
verifiedAt: new Date().toISOString()
|
|
632
|
+
}
|
|
633
|
+
} catch (error) {
|
|
634
|
+
errors.push(error.message)
|
|
635
|
+
return { valid: false, errors: errors }
|
|
636
|
+
}
|
|
637
|
+
},
|
|
543
638
|
|
|
544
639
|
/**
|
|
545
640
|
* Sign a token with private key
|
|
@@ -577,19 +672,23 @@ var RightToken = {
|
|
|
577
672
|
},
|
|
578
673
|
|
|
579
674
|
/**
|
|
580
|
-
*
|
|
675
|
+
* Build a detached compact JWS (RFC 7797) for a token hash + ECDSA signature.
|
|
676
|
+
* The payload segment is intentionally empty (header..signature) because the
|
|
677
|
+
* signed content is the canonical token hash, carried separately as tokenHash.
|
|
581
678
|
* @private
|
|
679
|
+
* @param {Buffer} hash - 32-byte canonical token hash
|
|
680
|
+
* @param {Signature} signature - ECDSA signature over the hash
|
|
681
|
+
* @returns {String} compact detached JWS
|
|
582
682
|
*/
|
|
583
|
-
|
|
683
|
+
_createJWS: function(hash, signature) {
|
|
584
684
|
var header = {
|
|
585
685
|
alg: 'ES256K',
|
|
586
686
|
typ: 'JWT'
|
|
587
687
|
}
|
|
588
|
-
|
|
688
|
+
|
|
589
689
|
var headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url')
|
|
590
|
-
var payloadB64 = hash.toString('base64url')
|
|
591
690
|
var signatureB64 = signature.toDER().toString('base64url')
|
|
592
|
-
|
|
691
|
+
|
|
593
692
|
return headerB64 + '..' + signatureB64
|
|
594
693
|
},
|
|
595
694
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartledger/bsv",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.2",
|
|
4
4
|
"description": "🚀 Complete Bitcoin SV development framework with legally-recognizable DID:web + W3C VC-JWT toolkit, Legal Token Protocol (LTP), Global Digital Attestation Framework (GDAF), StatusList2021 revocation, and 16 flexible loading options. Standards-based credentials with ES256/ES256K support, on-chain BSV anchoring, and comprehensive Bitcoin SV API. Perfect for legal tokens, verifiable credentials, DeFi, smart contracts, and secure Bitcoin applications.",
|
|
5
5
|
"author": "SmartLedger Technology <hello@smartledger.technology> (https://smartledger.technology)",
|
|
6
6
|
"homepage": "https://github.com/codenlighten/smartledger-bsv#readme",
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
"@noble/curves": "2.2.0",
|
|
197
197
|
"@noble/hashes": "2.2.0",
|
|
198
198
|
"aes-js": "^3.1.2",
|
|
199
|
-
"bn.js": "=4.
|
|
199
|
+
"bn.js": "=4.12.3",
|
|
200
200
|
"bs58": "=4.0.1",
|
|
201
201
|
"clone-deep": "^4.0.1",
|
|
202
202
|
"hash.js": "^1.1.7",
|
|
@@ -212,8 +212,8 @@
|
|
|
212
212
|
"chai": "4.2.0",
|
|
213
213
|
"crypto-browserify": "^3.12.1",
|
|
214
214
|
"jose": "^5.10.0",
|
|
215
|
-
"mocha": "^
|
|
216
|
-
"nyc": "^
|
|
215
|
+
"mocha": "^11.7.6",
|
|
216
|
+
"nyc": "^18.0.0",
|
|
217
217
|
"path-browserify": "^1.0.1",
|
|
218
218
|
"process": "^0.11.10",
|
|
219
219
|
"sinon": "7.2.3",
|