@smartledger/bsv 3.3.5 → 3.4.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/README.md +147 -20
- package/anchor-entry.js +1 -0
- package/bin/cli.js +349 -0
- package/bsv-smartcontract.min.js +9 -9
- package/bsv.min.js +8 -8
- package/build/webpack.anchor.config.js +21 -0
- package/build/webpack.didweb.config.js +21 -0
- package/build/webpack.statuslist.config.js +22 -0
- package/build/webpack.vcjwt.config.js +21 -0
- package/demos/browser-test.html +1 -1
- package/didweb-entry.js +1 -0
- package/docs/technical/roadmap.md +3 -3
- package/index.js +28 -0
- package/lib/anchor/index.js +102 -0
- package/lib/didweb/index.js +177 -0
- package/lib/statuslist/index.js +164 -0
- package/lib/vcjwt/index.js +189 -0
- package/package.json +11 -3
- package/statuslist-entry.js +1 -0
- package/vcjwt-entry.js +1 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* VC-JWT Module
|
|
5
|
+
* W3C Verifiable Credentials using JWT/JWS
|
|
6
|
+
* Supports ES256 (P-256) and ES256K (secp256k1)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
var crypto = require('crypto')
|
|
10
|
+
|
|
11
|
+
// Base64URL encoding
|
|
12
|
+
function base64UrlEncode(buffer) {
|
|
13
|
+
return buffer.toString('base64')
|
|
14
|
+
.replace(/\+/g, '-')
|
|
15
|
+
.replace(/\//g, '_')
|
|
16
|
+
.replace(/=/g, '')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Base64URL decoding
|
|
20
|
+
function base64UrlDecode(str) {
|
|
21
|
+
str = str.replace(/-/g, '+').replace(/_/g, '/')
|
|
22
|
+
while (str.length % 4) {
|
|
23
|
+
str += '='
|
|
24
|
+
}
|
|
25
|
+
return Buffer.from(str, 'base64')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Issue a Verifiable Credential as JWT
|
|
29
|
+
async function issueVcJwt(params) {
|
|
30
|
+
if (!params.issuerDid || !params.subjectId || !params.credentialSubject || !params.privateJwk) {
|
|
31
|
+
throw new Error('issuerDid, subjectId, credentialSubject, and privateJwk are required')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var alg = params.alg || 'ES256'
|
|
35
|
+
var kid = params.kid || params.privateJwk.kid
|
|
36
|
+
var types = params.types || ['VerifiableCredential']
|
|
37
|
+
var expSeconds = params.expSeconds || (365 * 24 * 60 * 60) // 1 year default
|
|
38
|
+
|
|
39
|
+
var now = Math.floor(Date.now() / 1000)
|
|
40
|
+
var issuedAt = new Date().toISOString()
|
|
41
|
+
|
|
42
|
+
// Build VC payload
|
|
43
|
+
var vcPayload = {
|
|
44
|
+
'@context': [
|
|
45
|
+
'https://www.w3.org/2018/credentials/v1'
|
|
46
|
+
],
|
|
47
|
+
type: types,
|
|
48
|
+
issuer: params.issuerDid,
|
|
49
|
+
issuanceDate: issuedAt,
|
|
50
|
+
credentialSubject: Object.assign({
|
|
51
|
+
id: params.subjectId
|
|
52
|
+
}, params.credentialSubject)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Build JWT claims
|
|
56
|
+
var jwtPayload = {
|
|
57
|
+
iss: params.issuerDid,
|
|
58
|
+
sub: params.subjectId,
|
|
59
|
+
iat: now,
|
|
60
|
+
exp: now + expSeconds,
|
|
61
|
+
vc: vcPayload
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Build JWT header
|
|
65
|
+
var header = {
|
|
66
|
+
alg: alg,
|
|
67
|
+
typ: 'JWT',
|
|
68
|
+
kid: kid
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Encode header and payload
|
|
72
|
+
var headerB64 = base64UrlEncode(Buffer.from(JSON.stringify(header)))
|
|
73
|
+
var payloadB64 = base64UrlEncode(Buffer.from(JSON.stringify(jwtPayload)))
|
|
74
|
+
var signingInput = headerB64 + '.' + payloadB64
|
|
75
|
+
|
|
76
|
+
// Sign with private key
|
|
77
|
+
var privateKey = crypto.createPrivateKey({
|
|
78
|
+
key: params.privateJwk,
|
|
79
|
+
format: 'jwk'
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
var signature
|
|
83
|
+
if (alg === 'ES256') {
|
|
84
|
+
signature = crypto.sign('sha256', Buffer.from(signingInput), privateKey)
|
|
85
|
+
} else if (alg === 'ES256K') {
|
|
86
|
+
signature = crypto.sign('sha256', Buffer.from(signingInput), privateKey)
|
|
87
|
+
} else {
|
|
88
|
+
throw new Error('Unsupported algorithm: ' + alg)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
var signatureB64 = base64UrlEncode(signature)
|
|
92
|
+
var jwt = signingInput + '.' + signatureB64
|
|
93
|
+
|
|
94
|
+
return { jwt: jwt }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Verify a VC-JWT
|
|
98
|
+
async function verifyVcJwt(jwt, opts) {
|
|
99
|
+
opts = opts || {}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Parse JWT
|
|
103
|
+
var parts = jwt.split('.')
|
|
104
|
+
if (parts.length !== 3) {
|
|
105
|
+
return { valid: false, error: 'Invalid JWT format' }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
var headerB64 = parts[0]
|
|
109
|
+
var payloadB64 = parts[1]
|
|
110
|
+
var signatureB64 = parts[2]
|
|
111
|
+
|
|
112
|
+
var header = JSON.parse(base64UrlDecode(headerB64).toString())
|
|
113
|
+
var payload = JSON.parse(base64UrlDecode(payloadB64).toString())
|
|
114
|
+
var signature = base64UrlDecode(signatureB64)
|
|
115
|
+
|
|
116
|
+
// Check expiration
|
|
117
|
+
var now = Math.floor(Date.now() / 1000)
|
|
118
|
+
var clockTolerance = opts.clockToleranceSec || 60
|
|
119
|
+
|
|
120
|
+
if (payload.exp && payload.exp < (now - clockTolerance)) {
|
|
121
|
+
return { valid: false, error: 'JWT expired', header: header, payload: payload }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check issuer if expected
|
|
125
|
+
if (opts.expectedIssuerDid && payload.iss !== opts.expectedIssuerDid) {
|
|
126
|
+
return { valid: false, error: 'Unexpected issuer', header: header, payload: payload }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Get public key from DID resolver or use default resolver
|
|
130
|
+
var publicKey
|
|
131
|
+
if (opts.didResolver) {
|
|
132
|
+
var resolved = await opts.didResolver(payload.iss)
|
|
133
|
+
if (!resolved || !resolved.jwks || !resolved.jwks.keys) {
|
|
134
|
+
return { valid: false, error: 'Failed to resolve issuer DID', header: header, payload: payload }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Find matching key by kid
|
|
138
|
+
var matchingKey = resolved.jwks.keys.find(function(k) {
|
|
139
|
+
return k.kid === header.kid
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
if (!matchingKey) {
|
|
143
|
+
return { valid: false, error: 'Key not found in JWKS', header: header, payload: payload }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
publicKey = matchingKey
|
|
147
|
+
} else {
|
|
148
|
+
// Without resolver, verification cannot proceed
|
|
149
|
+
return { valid: false, error: 'DID resolver required for verification', header: header, payload: payload }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Verify signature
|
|
153
|
+
var signingInput = headerB64 + '.' + payloadB64
|
|
154
|
+
var pubKey = crypto.createPublicKey({
|
|
155
|
+
key: publicKey,
|
|
156
|
+
format: 'jwk'
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
var isValid = crypto.verify(
|
|
160
|
+
'sha256',
|
|
161
|
+
Buffer.from(signingInput),
|
|
162
|
+
pubKey,
|
|
163
|
+
signature
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if (!isValid) {
|
|
167
|
+
return { valid: false, error: 'Invalid signature', header: header, payload: payload }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
valid: true,
|
|
172
|
+
header: header,
|
|
173
|
+
payload: payload
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
} catch (error) {
|
|
177
|
+
return {
|
|
178
|
+
valid: false,
|
|
179
|
+
error: error.message || 'Verification failed'
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = {
|
|
185
|
+
issueVcJwt: issueVcJwt,
|
|
186
|
+
verifyVcJwt: verifyVcJwt,
|
|
187
|
+
base64UrlEncode: base64UrlEncode,
|
|
188
|
+
base64UrlDecode: base64UrlDecode
|
|
189
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartledger/bsv",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "🚀 Complete Bitcoin SV development framework with Legal Token Protocol (LTP), Global Digital Attestation Framework (GDAF),
|
|
3
|
+
"version": "3.4.0",
|
|
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",
|
|
7
7
|
"bugs": {
|
|
8
8
|
"url": "https://github.com/codenlighten/smartledger-bsv/issues"
|
|
9
9
|
},
|
|
10
10
|
"main": "index.js",
|
|
11
|
+
"bin": {
|
|
12
|
+
"smartledger-bsv": "./bin/cli.js"
|
|
13
|
+
},
|
|
11
14
|
"scripts": {
|
|
12
15
|
"lint": "standard",
|
|
13
16
|
"test": "mocha",
|
|
@@ -46,11 +49,16 @@
|
|
|
46
49
|
"build-covenant": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack covenant-entry.js --config build/webpack.covenant.config.js",
|
|
47
50
|
"build-script-helper": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack script-helper-entry.js --config build/webpack.script-helper.config.js",
|
|
48
51
|
"build-security": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack security-entry.js --config build/webpack.security.config.js",
|
|
52
|
+
"build-didweb": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack didweb-entry.js --config build/webpack.didweb.config.js",
|
|
53
|
+
"build-vcjwt": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack vcjwt-entry.js --config build/webpack.vcjwt.config.js",
|
|
54
|
+
"build-statuslist": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack statuslist-entry.js --config build/webpack.statuslist.config.js",
|
|
55
|
+
"build-anchor": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack anchor-entry.js --config build/webpack.anchor.config.js",
|
|
49
56
|
"build-bundle": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack bundle-entry.js --config build/webpack.bundle.config.js",
|
|
50
57
|
"build": "npm run build-bsv && npm run build-ecies && npm run build-message && npm run build-mnemonic && npm run build-shamir && npm run build-smartcontract",
|
|
51
58
|
"build-specialized": "npm run build-covenant && npm run build-script-helper && npm run build-security",
|
|
59
|
+
"build-credentials": "npm run build-didweb && npm run build-vcjwt && npm run build-statuslist && npm run build-anchor",
|
|
52
60
|
"build-advanced": "npm run build-ltp && npm run build-gdaf",
|
|
53
|
-
"build-all": "npm run build && npm run build-bundle && npm run build-specialized && npm run build-advanced",
|
|
61
|
+
"build-all": "npm run build && npm run build-bundle && npm run build-specialized && npm run build-advanced && npm run build-credentials",
|
|
54
62
|
"test:browser": "echo 'Open tests/standalone-modules-test.html in browser for comprehensive testing'",
|
|
55
63
|
"test:bundle": "echo 'Open tests/bundle-completeness-test.html in browser to verify bundle completeness'",
|
|
56
64
|
"preimage:extract": "node examples/preimage/extract_preimage_bidirectional.js",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/statuslist')
|
package/vcjwt-entry.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/vcjwt')
|