@sphereon/ssi-sdk.vc-status-list 0.34.1-next.3 → 0.34.1-next.40
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/dist/index.cjs +692 -119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -37
- package/dist/index.d.ts +163 -37
- package/dist/index.js +693 -120
- package/dist/index.js.map +1 -1
- package/package.json +10 -7
- package/src/functions.ts +126 -47
- package/src/impl/BitstringStatusListImplementation.ts +496 -0
- package/src/impl/IStatusList.ts +102 -8
- package/src/impl/OAuthStatusList.ts +133 -38
- package/src/impl/StatusList2021.ts +114 -34
- package/src/impl/StatusListFactory.ts +2 -0
- package/src/index.ts +1 -0
- package/src/types/BitstringStatusList.ts +4 -0
- package/src/types/index.ts +56 -35
- package/src/utils.ts +82 -20
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitstring Status List Implementation
|
|
3
|
+
*
|
|
4
|
+
* This module implements the W3C Bitstring Status List specification for managing
|
|
5
|
+
* credential status information. It provides functionality to create, update, and
|
|
6
|
+
* check the status of verifiable credentials using compressed bitstring status lists.
|
|
7
|
+
*
|
|
8
|
+
* Key features:
|
|
9
|
+
* - Create new bitstring status lists with configurable purposes and bit sizes
|
|
10
|
+
* - Update individual credential status entries in existing lists
|
|
11
|
+
* - Check the status of specific credentials by index
|
|
12
|
+
* - Support for multiple proof formats (JWT, LDS, CBOR)
|
|
13
|
+
* - Integration with Veramo agent context for credential signing
|
|
14
|
+
*
|
|
15
|
+
* @author Sphereon International B.V.
|
|
16
|
+
* @since 2024
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { IAgentContext } from '@veramo/core'
|
|
20
|
+
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
CredentialMapper,
|
|
24
|
+
type CredentialProofFormat,
|
|
25
|
+
DocumentFormat,
|
|
26
|
+
type IIssuer,
|
|
27
|
+
type StatusListCredential,
|
|
28
|
+
StatusListType,
|
|
29
|
+
} from '@sphereon/ssi-types'
|
|
30
|
+
|
|
31
|
+
import type { IBitstringStatusListImplementationResult, IExtractedCredentialDetails, IStatusList } from './IStatusList'
|
|
32
|
+
import {
|
|
33
|
+
CheckStatusIndexArgs,
|
|
34
|
+
CreateStatusListArgs,
|
|
35
|
+
IMergeDetailsWithEntityArgs,
|
|
36
|
+
IToDetailsFromCredentialArgs,
|
|
37
|
+
StatusListResult,
|
|
38
|
+
UpdateStatusListFromEncodedListArgs,
|
|
39
|
+
UpdateStatusListIndexArgs,
|
|
40
|
+
} from '../types'
|
|
41
|
+
|
|
42
|
+
import { assertValidProofType, ensureDate, getAssertedProperty, getAssertedValue, getAssertedValues } from '../utils'
|
|
43
|
+
import { BitstringStatusListCredential } from '../types/BitstringStatusList'
|
|
44
|
+
import {
|
|
45
|
+
BitstreamStatusList,
|
|
46
|
+
BitstringStatusListCredentialUnsigned,
|
|
47
|
+
BitstringStatusPurpose,
|
|
48
|
+
createStatusListCredential,
|
|
49
|
+
} from '@4sure-tech/vc-bitstring-status-lists'
|
|
50
|
+
import {
|
|
51
|
+
BitstringStatusListEntity,
|
|
52
|
+
BitstringStatusListEntryCredentialStatus,
|
|
53
|
+
IBitstringStatusListEntryEntity,
|
|
54
|
+
IStatusListEntryEntity,
|
|
55
|
+
StatusListEntity,
|
|
56
|
+
} from '@sphereon/ssi-sdk.data-store'
|
|
57
|
+
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
58
|
+
|
|
59
|
+
export const DEFAULT_LIST_LENGTH = 131072 // W3C spec minimum
|
|
60
|
+
export const DEFAULT_PROOF_FORMAT = 'vc+jwt' as CredentialProofFormat
|
|
61
|
+
export const DEFAULT_STATUS_PURPOSE: BitstringStatusPurpose = 'revocation'
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Implementation of the IStatusList interface for W3C Bitstring Status Lists
|
|
65
|
+
*
|
|
66
|
+
* This class handles the creation, updating, and verification of bitstring status lists
|
|
67
|
+
* according to the W3C Bitstring Status List specification. It supports multiple
|
|
68
|
+
* status purposes (revocation, suspension, etc.) and various proof formats.
|
|
69
|
+
*/
|
|
70
|
+
export class BitstringStatusListImplementation implements IStatusList {
|
|
71
|
+
/**
|
|
72
|
+
* Creates a new bitstring status list with the specified configuration
|
|
73
|
+
*
|
|
74
|
+
* @param args - Configuration for the new status list including issuer, purpose, and size
|
|
75
|
+
* @param context - Veramo agent context for credential operations
|
|
76
|
+
* @returns Promise resolving to the created status list details
|
|
77
|
+
*/
|
|
78
|
+
async createNewStatusList(
|
|
79
|
+
args: CreateStatusListArgs,
|
|
80
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
81
|
+
): Promise<StatusListResult> {
|
|
82
|
+
if (!args.bitstringStatusList) {
|
|
83
|
+
throw new Error('BitstringStatusList options are required for type BitstringStatusList')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const length = args?.length ?? DEFAULT_LIST_LENGTH
|
|
87
|
+
const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
|
|
88
|
+
assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
|
|
89
|
+
|
|
90
|
+
const { issuer, id } = args
|
|
91
|
+
const correlationId = getAssertedValue('correlationId', args.correlationId)
|
|
92
|
+
const { statusPurpose, bitsPerStatus, validFrom, validUntil, ttl } = args.bitstringStatusList
|
|
93
|
+
|
|
94
|
+
const unsignedCredential: BitstringStatusListCredentialUnsigned = await createStatusListCredential({
|
|
95
|
+
id,
|
|
96
|
+
issuer,
|
|
97
|
+
statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
|
|
98
|
+
validFrom: ensureDate(validFrom),
|
|
99
|
+
validUntil: ensureDate(validUntil),
|
|
100
|
+
ttl,
|
|
101
|
+
})
|
|
102
|
+
const statusListCredential = await this.createVerifiableCredential(
|
|
103
|
+
{
|
|
104
|
+
unsignedCredential,
|
|
105
|
+
id,
|
|
106
|
+
issuer,
|
|
107
|
+
proofFormat,
|
|
108
|
+
keyRef: args.keyRef,
|
|
109
|
+
},
|
|
110
|
+
context,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
115
|
+
statusListCredential,
|
|
116
|
+
bitstringStatusList: {
|
|
117
|
+
statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
|
|
118
|
+
...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
|
|
119
|
+
...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
|
|
120
|
+
ttl,
|
|
121
|
+
bitsPerStatus,
|
|
122
|
+
},
|
|
123
|
+
length,
|
|
124
|
+
type: StatusListType.BitstringStatusList,
|
|
125
|
+
proofFormat,
|
|
126
|
+
id,
|
|
127
|
+
correlationId,
|
|
128
|
+
issuer,
|
|
129
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Updates the status of a specific credential in an existing status list
|
|
135
|
+
*
|
|
136
|
+
* @param args - Update parameters including the status list credential, index, and new value
|
|
137
|
+
* @param context - Veramo agent context for credential operations
|
|
138
|
+
* @returns Promise resolving to the updated status list details
|
|
139
|
+
*/
|
|
140
|
+
async updateStatusListIndex(
|
|
141
|
+
args: UpdateStatusListIndexArgs,
|
|
142
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
143
|
+
): Promise<StatusListResult> {
|
|
144
|
+
if (!args.bitsPerStatus || args.bitsPerStatus < 1) {
|
|
145
|
+
return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListIndex)'))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const credential = args.statusListCredential
|
|
149
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
150
|
+
const { issuer, credentialSubject } = uniform
|
|
151
|
+
const id = getAssertedValue('id', uniform.id)
|
|
152
|
+
const origEncodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
153
|
+
|
|
154
|
+
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
155
|
+
const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: origEncodedList, statusSize: args.bitsPerStatus })
|
|
156
|
+
const bitstringStatusId = args.value as number
|
|
157
|
+
statusList.setStatus(index, bitstringStatusId)
|
|
158
|
+
|
|
159
|
+
const proofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
160
|
+
|
|
161
|
+
const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
162
|
+
|
|
163
|
+
const statusPurpose = getAssertedProperty('statusPurpose', credSubject)
|
|
164
|
+
|
|
165
|
+
const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : undefined
|
|
166
|
+
const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
|
|
167
|
+
const ttl = credSubject.ttl
|
|
168
|
+
|
|
169
|
+
const unsignedCredential: BitstringStatusListCredentialUnsigned = await createStatusListCredential({
|
|
170
|
+
id,
|
|
171
|
+
issuer,
|
|
172
|
+
statusList,
|
|
173
|
+
statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
|
|
174
|
+
validFrom: ensureDate(validFrom),
|
|
175
|
+
validUntil: ensureDate(validUntil),
|
|
176
|
+
ttl,
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const updatedCredential = await this.createVerifiableCredential(
|
|
180
|
+
{
|
|
181
|
+
unsignedCredential,
|
|
182
|
+
id,
|
|
183
|
+
issuer,
|
|
184
|
+
proofFormat,
|
|
185
|
+
keyRef: args.keyRef,
|
|
186
|
+
},
|
|
187
|
+
context,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
statusListCredential: updatedCredential,
|
|
192
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
193
|
+
bitstringStatusList: {
|
|
194
|
+
statusPurpose,
|
|
195
|
+
...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
|
|
196
|
+
...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
|
|
197
|
+
bitsPerStatus: args.bitsPerStatus,
|
|
198
|
+
ttl,
|
|
199
|
+
},
|
|
200
|
+
length: statusList.getLength(),
|
|
201
|
+
type: StatusListType.BitstringStatusList,
|
|
202
|
+
proofFormat,
|
|
203
|
+
id,
|
|
204
|
+
issuer,
|
|
205
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Updates a status list by decoding an encoded list, modifying it, and re-encoding
|
|
211
|
+
*
|
|
212
|
+
* @param args - Update parameters including encoded list, index, and new value
|
|
213
|
+
* @param context - Veramo agent context for credential operations
|
|
214
|
+
* @returns Promise resolving to the updated status list details
|
|
215
|
+
*/
|
|
216
|
+
async updateStatusListFromEncodedList(
|
|
217
|
+
args: UpdateStatusListFromEncodedListArgs,
|
|
218
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
219
|
+
): Promise<StatusListResult> {
|
|
220
|
+
if (!args.bitstringStatusList) {
|
|
221
|
+
throw new Error('bitstringStatusList options required for type BitstringStatusList')
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (args.bitstringStatusList.bitsPerStatus < 1) {
|
|
225
|
+
return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListFromEncodedList)'))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const { statusPurpose, bitsPerStatus, ttl, validFrom, validUntil } = args.bitstringStatusList
|
|
229
|
+
|
|
230
|
+
const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
|
|
231
|
+
assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
|
|
232
|
+
|
|
233
|
+
const { issuer, id } = getAssertedValues(args)
|
|
234
|
+
const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: args.encodedList, statusSize: bitsPerStatus })
|
|
235
|
+
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
236
|
+
statusList.setStatus(index, args.value)
|
|
237
|
+
|
|
238
|
+
const unsignedCredential: BitstringStatusListCredentialUnsigned = await createStatusListCredential({
|
|
239
|
+
id,
|
|
240
|
+
issuer,
|
|
241
|
+
statusList,
|
|
242
|
+
statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
|
|
243
|
+
validFrom: ensureDate(validFrom),
|
|
244
|
+
validUntil: ensureDate(validUntil),
|
|
245
|
+
ttl,
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const credential = await this.createVerifiableCredential(
|
|
249
|
+
{
|
|
250
|
+
unsignedCredential,
|
|
251
|
+
id,
|
|
252
|
+
issuer,
|
|
253
|
+
proofFormat,
|
|
254
|
+
keyRef: args.keyRef,
|
|
255
|
+
},
|
|
256
|
+
context,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
type: StatusListType.BitstringStatusList,
|
|
261
|
+
statusListCredential: credential,
|
|
262
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
263
|
+
bitstringStatusList: {
|
|
264
|
+
statusPurpose,
|
|
265
|
+
bitsPerStatus,
|
|
266
|
+
...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
|
|
267
|
+
...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
|
|
268
|
+
ttl,
|
|
269
|
+
},
|
|
270
|
+
length: statusList.getLength(),
|
|
271
|
+
proofFormat: args.proofFormat ?? 'lds',
|
|
272
|
+
id,
|
|
273
|
+
issuer,
|
|
274
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Checks the status of a specific credential by its index in the status list
|
|
280
|
+
*
|
|
281
|
+
* @param args - Check parameters including the status list credential and index
|
|
282
|
+
* @returns Promise resolving to the status value at the specified index
|
|
283
|
+
*/
|
|
284
|
+
async checkStatusIndex(args: CheckStatusIndexArgs): Promise<number> {
|
|
285
|
+
if (!args.bitsPerStatus || args.bitsPerStatus < 1) {
|
|
286
|
+
return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (checkStatusIndex)'))
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const uniform = CredentialMapper.toUniformCredential(args.statusListCredential)
|
|
290
|
+
const { credentialSubject } = uniform
|
|
291
|
+
const encodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
292
|
+
|
|
293
|
+
const statusList = await BitstreamStatusList.decode({ encodedList, statusSize: args.bitsPerStatus })
|
|
294
|
+
const numIndex = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
295
|
+
if (statusList.getLength() <= numIndex) {
|
|
296
|
+
throw new Error(`Status list index out of bounds, has ${statusList.getLength()} entries, requested ${numIndex}`)
|
|
297
|
+
}
|
|
298
|
+
return statusList.getStatus(numIndex)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Performs the initial parsing of a StatusListCredential.
|
|
303
|
+
* This method handles expensive operations like JWT/CWT decoding once.
|
|
304
|
+
* It extracts all details available from the credential payload itself.
|
|
305
|
+
*/
|
|
306
|
+
async extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails> {
|
|
307
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
308
|
+
const { issuer, credentialSubject } = uniform
|
|
309
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
id: getAssertedValue('id', uniform.id),
|
|
313
|
+
issuer,
|
|
314
|
+
encodedList: getAssertedProperty('encodedList', subject),
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Converts a status list credential payload to detailed status list information
|
|
320
|
+
*
|
|
321
|
+
* @param args - Conversion parameters including the status list payload
|
|
322
|
+
* @returns Promise resolving to detailed status list information
|
|
323
|
+
*/
|
|
324
|
+
// For CREATE and READ contexts
|
|
325
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IBitstringStatusListImplementationResult>
|
|
326
|
+
// For UPDATE contexts
|
|
327
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IBitstringStatusListImplementationResult>
|
|
328
|
+
async toStatusListDetails(
|
|
329
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
330
|
+
): Promise<StatusListResult & IBitstringStatusListImplementationResult> {
|
|
331
|
+
if ('statusListCredential' in args) {
|
|
332
|
+
// CREATE/READ context
|
|
333
|
+
const { statusListCredential, bitsPerStatus, correlationId, driverType } = args
|
|
334
|
+
if (!bitsPerStatus || bitsPerStatus < 1) {
|
|
335
|
+
return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher'))
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const uniform = CredentialMapper.toUniformCredential(statusListCredential)
|
|
339
|
+
const { issuer, credentialSubject } = uniform
|
|
340
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
341
|
+
|
|
342
|
+
const id = getAssertedValue('id', uniform.id)
|
|
343
|
+
const encodedList = getAssertedProperty('encodedList', subject)
|
|
344
|
+
const statusPurpose = getAssertedProperty('statusPurpose', subject)
|
|
345
|
+
const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : undefined
|
|
346
|
+
const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
|
|
347
|
+
const ttl = subject.ttl
|
|
348
|
+
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListCredential) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
349
|
+
const statuslistLength = BitstreamStatusList.getStatusListLength(encodedList, bitsPerStatus)
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
id,
|
|
353
|
+
encodedList,
|
|
354
|
+
issuer,
|
|
355
|
+
type: StatusListType.BitstringStatusList,
|
|
356
|
+
proofFormat,
|
|
357
|
+
length: statuslistLength,
|
|
358
|
+
statusListCredential,
|
|
359
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
360
|
+
correlationId,
|
|
361
|
+
driverType,
|
|
362
|
+
statusPurpose,
|
|
363
|
+
bitsPerStatus,
|
|
364
|
+
...(validFrom && { validFrom }),
|
|
365
|
+
...(validUntil && { validUntil }),
|
|
366
|
+
...(ttl && { ttl }),
|
|
367
|
+
bitstringStatusList: {
|
|
368
|
+
statusPurpose,
|
|
369
|
+
bitsPerStatus,
|
|
370
|
+
...(validFrom && { validFrom }),
|
|
371
|
+
...(validUntil && { validUntil }),
|
|
372
|
+
...(ttl && { ttl }),
|
|
373
|
+
},
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
// UPDATE context
|
|
377
|
+
const { extractedDetails, statusListEntity } = args
|
|
378
|
+
const bitstringEntity = statusListEntity as BitstringStatusListEntity
|
|
379
|
+
if (!bitstringEntity.bitsPerStatus) {
|
|
380
|
+
return Promise.reject(Error('bitsPerStatus must be present for a bitstring status list'))
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const proofFormat: CredentialProofFormat =
|
|
384
|
+
CredentialMapper.detectDocumentType(statusListEntity.statusListCredential!) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
385
|
+
const statuslistLength = BitstreamStatusList.getStatusListLength(extractedDetails.encodedList, bitstringEntity.bitsPerStatus)
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
id: extractedDetails.id,
|
|
389
|
+
encodedList: extractedDetails.encodedList,
|
|
390
|
+
issuer: extractedDetails.issuer,
|
|
391
|
+
type: StatusListType.BitstringStatusList,
|
|
392
|
+
proofFormat,
|
|
393
|
+
length: statuslistLength,
|
|
394
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
395
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
396
|
+
correlationId: statusListEntity.correlationId,
|
|
397
|
+
driverType: statusListEntity.driverType,
|
|
398
|
+
statusPurpose: bitstringEntity.statusPurpose,
|
|
399
|
+
bitsPerStatus: bitstringEntity.bitsPerStatus,
|
|
400
|
+
...(bitstringEntity.validFrom && { validFrom: bitstringEntity.validFrom }),
|
|
401
|
+
...(bitstringEntity.validUntil && { validUntil: bitstringEntity.validUntil }),
|
|
402
|
+
...(bitstringEntity.ttl && { ttl: bitstringEntity.ttl }),
|
|
403
|
+
bitstringStatusList: {
|
|
404
|
+
statusPurpose: bitstringEntity.statusPurpose,
|
|
405
|
+
bitsPerStatus: bitstringEntity.bitsPerStatus,
|
|
406
|
+
...(bitstringEntity.validFrom && { validFrom: bitstringEntity.validFrom }),
|
|
407
|
+
...(bitstringEntity.validUntil && { validUntil: bitstringEntity.validUntil }),
|
|
408
|
+
...(bitstringEntity.ttl && { ttl: bitstringEntity.ttl }),
|
|
409
|
+
},
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Creates a credential status entry for a specific credential in a status list
|
|
416
|
+
*
|
|
417
|
+
* @param args - Parameters including the status list, entry details, and index
|
|
418
|
+
* @returns Promise resolving to the credential status entry
|
|
419
|
+
*/
|
|
420
|
+
async createCredentialStatus(args: {
|
|
421
|
+
statusList: StatusListEntity
|
|
422
|
+
statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
|
|
423
|
+
statusListIndex: number
|
|
424
|
+
}): Promise<BitstringStatusListEntryCredentialStatus> {
|
|
425
|
+
const { statusList, statusListEntry, statusListIndex } = args
|
|
426
|
+
|
|
427
|
+
const bitstringStatusList = statusList as BitstringStatusListEntity
|
|
428
|
+
const bitstringStatusListEntry = statusListEntry as IBitstringStatusListEntryEntity
|
|
429
|
+
return {
|
|
430
|
+
id: `${statusList.id}#${statusListIndex}`,
|
|
431
|
+
type: 'BitstringStatusListEntry',
|
|
432
|
+
statusPurpose: bitstringStatusListEntry.statusPurpose,
|
|
433
|
+
statusListIndex: '' + statusListIndex,
|
|
434
|
+
statusListCredential: statusList.id,
|
|
435
|
+
bitsPerStatus: bitstringStatusList.bitsPerStatus,
|
|
436
|
+
statusMessage: bitstringStatusListEntry.statusMessage,
|
|
437
|
+
statusReference: bitstringStatusListEntry.statusReference,
|
|
438
|
+
} satisfies BitstringStatusListEntryCredentialStatus
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Creates a signed verifiable credential from an unsigned status list credential
|
|
443
|
+
*
|
|
444
|
+
* @param args - Parameters including the unsigned credential and signing details
|
|
445
|
+
* @param context - Veramo agent context for credential operations
|
|
446
|
+
* @returns Promise resolving to the signed credential
|
|
447
|
+
*/
|
|
448
|
+
private async createVerifiableCredential(
|
|
449
|
+
args: {
|
|
450
|
+
unsignedCredential: BitstringStatusListCredentialUnsigned
|
|
451
|
+
id: string
|
|
452
|
+
issuer: string | IIssuer
|
|
453
|
+
proofFormat: CredentialProofFormat
|
|
454
|
+
keyRef?: string
|
|
455
|
+
},
|
|
456
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
457
|
+
): Promise<BitstringStatusListCredential> {
|
|
458
|
+
const { unsignedCredential, issuer, proofFormat, keyRef } = args
|
|
459
|
+
|
|
460
|
+
const identifier = await context.agent.identifierManagedGet({
|
|
461
|
+
identifier: typeof issuer === 'string' ? issuer : issuer.id,
|
|
462
|
+
vmRelationship: 'assertionMethod',
|
|
463
|
+
offlineWhenNoDIDRegistered: true,
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
const verifiableCredential = await context.agent.createVerifiableCredential({
|
|
467
|
+
credential: unsignedCredential,
|
|
468
|
+
keyRef: keyRef ?? identifier.kmsKeyRef,
|
|
469
|
+
proofFormat,
|
|
470
|
+
fetchRemoteContexts: true,
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as BitstringStatusListCredential
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Builds the appropriate content type string for a given proof format
|
|
478
|
+
*
|
|
479
|
+
* @param proofFormat - The proof format to build content type for
|
|
480
|
+
* @returns The corresponding content type string
|
|
481
|
+
*/
|
|
482
|
+
private buildContentType(proofFormat: CredentialProofFormat | undefined): string {
|
|
483
|
+
switch (proofFormat) {
|
|
484
|
+
case 'jwt':
|
|
485
|
+
return 'application/statuslist+jwt'
|
|
486
|
+
case 'cbor':
|
|
487
|
+
return 'application/statuslist+cwt'
|
|
488
|
+
case 'vc+jwt':
|
|
489
|
+
return 'application/statuslist+vc+jwt'
|
|
490
|
+
case 'lds':
|
|
491
|
+
return 'application/statuslist+ld+json'
|
|
492
|
+
default:
|
|
493
|
+
throw Error(`Unsupported content type '${proofFormat}' for status lists`)
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
package/src/impl/IStatusList.ts
CHANGED
|
@@ -1,33 +1,64 @@
|
|
|
1
|
-
import type { IAgentContext
|
|
1
|
+
import type { IAgentContext } from '@veramo/core'
|
|
2
2
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
4
|
CheckStatusIndexArgs,
|
|
5
5
|
CreateStatusListArgs,
|
|
6
|
+
IMergeDetailsWithEntityArgs,
|
|
7
|
+
IToDetailsFromCredentialArgs,
|
|
6
8
|
Status2021,
|
|
9
|
+
StatusList2021EntryCredentialStatus,
|
|
10
|
+
StatusListOAuthEntryCredentialStatus,
|
|
7
11
|
StatusListResult,
|
|
8
12
|
StatusOAuth,
|
|
9
|
-
ToStatusListDetailsArgs,
|
|
10
13
|
UpdateStatusListFromEncodedListArgs,
|
|
11
14
|
UpdateStatusListIndexArgs,
|
|
12
15
|
} from '../types'
|
|
16
|
+
import { BitstringStatusPurpose } from '@4sure-tech/vc-bitstring-status-lists'
|
|
17
|
+
import {
|
|
18
|
+
CredentialProofFormat,
|
|
19
|
+
IIssuer,
|
|
20
|
+
StatusListCredential,
|
|
21
|
+
StatusListDriverType,
|
|
22
|
+
StatusListIndexingDirection,
|
|
23
|
+
StatusListType,
|
|
24
|
+
StatusPurpose2021,
|
|
25
|
+
} from '@sphereon/ssi-types'
|
|
26
|
+
import {
|
|
27
|
+
BitstringStatusListEntryCredentialStatus,
|
|
28
|
+
IBitstringStatusListEntryEntity,
|
|
29
|
+
IStatusListEntryEntity,
|
|
30
|
+
StatusListEntity,
|
|
31
|
+
} from '@sphereon/ssi-sdk.data-store'
|
|
32
|
+
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
33
|
+
import { DecodedStatusListPayload } from './encoding/common'
|
|
34
|
+
|
|
35
|
+
export interface IExtractedCredentialDetails {
|
|
36
|
+
id: string
|
|
37
|
+
issuer: string | IIssuer
|
|
38
|
+
encodedList: string
|
|
39
|
+
decodedPayload?: DecodedStatusListPayload
|
|
40
|
+
}
|
|
13
41
|
|
|
14
42
|
export interface IStatusList {
|
|
15
43
|
/**
|
|
16
44
|
* Creates a new status list of the specific type
|
|
17
45
|
*/
|
|
18
|
-
createNewStatusList(args: CreateStatusListArgs, context: IAgentContext<
|
|
46
|
+
createNewStatusList(args: CreateStatusListArgs, context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
|
|
19
47
|
|
|
20
48
|
/**
|
|
21
49
|
* Updates a status at the given index in the status list
|
|
22
50
|
*/
|
|
23
|
-
updateStatusListIndex(
|
|
51
|
+
updateStatusListIndex(
|
|
52
|
+
args: UpdateStatusListIndexArgs,
|
|
53
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
54
|
+
): Promise<StatusListResult>
|
|
24
55
|
|
|
25
56
|
/**
|
|
26
57
|
* Updates a status list using a base64 encoded list of statuses
|
|
27
58
|
*/
|
|
28
59
|
updateStatusListFromEncodedList(
|
|
29
60
|
args: UpdateStatusListFromEncodedListArgs,
|
|
30
|
-
context: IAgentContext<
|
|
61
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
31
62
|
): Promise<StatusListResult>
|
|
32
63
|
|
|
33
64
|
/**
|
|
@@ -36,7 +67,70 @@ export interface IStatusList {
|
|
|
36
67
|
checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth>
|
|
37
68
|
|
|
38
69
|
/**
|
|
39
|
-
*
|
|
70
|
+
* Performs the initial parsing of a StatusListCredential.
|
|
71
|
+
* This method handles expensive operations like JWT/CWT decoding once.
|
|
72
|
+
* It extracts all details available from the credential payload itself.
|
|
73
|
+
*/
|
|
74
|
+
extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails>
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Converts a credential and its known metadata into a full StatusListResult.
|
|
78
|
+
*/
|
|
79
|
+
toStatusListDetails(
|
|
80
|
+
args: IToDetailsFromCredentialArgs,
|
|
81
|
+
): Promise<
|
|
82
|
+
StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
|
|
83
|
+
>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Merges pre-parsed details from a new credential with an existing database entity.
|
|
87
|
+
*/
|
|
88
|
+
toStatusListDetails(
|
|
89
|
+
args: IMergeDetailsWithEntityArgs,
|
|
90
|
+
): Promise<
|
|
91
|
+
StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
|
|
92
|
+
>
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Creates a credential status object from a status list and entry
|
|
40
96
|
*/
|
|
41
|
-
|
|
97
|
+
createCredentialStatus(args: {
|
|
98
|
+
statusList: StatusListEntity
|
|
99
|
+
statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
|
|
100
|
+
statusListIndex: number
|
|
101
|
+
}): Promise<StatusList2021EntryCredentialStatus | StatusListOAuthEntryCredentialStatus | BitstringStatusListEntryCredentialStatus>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface IStatusListImplementationResult {
|
|
105
|
+
id: string
|
|
106
|
+
encodedList: string
|
|
107
|
+
issuer: string | IIssuer
|
|
108
|
+
type: StatusListType
|
|
109
|
+
proofFormat: CredentialProofFormat
|
|
110
|
+
length: number
|
|
111
|
+
statusListCredential: StatusListCredential
|
|
112
|
+
statuslistContentType: string
|
|
113
|
+
correlationId?: string
|
|
114
|
+
driverType?: StatusListDriverType
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface IStatusList2021ImplementationResult extends IStatusListImplementationResult {
|
|
118
|
+
type: StatusListType.StatusList2021
|
|
119
|
+
indexingDirection: StatusListIndexingDirection
|
|
120
|
+
statusPurpose: StatusPurpose2021
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface IOAuthStatusListImplementationResult extends IStatusListImplementationResult {
|
|
124
|
+
type: StatusListType.OAuthStatusList
|
|
125
|
+
bitsPerStatus: number
|
|
126
|
+
expiresAt?: Date
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface IBitstringStatusListImplementationResult extends IStatusListImplementationResult {
|
|
130
|
+
type: StatusListType.BitstringStatusList
|
|
131
|
+
statusPurpose: BitstringStatusPurpose | BitstringStatusPurpose[]
|
|
132
|
+
bitsPerStatus?: number
|
|
133
|
+
validFrom?: Date
|
|
134
|
+
validUntil?: Date
|
|
135
|
+
ttl?: number
|
|
42
136
|
}
|