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