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

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/src/functions.ts CHANGED
@@ -2,13 +2,14 @@ import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-res
2
2
  import {
3
3
  CredentialMapper,
4
4
  type CredentialProofFormat,
5
- DocumentFormat,
6
5
  type StatusListCredential,
7
6
  StatusListDriverType,
8
7
  StatusListType,
9
8
  type StatusPurpose2021,
10
9
  } from '@sphereon/ssi-types'
11
- import type { CredentialStatus, DIDDocument, IAgentContext, ICredentialPlugin, ProofFormat as VeramoProofFormat } from '@veramo/core'
10
+ import type { CredentialStatus, DIDDocument, IAgentContext, ProofFormat as VeramoProofFormat } from '@veramo/core'
11
+
12
+ import { IAddStatusListArgs, IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
12
13
 
13
14
  import { checkStatus } from '@sphereon/vc-status-list'
14
15
 
@@ -16,9 +17,12 @@ import { checkStatus } from '@sphereon/vc-status-list'
16
17
  import { CredentialJwtOrJSON, StatusMethod } from 'credential-status'
17
18
  import {
18
19
  BitstringStatus,
20
+ BitstringStatusListEntryCredentialStatus,
19
21
  CreateNewStatusListFuncArgs,
20
22
  Status2021,
23
+ StatusList2021EntryCredentialStatus,
21
24
  StatusList2021ToVerifiableCredentialArgs,
25
+ StatusListOAuthEntryCredentialStatus,
22
26
  StatusListResult,
23
27
  StatusOAuth,
24
28
  UpdateStatusListFromEncodedListArgs,
@@ -26,6 +30,7 @@ import {
26
30
  } from './types'
27
31
  import { assertValidProofType, determineStatusListType, getAssertedValue, getAssertedValues } from './utils'
28
32
  import { getStatusListImplementation } from './impl/StatusListFactory'
33
+ import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
29
34
 
30
35
  export async function fetchStatusListCredential(args: { statusListCredential: string }): Promise<StatusListCredential> {
31
36
  const url = getAssertedValue('statusListCredential', args.statusListCredential)
@@ -146,7 +151,7 @@ export async function simpleCheckStatusFromStatusListUrl(args: {
146
151
 
147
152
  export async function checkStatusIndexFromStatusListCredential(args: {
148
153
  statusListCredential: StatusListCredential
149
- statusPurpose?: StatusPurpose2021
154
+ statusPurpose?: StatusPurpose2021 | string | string[]
150
155
  type?: StatusListType | 'StatusList2021Entry' | 'BitstringStatusListEntry'
151
156
  id?: string
152
157
  statusListIndex: string | number
@@ -159,7 +164,7 @@ export async function checkStatusIndexFromStatusListCredential(args: {
159
164
 
160
165
  export async function createNewStatusList(
161
166
  args: CreateNewStatusListFuncArgs,
162
- context: IAgentContext<(ICredentialPlugin | any) /*IvcdMCredentialPlugin is not available*/ & IIdentifierResolution>,
167
+ context: IAgentContext<(IVcdmCredentialPlugin | any) /*IvcdMCredentialPlugin is not available*/ & IIdentifierResolution>,
163
168
  ): Promise<StatusListResult> {
164
169
  const { type } = getAssertedValues(args)
165
170
  const implementation = getStatusListImplementation(type)
@@ -168,7 +173,7 @@ export async function createNewStatusList(
168
173
 
169
174
  export async function updateStatusIndexFromStatusListCredential(
170
175
  args: UpdateStatusListIndexArgs,
171
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
176
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
172
177
  ): Promise<StatusListResult> {
173
178
  const credential = getAssertedValue('statusListCredential', args.statusListCredential)
174
179
  const statusListType: StatusListType = determineStatusListType(credential)
@@ -176,54 +181,55 @@ export async function updateStatusIndexFromStatusListCredential(
176
181
  return implementation.updateStatusListIndex(args, context)
177
182
  }
178
183
 
179
- // Keeping helper function for backward compatibility
180
184
  export async function statusListCredentialToDetails({
185
+ statusListType,
181
186
  correlationId,
182
187
  driverType,
183
188
  statusListCredential,
184
189
  bitsPerStatus,
185
190
  }: {
191
+ statusListType: StatusListType
186
192
  statusListCredential: StatusListCredential
187
193
  correlationId?: string
188
194
  driverType?: StatusListDriverType
189
195
  bitsPerStatus?: number
190
- }): Promise<StatusListResult> {
196
+ }): Promise<StatusListResult & Partial<IAddStatusListArgs>> {
191
197
  const credential = getAssertedValue('statusListCredential', statusListCredential)
192
-
193
- let statusListType: StatusListType | undefined
194
- const documentFormat = CredentialMapper.detectDocumentType(credential)
195
- if (documentFormat === DocumentFormat.JWT) {
196
- const [header] = credential.split('.')
197
- const decodedHeader = JSON.parse(Buffer.from(header, 'base64').toString())
198
-
199
- if (decodedHeader.typ === 'statuslist+jwt') {
200
- statusListType = StatusListType.OAuthStatusList
201
- }
202
- } else if (documentFormat === DocumentFormat.MSO_MDOC) {
203
- statusListType = StatusListType.OAuthStatusList
204
- // TODO check CBOR content?
205
- }
206
- if (!statusListType) {
207
- const uniform = CredentialMapper.toUniformCredential(credential)
208
- const type = uniform.type.find((t) => t.includes('StatusList2021') || t.includes('OAuth2StatusList') || t.includes('BitstringStatusList'))
209
- if (!type) {
210
- throw new Error('Invalid status list credential type')
211
- }
212
- statusListType = type.replace('Credential', '') as StatusListType
213
- }
214
-
215
198
  const implementation = getStatusListImplementation(statusListType)
216
- return await implementation.toStatusListDetails({
199
+
200
+ // The implementation should now return all the type-specific fields needed for the entity
201
+ const result = await implementation.toStatusListDetails({
217
202
  statusListPayload: credential,
218
203
  correlationId: correlationId,
219
204
  driverType: driverType,
220
205
  bitsPerStatus: bitsPerStatus,
221
206
  })
207
+
208
+ return result
209
+ }
210
+
211
+ export async function createCredentialStatusFromStatusList(args: {
212
+ statusList: StatusListEntity
213
+ statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
214
+ statusListIndex: number
215
+ }): Promise<StatusList2021EntryCredentialStatus | StatusListOAuthEntryCredentialStatus | BitstringStatusListEntryCredentialStatus> {
216
+ const { statusList, statusListEntry, statusListIndex } = args
217
+
218
+ // Determine the status list type and delegate to appropriate implementation
219
+ const statusListType = determineStatusListType(statusList.statusListCredential!)
220
+ const implementation = getStatusListImplementation(statusListType)
221
+
222
+ // Each implementation should have a method to create credential status
223
+ return implementation.createCredentialStatus({
224
+ statusList,
225
+ statusListEntry,
226
+ statusListIndex,
227
+ })
222
228
  }
223
229
 
224
230
  export async function updateStatusListIndexFromEncodedList(
225
231
  args: UpdateStatusListFromEncodedListArgs,
226
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
232
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
227
233
  ): Promise<StatusListResult> {
228
234
  const { type } = getAssertedValue('type', args)
229
235
  const implementation = getStatusListImplementation(type!)
@@ -232,7 +238,7 @@ export async function updateStatusListIndexFromEncodedList(
232
238
 
233
239
  export async function statusList2021ToVerifiableCredential(
234
240
  args: StatusList2021ToVerifiableCredentialArgs,
235
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
241
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
236
242
  ): Promise<StatusListCredential> {
237
243
  const { issuer, id, type } = getAssertedValues(args)
238
244
  const identifier = await context.agent.identifierManagedGet({
@@ -1,5 +1,24 @@
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
23
  CredentialMapper,
5
24
  type CredentialProofFormat,
@@ -9,9 +28,10 @@ import {
9
28
  StatusListType,
10
29
  } from '@sphereon/ssi-types'
11
30
 
12
- import type { IStatusList } from './IStatusList'
31
+ import type { IBitstringStatusListImplementationResult, IStatusList } from './IStatusList'
13
32
  import {
14
33
  BitstringStatus,
34
+ BitstringStatusListEntryCredentialStatus,
15
35
  CheckStatusIndexArgs,
16
36
  CreateStatusListArgs,
17
37
  StatusListResult,
@@ -22,16 +42,37 @@ import {
22
42
 
23
43
  import { assertValidProofType, ensureDate, getAssertedProperty, getAssertedValue, getAssertedValues } from '../utils'
24
44
  import { BitstringStatusListCredential } from '../types/BitstringStatusList'
25
- import { BitstreamStatusList, BitstringStatusPurpose, createStatusListCredential } from '@4sure-tech/vc-bitstring-status-lists'
45
+ import {
46
+ BitstreamStatusList,
47
+ BitstringStatusListCredentialUnsigned,
48
+ BitstringStatusPurpose,
49
+ createStatusListCredential,
50
+ } from '@4sure-tech/vc-bitstring-status-lists'
51
+ import { BitstringStatusListEntity, IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
52
+ import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
26
53
 
27
54
  export const DEFAULT_LIST_LENGTH = 131072 // W3C spec minimum
28
55
  export const DEFAULT_PROOF_FORMAT = 'lds' as CredentialProofFormat
29
56
  export const DEFAULT_STATUS_PURPOSE: BitstringStatusPurpose = 'revocation'
30
57
 
58
+ /**
59
+ * Implementation of the IStatusList interface for W3C Bitstring Status Lists
60
+ *
61
+ * This class handles the creation, updating, and verification of bitstring status lists
62
+ * according to the W3C Bitstring Status List specification. It supports multiple
63
+ * status purposes (revocation, suspension, etc.) and various proof formats.
64
+ */
31
65
  export class BitstringStatusListImplementation implements IStatusList {
66
+ /**
67
+ * Creates a new bitstring status list with the specified configuration
68
+ *
69
+ * @param args - Configuration for the new status list including issuer, purpose, and size
70
+ * @param context - Veramo agent context for credential operations
71
+ * @returns Promise resolving to the created status list details
72
+ */
32
73
  async createNewStatusList(
33
74
  args: CreateStatusListArgs,
34
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
75
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
35
76
  ): Promise<StatusListResult> {
36
77
  if (!args.bitstringStatusList) {
37
78
  throw new Error('BitstringStatusList options are required for type BitstringStatusList')
@@ -40,30 +81,37 @@ export class BitstringStatusListImplementation implements IStatusList {
40
81
  const length = args?.length ?? DEFAULT_LIST_LENGTH
41
82
  const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
42
83
  assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
43
- const veramoProofFormat: VeramoProofFormat = proofFormat as VeramoProofFormat
44
84
 
45
85
  const { issuer, id } = args
46
86
  const correlationId = getAssertedValue('correlationId', args.correlationId)
47
87
  const { statusPurpose, bitsPerStatus, validFrom, validUntil, ttl } = args.bitstringStatusList
88
+
89
+ const unsignedCredential: BitstringStatusListCredentialUnsigned = await createStatusListCredential({
90
+ id,
91
+ issuer,
92
+ statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
93
+ validFrom: ensureDate(validFrom),
94
+ validUntil: ensureDate(validUntil),
95
+ ttl,
96
+ })
48
97
  const statusListCredential = await this.createVerifiableCredential(
49
98
  {
50
- ...args,
51
- proofFormat: veramoProofFormat,
52
- statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
53
- validFrom: ensureDate(validFrom),
54
- validUntil: ensureDate(validUntil),
55
- ttl,
99
+ unsignedCredential,
100
+ id,
101
+ issuer,
102
+ proofFormat,
103
+ keyRef: args.keyRef,
56
104
  },
57
105
  context,
58
106
  )
59
107
 
60
108
  return {
61
- encodedList: statusListCredential.credentialSubject.encodedList,
62
- statusListCredential: statusListCredential,
109
+ encodedList: unsignedCredential.credentialSubject.encodedList,
110
+ statusListCredential,
63
111
  bitstringStatusList: {
64
112
  statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
65
- ...(statusListCredential.validFrom && { validFrom: new Date(statusListCredential.validFrom) }),
66
- ...(statusListCredential.validUntil && { validUntil: new Date(statusListCredential.validUntil) }),
113
+ ...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
114
+ ...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
67
115
  ttl,
68
116
  bitsPerStatus,
69
117
  },
@@ -77,12 +125,19 @@ export class BitstringStatusListImplementation implements IStatusList {
77
125
  }
78
126
  }
79
127
 
128
+ /**
129
+ * Updates the status of a specific credential in an existing status list
130
+ *
131
+ * @param args - Update parameters including the status list credential, index, and new value
132
+ * @param context - Veramo agent context for credential operations
133
+ * @returns Promise resolving to the updated status list details
134
+ */
80
135
  async updateStatusListIndex(
81
136
  args: UpdateStatusListIndexArgs,
82
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
137
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
83
138
  ): Promise<StatusListResult> {
84
139
  if (!args.bitsPerStatus || args.bitsPerStatus < 1) {
85
- return Promise.reject('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListIndex)')
140
+ return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListIndex)'))
86
141
  }
87
142
 
88
143
  const credential = args.statusListCredential
@@ -105,74 +160,92 @@ export class BitstringStatusListImplementation implements IStatusList {
105
160
  const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
106
161
  const ttl = credSubject.ttl
107
162
 
163
+ const unsignedCredential: BitstringStatusListCredentialUnsigned = await createStatusListCredential({
164
+ id,
165
+ issuer,
166
+ statusList,
167
+ statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
168
+ validFrom: ensureDate(validFrom),
169
+ validUntil: ensureDate(validUntil),
170
+ ttl,
171
+ })
172
+
108
173
  const updatedCredential = await this.createVerifiableCredential(
109
174
  {
110
- ...args,
175
+ unsignedCredential,
111
176
  id,
112
177
  issuer,
113
- statusList,
114
- proofFormat: proofFormat,
115
- statusPurpose,
116
- ttl,
117
- validFrom,
118
- validUntil,
178
+ proofFormat,
179
+ keyRef: args.keyRef,
119
180
  },
120
181
  context,
121
182
  )
122
183
 
123
184
  return {
124
185
  statusListCredential: updatedCredential,
125
- encodedList: updatedCredential.credentialSubject.encodedList,
186
+ encodedList: unsignedCredential.credentialSubject.encodedList,
126
187
  bitstringStatusList: {
127
188
  statusPurpose,
128
- ...(updatedCredential.validFrom && { validFrom: new Date(updatedCredential.validFrom) }),
129
- ...(updatedCredential.validUntil && { validUntil: new Date(updatedCredential.validUntil) }),
189
+ ...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
190
+ ...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
130
191
  bitsPerStatus: args.bitsPerStatus,
131
192
  ttl,
132
193
  },
133
194
  length: statusList.getLength(),
134
195
  type: StatusListType.BitstringStatusList,
135
- proofFormat: proofFormat,
196
+ proofFormat,
136
197
  id,
137
198
  issuer,
138
199
  statuslistContentType: this.buildContentType(proofFormat),
139
200
  }
140
201
  }
141
202
 
203
+ /**
204
+ * Updates a status list by decoding an encoded list, modifying it, and re-encoding
205
+ *
206
+ * @param args - Update parameters including encoded list, index, and new value
207
+ * @param context - Veramo agent context for credential operations
208
+ * @returns Promise resolving to the updated status list details
209
+ */
142
210
  async updateStatusListFromEncodedList(
143
211
  args: UpdateStatusListFromEncodedListArgs,
144
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
212
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
145
213
  ): Promise<StatusListResult> {
146
214
  if (!args.bitstringStatusList) {
147
215
  throw new Error('bitstringStatusList options required for type BitstringStatusList')
148
216
  }
149
217
 
150
218
  if (args.bitstringStatusList.bitsPerStatus < 1) {
151
- return Promise.reject('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListFromEncodedList)')
219
+ return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListFromEncodedList)'))
152
220
  }
153
221
 
154
222
  const { statusPurpose, bitsPerStatus, ttl, validFrom, validUntil } = args.bitstringStatusList
155
223
 
156
224
  const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
157
225
  assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
158
- const veramoProofFormat: VeramoProofFormat = proofFormat as VeramoProofFormat
159
226
 
160
227
  const { issuer, id } = getAssertedValues(args)
161
228
  const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: args.encodedList, statusSize: bitsPerStatus })
162
229
  const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
163
230
  statusList.setStatus(index, args.value)
164
231
 
232
+ const unsignedCredential: BitstringStatusListCredentialUnsigned = await createStatusListCredential({
233
+ id,
234
+ issuer,
235
+ statusList,
236
+ statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
237
+ validFrom: ensureDate(validFrom),
238
+ validUntil: ensureDate(validUntil),
239
+ ttl,
240
+ })
241
+
165
242
  const credential = await this.createVerifiableCredential(
166
243
  {
244
+ unsignedCredential,
167
245
  id,
168
246
  issuer,
169
- statusList,
170
- proofFormat: veramoProofFormat,
247
+ proofFormat,
171
248
  keyRef: args.keyRef,
172
- statusPurpose,
173
- validFrom: ensureDate(validFrom),
174
- validUntil: ensureDate(validUntil),
175
- ttl,
176
249
  },
177
250
  context,
178
251
  )
@@ -180,34 +253,38 @@ export class BitstringStatusListImplementation implements IStatusList {
180
253
  return {
181
254
  type: StatusListType.BitstringStatusList,
182
255
  statusListCredential: credential,
183
- encodedList: credential.credentialSubject.encodedList,
256
+ encodedList: unsignedCredential.credentialSubject.encodedList,
184
257
  bitstringStatusList: {
185
258
  statusPurpose,
186
259
  bitsPerStatus,
187
- ...(credential.validFrom && { validFrom: new Date(credential.validFrom) }),
188
- ...(credential.validUntil && { validUntil: new Date(credential.validUntil) }),
260
+ ...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
261
+ ...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
189
262
  ttl,
190
263
  },
191
264
  length: statusList.getLength(),
192
265
  proofFormat: args.proofFormat ?? 'lds',
193
- id: id,
194
- issuer: issuer,
266
+ id,
267
+ issuer,
195
268
  statuslistContentType: this.buildContentType(proofFormat),
196
269
  }
197
270
  }
198
271
 
272
+ /**
273
+ * Checks the status of a specific credential by its index in the status list
274
+ *
275
+ * @param args - Check parameters including the status list credential and index
276
+ * @returns Promise resolving to the status value at the specified index
277
+ */
199
278
  async checkStatusIndex(args: CheckStatusIndexArgs): Promise<BitstringStatus> {
200
279
  if (!args.bitsPerStatus || args.bitsPerStatus < 1) {
201
- return Promise.reject('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (checkStatusIndex)')
280
+ return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (checkStatusIndex)'))
202
281
  }
203
282
 
204
283
  const uniform = CredentialMapper.toUniformCredential(args.statusListCredential)
205
284
  const { credentialSubject } = uniform
206
285
  const encodedList = getAssertedProperty('encodedList', credentialSubject)
207
286
 
208
- const statusSize = args.bitsPerStatus
209
- const statusList = await BitstreamStatusList.decode({ encodedList, statusSize })
210
-
287
+ const statusList = await BitstreamStatusList.decode({ encodedList, statusSize: args.bitsPerStatus })
211
288
  const numIndex = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
212
289
  if (statusList.getLength() <= numIndex) {
213
290
  throw new Error(`Status list index out of bounds, has ${statusList.getLength()} entries, requested ${numIndex}`)
@@ -215,17 +292,23 @@ export class BitstringStatusListImplementation implements IStatusList {
215
292
  return statusList.getStatus(numIndex)
216
293
  }
217
294
 
218
- async toStatusListDetails(args: ToStatusListDetailsArgs): Promise<StatusListResult> {
295
+ /**
296
+ * Converts a status list credential payload to detailed status list information
297
+ *
298
+ * @param args - Conversion parameters including the status list payload
299
+ * @returns Promise resolving to detailed status list information
300
+ */
301
+ async toStatusListDetails(args: ToStatusListDetailsArgs): Promise<StatusListResult & IBitstringStatusListImplementationResult> {
219
302
  const { statusListPayload, bitsPerStatus } = args
220
303
  if (!bitsPerStatus || bitsPerStatus < 1) {
221
- return Promise.reject('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (toStatusListDetails)')
304
+ return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (toStatusListDetails)'))
222
305
  }
223
306
 
224
307
  const uniform = CredentialMapper.toUniformCredential(statusListPayload)
225
308
  const { issuer, credentialSubject } = uniform
226
309
  const id = getAssertedValue('id', uniform.id)
227
310
  const encodedList = getAssertedProperty('encodedList', credentialSubject)
228
- const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListPayload) === DocumentFormat.JWT ? 'jwt' : 'lds'
311
+ const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListPayload) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
229
312
  const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
230
313
  const statusPurpose = getAssertedProperty('statusPurpose', credSubject)
231
314
  const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : undefined
@@ -234,6 +317,7 @@ export class BitstringStatusListImplementation implements IStatusList {
234
317
  const statuslistLength: number = BitstreamStatusList.getStatusListLength(encodedList, bitsPerStatus)
235
318
 
236
319
  return {
320
+ // Base implementation fields
237
321
  id,
238
322
  encodedList,
239
323
  issuer,
@@ -242,56 +326,115 @@ export class BitstringStatusListImplementation implements IStatusList {
242
326
  length: statuslistLength,
243
327
  statusListCredential: statusListPayload,
244
328
  statuslistContentType: this.buildContentType(proofFormat),
329
+ correlationId: args.correlationId, // FIXME these do not need to be inside the impl
330
+ driverType: args.driverType, // FIXME these do not need to be inside the impl
331
+
332
+ // Flattened Bitstring-specific fields
333
+ statusPurpose,
334
+ bitsPerStatus,
335
+ ...(validFrom && { validFrom }),
336
+ ...(validUntil && { validUntil }),
337
+ ...(ttl && { ttl }),
338
+
339
+ // Legacy nested structure for backward compatibility
245
340
  bitstringStatusList: {
246
341
  statusPurpose,
247
342
  bitsPerStatus,
248
- validFrom,
249
- validUntil,
250
- ttl,
343
+ ...(validFrom && { validFrom }),
344
+ ...(validUntil && { validUntil }),
345
+ ...(ttl && { ttl }),
251
346
  },
347
+
348
+ // Optional fields from args
252
349
  ...(args.correlationId && { correlationId: args.correlationId }),
253
350
  ...(args.driverType && { driverType: args.driverType }),
254
351
  }
255
352
  }
256
353
 
354
+ /**
355
+ * Creates a credential status entry for a specific credential in a status list
356
+ *
357
+ * @param args - Parameters including the status list, entry details, and index
358
+ * @returns Promise resolving to the credential status entry
359
+ */
360
+ async createCredentialStatus(args: {
361
+ statusList: StatusListEntity
362
+ statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
363
+ statusListIndex: number
364
+ }): Promise<BitstringStatusListEntryCredentialStatus> {
365
+ const { statusList, statusListEntry, statusListIndex } = args
366
+
367
+ // Type guard to ensure we have a bitstring entry
368
+ const isBitstringEntry = (entry: IStatusListEntryEntity | IBitstringStatusListEntryEntity): entry is IBitstringStatusListEntryEntity => {
369
+ return 'statusPurpose' in entry
370
+ }
371
+
372
+ if (!isBitstringEntry(statusListEntry)) {
373
+ throw new Error('Expected bitstring status list entry for bitstring status list')
374
+ }
375
+
376
+ // Cast to BitstringStatusListEntity to access specific properties
377
+ const bitstringStatusList = statusList as BitstringStatusListEntity
378
+
379
+ return {
380
+ id: `${statusList.id}#${statusListIndex}`,
381
+ type: 'BitstringStatusListEntry',
382
+ statusPurpose: statusListEntry.statusPurpose,
383
+ statusListIndex: '' + statusListIndex,
384
+ statusListCredential: statusList.id,
385
+ bitsPerStatus: bitstringStatusList.bitsPerStatus,
386
+ } satisfies BitstringStatusListEntryCredentialStatus
387
+ }
388
+
389
+ /**
390
+ * Creates a signed verifiable credential from an unsigned status list credential
391
+ *
392
+ * @param args - Parameters including the unsigned credential and signing details
393
+ * @param context - Veramo agent context for credential operations
394
+ * @returns Promise resolving to the signed credential
395
+ */
257
396
  private async createVerifiableCredential(
258
397
  args: {
398
+ unsignedCredential: BitstringStatusListCredentialUnsigned
259
399
  id: string
260
400
  issuer: string | IIssuer
261
- statusList?: BitstreamStatusList
262
- proofFormat: VeramoProofFormat
263
- statusPurpose: BitstringStatusPurpose
264
- validFrom?: Date
265
- validUntil?: Date
266
- ttl?: number
401
+ proofFormat: CredentialProofFormat
267
402
  keyRef?: string
268
403
  },
269
- context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
404
+ context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
270
405
  ): Promise<BitstringStatusListCredential> {
406
+ const { unsignedCredential, issuer, proofFormat, keyRef } = args
407
+
271
408
  const identifier = await context.agent.identifierManagedGet({
272
- identifier: typeof args.issuer === 'string' ? args.issuer : args.issuer.id,
409
+ identifier: typeof issuer === 'string' ? issuer : issuer.id,
273
410
  vmRelationship: 'assertionMethod',
274
411
  offlineWhenNoDIDRegistered: true,
275
412
  })
276
413
 
277
- const unsignedCredential = await createStatusListCredential(args)
278
-
279
414
  const verifiableCredential = await context.agent.createVerifiableCredential({
280
415
  credential: unsignedCredential,
281
- keyRef: args.keyRef ?? identifier.kmsKeyRef,
282
- proofFormat: args.proofFormat,
416
+ keyRef: keyRef ?? identifier.kmsKeyRef,
417
+ proofFormat,
283
418
  fetchRemoteContexts: true,
284
419
  })
285
420
 
286
421
  return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as BitstringStatusListCredential
287
422
  }
288
423
 
289
- private buildContentType(proofFormat: CredentialProofFormat | undefined) {
424
+ /**
425
+ * Builds the appropriate content type string for a given proof format
426
+ *
427
+ * @param proofFormat - The proof format to build content type for
428
+ * @returns The corresponding content type string
429
+ */
430
+ private buildContentType(proofFormat: CredentialProofFormat | undefined): string {
290
431
  switch (proofFormat) {
291
432
  case 'jwt':
292
- return `application/statuslist+jwt`
433
+ return 'application/statuslist+jwt'
293
434
  case 'cbor':
294
- return `application/statuslist+cwt`
435
+ return 'application/statuslist+cwt'
436
+ case 'vc+jwt':
437
+ return 'application/statuslist+vc+jwt'
295
438
  case 'lds':
296
439
  return 'application/statuslist+ld+json'
297
440
  default: