@sphereon/ssi-sdk.vc-status-list 0.34.1-feature.SSISDK.17.bitstring.sl.2 → 0.34.1-feature.SSISDK.17.bitstring.sl.25

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.
@@ -1,7 +1,25 @@
1
- import type { IAgentContext, ICredentialPlugin, ProofFormat as VeramoProofFormat } from '@veramo/core'
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'
2
20
  import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
21
+
3
22
  import {
4
- type BitstringStatusPurpose,
5
23
  CredentialMapper,
6
24
  type CredentialProofFormat,
7
25
  DocumentFormat,
@@ -10,31 +28,56 @@ import {
10
28
  StatusListType,
11
29
  } from '@sphereon/ssi-types'
12
30
 
13
- import type { IStatusList } from './IStatusList'
31
+ import type { IBitstringStatusListImplementationResult, IExtractedCredentialDetails, IStatusList } from './IStatusList'
14
32
  import {
15
- BitstringStatus,
16
- BitstringStatusResult,
17
33
  CheckStatusIndexArgs,
18
34
  CreateStatusListArgs,
35
+ IMergeDetailsWithEntityArgs,
36
+ IToDetailsFromCredentialArgs,
19
37
  StatusListResult,
20
- ToStatusListDetailsArgs,
21
38
  UpdateStatusListFromEncodedListArgs,
22
39
  UpdateStatusListIndexArgs,
23
40
  } from '../types'
24
41
 
25
- import { assertValidProofType, getAssertedProperty, getAssertedValue, getAssertedValues } from '../utils'
26
- import { createList, decodeList } from '@digitalbazaar/vc-bitstring-status-list'
27
- import { IBitstringStatusList } from '../types/BitstringStatusList'
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'
28
58
 
29
59
  export const DEFAULT_LIST_LENGTH = 131072 // W3C spec minimum
30
- export const DEFAULT_PROOF_FORMAT = 'lds' as CredentialProofFormat
31
- export const DEFAULT_STATUS_SIZE = 1
60
+ export const DEFAULT_PROOF_FORMAT = 'vc+jwt' as CredentialProofFormat
32
61
  export const DEFAULT_STATUS_PURPOSE: BitstringStatusPurpose = 'revocation'
33
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
+ */
34
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
+ */
35
78
  async createNewStatusList(
36
79
  args: CreateStatusListArgs,
37
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
80
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
38
81
  ): Promise<StatusListResult> {
39
82
  if (!args.bitstringStatusList) {
40
83
  throw new Error('BitstringStatusList options are required for type BitstringStatusList')
@@ -43,33 +86,39 @@ export class BitstringStatusListImplementation implements IStatusList {
43
86
  const length = args?.length ?? DEFAULT_LIST_LENGTH
44
87
  const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
45
88
  assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
46
- const veramoProofFormat: VeramoProofFormat = proofFormat as VeramoProofFormat
47
89
 
48
90
  const { issuer, id } = args
49
91
  const correlationId = getAssertedValue('correlationId', args.correlationId)
50
- const { statusPurpose, statusSize, statusMessage, ttl } = args.bitstringStatusList
51
- const list = (await createList({ length })) as IBitstringStatusList
52
- const encodedList = await list.encode()
92
+ const { statusPurpose, bitsPerStatus, validFrom, validUntil, ttl } = args.bitstringStatusList
53
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
+ })
54
102
  const statusListCredential = await this.createVerifiableCredential(
55
103
  {
56
- ...args,
57
- encodedList,
58
- proofFormat: veramoProofFormat,
59
- statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
60
- statusSize: statusSize ?? DEFAULT_STATUS_SIZE,
61
- statusMessage: statusMessage,
62
- ttl,
104
+ unsignedCredential,
105
+ id,
106
+ issuer,
107
+ proofFormat,
108
+ keyRef: args.keyRef,
63
109
  },
64
110
  context,
65
111
  )
66
112
 
67
113
  return {
68
- encodedList,
69
- statusListCredential: statusListCredential,
114
+ encodedList: unsignedCredential.credentialSubject.encodedList,
115
+ statusListCredential,
70
116
  bitstringStatusList: {
71
117
  statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
118
+ ...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
119
+ ...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
72
120
  ttl,
121
+ bitsPerStatus,
73
122
  },
74
123
  length,
75
124
  type: StatusListType.BitstringStatusList,
@@ -81,10 +130,21 @@ export class BitstringStatusListImplementation implements IStatusList {
81
130
  }
82
131
  }
83
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
+ */
84
140
  async updateStatusListIndex(
85
141
  args: UpdateStatusListIndexArgs,
86
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
142
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
87
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
+
88
148
  const credential = args.statusListCredential
89
149
  const uniform = CredentialMapper.toUniformCredential(credential)
90
150
  const { issuer, credentialSubject } = uniform
@@ -92,11 +152,11 @@ export class BitstringStatusListImplementation implements IStatusList {
92
152
  const origEncodedList = getAssertedProperty('encodedList', credentialSubject)
93
153
 
94
154
  const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
95
- const statusList = (await decodeList({ encodedList: origEncodedList })) as IBitstringStatusList
96
- statusList.setStatus(index, args.value != 0)
97
- const encodedList = await statusList.encode()
155
+ const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: origEncodedList, statusSize: args.bitsPerStatus })
156
+ const bitstringStatusId = args.value as number
157
+ statusList.setStatus(index, bitstringStatusId)
98
158
 
99
- const proofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'jwt' : 'lds'
159
+ const proofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
100
160
 
101
161
  const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
102
162
 
@@ -106,71 +166,92 @@ export class BitstringStatusListImplementation implements IStatusList {
106
166
  const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
107
167
  const ttl = credSubject.ttl
108
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
+
109
179
  const updatedCredential = await this.createVerifiableCredential(
110
180
  {
111
- ...args,
181
+ unsignedCredential,
112
182
  id,
113
183
  issuer,
114
- encodedList,
115
- proofFormat: proofFormat,
116
- statusPurpose,
117
- ttl,
118
- validFrom,
119
- validUntil,
184
+ proofFormat,
185
+ keyRef: args.keyRef,
120
186
  },
121
187
  context,
122
188
  )
123
189
 
124
190
  return {
125
191
  statusListCredential: updatedCredential,
126
- encodedList,
192
+ encodedList: unsignedCredential.credentialSubject.encodedList,
127
193
  bitstringStatusList: {
128
194
  statusPurpose,
129
- validFrom,
130
- validUntil,
195
+ ...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
196
+ ...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
197
+ bitsPerStatus: args.bitsPerStatus,
131
198
  ttl,
132
199
  },
133
- length: statusList.length - 1,
200
+ length: statusList.getLength(),
134
201
  type: StatusListType.BitstringStatusList,
135
- proofFormat: proofFormat,
202
+ proofFormat,
136
203
  id,
137
204
  issuer,
138
205
  statuslistContentType: this.buildContentType(proofFormat),
139
206
  }
140
207
  }
141
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
+ */
142
216
  async updateStatusListFromEncodedList(
143
217
  args: UpdateStatusListFromEncodedListArgs,
144
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
218
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
145
219
  ): Promise<StatusListResult> {
146
220
  if (!args.bitstringStatusList) {
147
221
  throw new Error('bitstringStatusList options required for type BitstringStatusList')
148
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
+
149
230
  const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
150
231
  assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
151
- const veramoProofFormat: VeramoProofFormat = proofFormat as VeramoProofFormat
152
232
 
153
233
  const { issuer, id } = getAssertedValues(args)
154
- const statusList = (await decodeList({ encodedList: args.encodedList })) as IBitstringStatusList
234
+ const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: args.encodedList, statusSize: bitsPerStatus })
155
235
  const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
156
236
  statusList.setStatus(index, args.value)
157
237
 
158
- const newEncodedList = await statusList.encode()
159
- const { statusPurpose, statusSize, statusMessage, ttl, validFrom, validUntil } = args.bitstringStatusList
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
+ })
160
247
 
161
248
  const credential = await this.createVerifiableCredential(
162
249
  {
250
+ unsignedCredential,
163
251
  id,
164
252
  issuer,
165
- encodedList: newEncodedList,
166
- proofFormat: veramoProofFormat,
253
+ proofFormat,
167
254
  keyRef: args.keyRef,
168
- statusPurpose,
169
- statusSize,
170
- statusMessage,
171
- validFrom,
172
- validUntil,
173
- ttl,
174
255
  },
175
256
  context,
176
257
  )
@@ -178,153 +259,234 @@ export class BitstringStatusListImplementation implements IStatusList {
178
259
  return {
179
260
  type: StatusListType.BitstringStatusList,
180
261
  statusListCredential: credential,
181
- encodedList: newEncodedList,
262
+ encodedList: unsignedCredential.credentialSubject.encodedList,
182
263
  bitstringStatusList: {
183
264
  statusPurpose,
184
- validFrom,
185
- validUntil,
265
+ bitsPerStatus,
266
+ ...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
267
+ ...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
186
268
  ttl,
187
269
  },
188
- length: statusList.length,
270
+ length: statusList.getLength(),
189
271
  proofFormat: args.proofFormat ?? 'lds',
190
- id: id,
191
- issuer: issuer,
272
+ id,
273
+ issuer,
192
274
  statuslistContentType: this.buildContentType(proofFormat),
193
275
  }
194
276
  }
195
277
 
196
- async checkStatusIndex(args: CheckStatusIndexArgs): Promise<BitstringStatusResult> {
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
+
197
289
  const uniform = CredentialMapper.toUniformCredential(args.statusListCredential)
198
290
  const { credentialSubject } = uniform
199
291
  const encodedList = getAssertedProperty('encodedList', credentialSubject)
200
292
 
201
- const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
202
- const messageList = (subject as any).statusMessage as Array<Partial<BitstringStatus>>
203
-
293
+ const statusList = await BitstreamStatusList.decode({ encodedList, statusSize: args.bitsPerStatus })
204
294
  const numIndex = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
205
- const hexIndex = `0x${numIndex.toString(16)}`
206
- const statusMessage = messageList.find((statMsg) => statMsg.status === hexIndex)
207
-
208
- const statusList = (await decodeList({ encodedList })) as IBitstringStatusList
209
- if (statusList.length <= numIndex) {
210
- throw new Error(`Status list index out of bounds, has ${messageList.length} messages, requested ${numIndex}`)
295
+ if (statusList.getLength() <= numIndex) {
296
+ throw new Error(`Status list index out of bounds, has ${statusList.getLength()} entries, requested ${numIndex}`)
211
297
  }
212
- const value = statusList.getStatus(numIndex)
213
- return {
214
- index: numIndex,
215
- status: hexIndex,
216
- message: statusMessage?.message,
217
- set: value,
218
- } satisfies BitstringStatusResult
298
+ return statusList.getStatus(numIndex)
219
299
  }
220
300
 
221
- async toStatusListDetails(args: ToStatusListDetailsArgs): Promise<StatusListResult> {
222
- const { statusListPayload } = args
223
- const uniform = CredentialMapper.toUniformCredential(statusListPayload)
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)
224
308
  const { issuer, credentialSubject } = uniform
225
- const id = getAssertedValue('id', uniform.id)
226
- const encodedList = getAssertedProperty('encodedList', credentialSubject)
227
- const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListPayload) === DocumentFormat.JWT ? 'jwt' : 'lds'
228
- const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
229
- const statusPurpose = getAssertedProperty('statusPurpose', credSubject)
230
- const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : undefined
231
- const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
232
- const ttl = credSubject.ttl
233
- const list = (await decodeList({ encodedList })) as IBitstringStatusList
309
+ const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
234
310
 
235
311
  return {
236
- id,
237
- encodedList,
312
+ id: getAssertedValue('id', uniform.id),
238
313
  issuer,
239
- type: StatusListType.BitstringStatusList,
240
- proofFormat,
241
- length: list.length,
242
- statusListCredential: statusListPayload,
243
- statuslistContentType: this.buildContentType(proofFormat),
244
- bitstringStatusList: {
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,
245
362
  statusPurpose,
246
- validFrom,
247
- validUntil,
248
- ttl,
249
- },
250
- ...(args.correlationId && { correlationId: args.correlationId }),
251
- ...(args.driverType && { driverType: args.driverType }),
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
+ }
252
411
  }
253
412
  }
254
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
+ */
255
448
  private async createVerifiableCredential(
256
449
  args: {
450
+ unsignedCredential: BitstringStatusListCredentialUnsigned
257
451
  id: string
258
452
  issuer: string | IIssuer
259
- encodedList: string
260
- proofFormat: VeramoProofFormat
261
- statusPurpose: BitstringStatusPurpose
262
- statusSize?: number
263
- statusMessage?: Array<BitstringStatus>
264
- validFrom?: Date
265
- validUntil?: Date
266
- ttl?: number
453
+ proofFormat: CredentialProofFormat
267
454
  keyRef?: string
268
455
  },
269
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
270
- ): Promise<StatusListCredential> {
456
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
457
+ ): Promise<BitstringStatusListCredential> {
458
+ const { unsignedCredential, issuer, proofFormat, keyRef } = args
459
+
271
460
  const identifier = await context.agent.identifierManagedGet({
272
- identifier: typeof args.issuer === 'string' ? args.issuer : args.issuer.id,
461
+ identifier: typeof issuer === 'string' ? issuer : issuer.id,
273
462
  vmRelationship: 'assertionMethod',
274
463
  offlineWhenNoDIDRegistered: true,
275
464
  })
276
465
 
277
- const credentialSubject: any = {
278
- id: args.id,
279
- type: 'BitstringStatusList',
280
- statusPurpose: args.statusPurpose,
281
- encodedList: args.encodedList,
282
- }
283
-
284
- if (args.statusSize && args.statusSize > 1) {
285
- credentialSubject.statusSize = args.statusSize
286
- }
287
-
288
- if (args.statusMessage) {
289
- credentialSubject.statusMessage = args.statusMessage
290
- }
291
-
292
- if (args.validFrom) {
293
- credentialSubject.validFrom = args.validFrom
294
- }
295
-
296
- if (args.validUntil) {
297
- credentialSubject.validUntil = args.validUntil
298
- }
299
-
300
- if (args.ttl) {
301
- credentialSubject.ttl = args.ttl
302
- }
303
-
304
- const credential = {
305
- '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/ns/credentials/status/v1'],
306
- id: args.id,
307
- issuer: args.issuer,
308
- type: ['VerifiableCredential', 'BitstringStatusListCredential'],
309
- credentialSubject,
310
- }
311
-
312
466
  const verifiableCredential = await context.agent.createVerifiableCredential({
313
- credential,
314
- keyRef: args.keyRef ?? identifier.kmsKeyRef,
315
- proofFormat: args.proofFormat,
467
+ credential: unsignedCredential,
468
+ keyRef: keyRef ?? identifier.kmsKeyRef,
469
+ proofFormat,
316
470
  fetchRemoteContexts: true,
317
471
  })
318
472
 
319
- return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as StatusListCredential
473
+ return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as BitstringStatusListCredential
320
474
  }
321
475
 
322
- private buildContentType(proofFormat: CredentialProofFormat | undefined) {
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 {
323
483
  switch (proofFormat) {
324
484
  case 'jwt':
325
- return `application/statuslist+jwt`
485
+ return 'application/statuslist+jwt'
326
486
  case 'cbor':
327
- return `application/statuslist+cwt`
487
+ return 'application/statuslist+cwt'
488
+ case 'vc+jwt':
489
+ return 'application/statuslist+vc+jwt'
328
490
  case 'lds':
329
491
  return 'application/statuslist+ld+json'
330
492
  default: