@sphereon/ssi-sdk.vc-status-list 0.34.1-feature.SSISDK.17.bitstring.sl.11 → 0.34.1-feature.SSISDK.17.bitstring.sl.14
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 +395 -216
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -41
- package/dist/index.d.ts +75 -41
- package/dist/index.js +396 -217
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/src/functions.ts +50 -62
- package/src/impl/BitstringStatusListImplementation.ts +265 -121
- package/src/impl/IStatusList.ts +43 -11
- package/src/impl/OAuthStatusList.ts +81 -33
- package/src/impl/StatusList2021.ts +79 -39
- package/src/index.ts +1 -0
- package/src/types/index.ts +22 -36
- package/src/utils.ts +48 -19
|
@@ -1,4 +1,22 @@
|
|
|
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'
|
|
3
21
|
|
|
4
22
|
import {
|
|
@@ -10,31 +28,56 @@ import {
|
|
|
10
28
|
StatusListType,
|
|
11
29
|
} from '@sphereon/ssi-types'
|
|
12
30
|
|
|
13
|
-
import type { IBitstringStatusListImplementationResult, IStatusList } from './IStatusList'
|
|
31
|
+
import type { IBitstringStatusListImplementationResult, IExtractedCredentialDetails, IStatusList } from './IStatusList'
|
|
14
32
|
import {
|
|
15
|
-
BitstringStatus,
|
|
16
|
-
BitstringStatusListEntryCredentialStatus,
|
|
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
42
|
import { assertValidProofType, ensureDate, getAssertedProperty, getAssertedValue, getAssertedValues } from '../utils'
|
|
26
43
|
import { BitstringStatusListCredential } from '../types/BitstringStatusList'
|
|
27
|
-
import {
|
|
28
|
-
|
|
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'
|
|
29
58
|
|
|
30
59
|
export const DEFAULT_LIST_LENGTH = 131072 // W3C spec minimum
|
|
31
|
-
export const DEFAULT_PROOF_FORMAT = '
|
|
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<
|
|
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,30 +86,37 @@ 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
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
|
+
})
|
|
51
102
|
const statusListCredential = await this.createVerifiableCredential(
|
|
52
103
|
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
ttl,
|
|
104
|
+
unsignedCredential,
|
|
105
|
+
id,
|
|
106
|
+
issuer,
|
|
107
|
+
proofFormat,
|
|
108
|
+
keyRef: args.keyRef,
|
|
59
109
|
},
|
|
60
110
|
context,
|
|
61
111
|
)
|
|
62
112
|
|
|
63
113
|
return {
|
|
64
|
-
encodedList:
|
|
65
|
-
statusListCredential
|
|
114
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
115
|
+
statusListCredential,
|
|
66
116
|
bitstringStatusList: {
|
|
67
117
|
statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
|
|
68
|
-
...(
|
|
69
|
-
...(
|
|
118
|
+
...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
|
|
119
|
+
...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
|
|
70
120
|
ttl,
|
|
71
121
|
bitsPerStatus,
|
|
72
122
|
},
|
|
@@ -80,12 +130,19 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
80
130
|
}
|
|
81
131
|
}
|
|
82
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
|
+
*/
|
|
83
140
|
async updateStatusListIndex(
|
|
84
141
|
args: UpdateStatusListIndexArgs,
|
|
85
|
-
context: IAgentContext<
|
|
142
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
86
143
|
): Promise<StatusListResult> {
|
|
87
144
|
if (!args.bitsPerStatus || args.bitsPerStatus < 1) {
|
|
88
|
-
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)'))
|
|
89
146
|
}
|
|
90
147
|
|
|
91
148
|
const credential = args.statusListCredential
|
|
@@ -96,9 +153,10 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
96
153
|
|
|
97
154
|
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
98
155
|
const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: origEncodedList, statusSize: args.bitsPerStatus })
|
|
99
|
-
|
|
156
|
+
const bitstringStatusId = args.value as number
|
|
157
|
+
statusList.setStatus(index, bitstringStatusId)
|
|
100
158
|
|
|
101
|
-
const proofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
159
|
+
const proofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
102
160
|
|
|
103
161
|
const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
104
162
|
|
|
@@ -108,74 +166,92 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
108
166
|
const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
|
|
109
167
|
const ttl = credSubject.ttl
|
|
110
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
|
+
|
|
111
179
|
const updatedCredential = await this.createVerifiableCredential(
|
|
112
180
|
{
|
|
113
|
-
|
|
181
|
+
unsignedCredential,
|
|
114
182
|
id,
|
|
115
183
|
issuer,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
statusPurpose,
|
|
119
|
-
ttl,
|
|
120
|
-
validFrom,
|
|
121
|
-
validUntil,
|
|
184
|
+
proofFormat,
|
|
185
|
+
keyRef: args.keyRef,
|
|
122
186
|
},
|
|
123
187
|
context,
|
|
124
188
|
)
|
|
125
189
|
|
|
126
190
|
return {
|
|
127
191
|
statusListCredential: updatedCredential,
|
|
128
|
-
encodedList:
|
|
192
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
129
193
|
bitstringStatusList: {
|
|
130
194
|
statusPurpose,
|
|
131
|
-
...(
|
|
132
|
-
...(
|
|
195
|
+
...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
|
|
196
|
+
...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
|
|
133
197
|
bitsPerStatus: args.bitsPerStatus,
|
|
134
198
|
ttl,
|
|
135
199
|
},
|
|
136
200
|
length: statusList.getLength(),
|
|
137
201
|
type: StatusListType.BitstringStatusList,
|
|
138
|
-
proofFormat
|
|
202
|
+
proofFormat,
|
|
139
203
|
id,
|
|
140
204
|
issuer,
|
|
141
205
|
statuslistContentType: this.buildContentType(proofFormat),
|
|
142
206
|
}
|
|
143
207
|
}
|
|
144
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
|
+
*/
|
|
145
216
|
async updateStatusListFromEncodedList(
|
|
146
217
|
args: UpdateStatusListFromEncodedListArgs,
|
|
147
|
-
context: IAgentContext<
|
|
218
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
148
219
|
): Promise<StatusListResult> {
|
|
149
220
|
if (!args.bitstringStatusList) {
|
|
150
221
|
throw new Error('bitstringStatusList options required for type BitstringStatusList')
|
|
151
222
|
}
|
|
152
223
|
|
|
153
224
|
if (args.bitstringStatusList.bitsPerStatus < 1) {
|
|
154
|
-
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)'))
|
|
155
226
|
}
|
|
156
227
|
|
|
157
228
|
const { statusPurpose, bitsPerStatus, ttl, validFrom, validUntil } = args.bitstringStatusList
|
|
158
229
|
|
|
159
230
|
const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
|
|
160
231
|
assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
|
|
161
|
-
const veramoProofFormat: VeramoProofFormat = proofFormat as VeramoProofFormat
|
|
162
232
|
|
|
163
233
|
const { issuer, id } = getAssertedValues(args)
|
|
164
234
|
const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: args.encodedList, statusSize: bitsPerStatus })
|
|
165
235
|
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
166
236
|
statusList.setStatus(index, args.value)
|
|
167
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
|
+
|
|
168
248
|
const credential = await this.createVerifiableCredential(
|
|
169
249
|
{
|
|
250
|
+
unsignedCredential,
|
|
170
251
|
id,
|
|
171
252
|
issuer,
|
|
172
|
-
|
|
173
|
-
proofFormat: veramoProofFormat,
|
|
253
|
+
proofFormat,
|
|
174
254
|
keyRef: args.keyRef,
|
|
175
|
-
statusPurpose,
|
|
176
|
-
validFrom: ensureDate(validFrom),
|
|
177
|
-
validUntil: ensureDate(validUntil),
|
|
178
|
-
ttl,
|
|
179
255
|
},
|
|
180
256
|
context,
|
|
181
257
|
)
|
|
@@ -183,34 +259,38 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
183
259
|
return {
|
|
184
260
|
type: StatusListType.BitstringStatusList,
|
|
185
261
|
statusListCredential: credential,
|
|
186
|
-
encodedList:
|
|
262
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
187
263
|
bitstringStatusList: {
|
|
188
264
|
statusPurpose,
|
|
189
265
|
bitsPerStatus,
|
|
190
|
-
...(
|
|
191
|
-
...(
|
|
266
|
+
...(unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }),
|
|
267
|
+
...(unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }),
|
|
192
268
|
ttl,
|
|
193
269
|
},
|
|
194
270
|
length: statusList.getLength(),
|
|
195
271
|
proofFormat: args.proofFormat ?? 'lds',
|
|
196
|
-
id
|
|
197
|
-
issuer
|
|
272
|
+
id,
|
|
273
|
+
issuer,
|
|
198
274
|
statuslistContentType: this.buildContentType(proofFormat),
|
|
199
275
|
}
|
|
200
276
|
}
|
|
201
277
|
|
|
202
|
-
|
|
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> {
|
|
203
285
|
if (!args.bitsPerStatus || args.bitsPerStatus < 1) {
|
|
204
|
-
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)'))
|
|
205
287
|
}
|
|
206
288
|
|
|
207
289
|
const uniform = CredentialMapper.toUniformCredential(args.statusListCredential)
|
|
208
290
|
const { credentialSubject } = uniform
|
|
209
291
|
const encodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
210
292
|
|
|
211
|
-
const
|
|
212
|
-
const statusList = await BitstreamStatusList.decode({ encodedList, statusSize })
|
|
213
|
-
|
|
293
|
+
const statusList = await BitstreamStatusList.decode({ encodedList, statusSize: args.bitsPerStatus })
|
|
214
294
|
const numIndex = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
215
295
|
if (statusList.getLength() <= numIndex) {
|
|
216
296
|
throw new Error(`Status list index out of bounds, has ${statusList.getLength()} entries, requested ${numIndex}`)
|
|
@@ -218,59 +298,120 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
218
298
|
return statusList.getStatus(numIndex)
|
|
219
299
|
}
|
|
220
300
|
|
|
221
|
-
async
|
|
222
|
-
const
|
|
223
|
-
if (!bitsPerStatus || bitsPerStatus < 1) {
|
|
224
|
-
return Promise.reject('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (toStatusListDetails)')
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const uniform = CredentialMapper.toUniformCredential(statusListPayload)
|
|
301
|
+
async extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails> {
|
|
302
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
228
303
|
const { issuer, credentialSubject } = uniform
|
|
229
|
-
const
|
|
230
|
-
const encodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
231
|
-
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListPayload) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
232
|
-
const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
233
|
-
const statusPurpose = getAssertedProperty('statusPurpose', credSubject)
|
|
234
|
-
const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : undefined
|
|
235
|
-
const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
|
|
236
|
-
const ttl = credSubject.ttl
|
|
237
|
-
const statuslistLength: number = BitstreamStatusList.getStatusListLength(encodedList, bitsPerStatus)
|
|
304
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
238
305
|
|
|
239
306
|
return {
|
|
240
|
-
|
|
241
|
-
id,
|
|
242
|
-
encodedList,
|
|
307
|
+
id: getAssertedValue('id', uniform.id),
|
|
243
308
|
issuer,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
statusListCredential: statusListPayload,
|
|
248
|
-
statuslistContentType: this.buildContentType(proofFormat),
|
|
249
|
-
correlationId: args.correlationId, // FIXME these do not need to be inside the impl
|
|
250
|
-
driverType: args.driverType, // FIXME these do not need to be inside the impl
|
|
251
|
-
|
|
252
|
-
// Flattened Bitstring-specific fields
|
|
253
|
-
statusPurpose,
|
|
254
|
-
bitsPerStatus,
|
|
255
|
-
...(validFrom && { validFrom }),
|
|
256
|
-
...(validUntil && { validUntil }),
|
|
257
|
-
...(ttl && { ttl }),
|
|
309
|
+
encodedList: getAssertedProperty('encodedList', subject),
|
|
310
|
+
}
|
|
311
|
+
}
|
|
258
312
|
|
|
259
|
-
|
|
260
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Converts a status list credential payload to detailed status list information
|
|
315
|
+
*
|
|
316
|
+
* @param args - Conversion parameters including the status list payload
|
|
317
|
+
* @returns Promise resolving to detailed status list information
|
|
318
|
+
*/
|
|
319
|
+
// For CREATE and READ contexts
|
|
320
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IBitstringStatusListImplementationResult>
|
|
321
|
+
// For UPDATE contexts
|
|
322
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IBitstringStatusListImplementationResult>
|
|
323
|
+
async toStatusListDetails(
|
|
324
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
325
|
+
): Promise<StatusListResult & IBitstringStatusListImplementationResult> {
|
|
326
|
+
if ('statusListCredential' in args) {
|
|
327
|
+
// CREATE/READ context
|
|
328
|
+
const { statusListCredential, bitsPerStatus, correlationId, driverType } = args
|
|
329
|
+
if (!bitsPerStatus || bitsPerStatus < 1) {
|
|
330
|
+
return Promise.reject(Error('bitsPerStatus must be set for bitstring status lists and must be 1 or higher'))
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const uniform = CredentialMapper.toUniformCredential(statusListCredential)
|
|
334
|
+
const { issuer, credentialSubject } = uniform
|
|
335
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
336
|
+
|
|
337
|
+
const id = getAssertedValue('id', uniform.id)
|
|
338
|
+
const encodedList = getAssertedProperty('encodedList', subject)
|
|
339
|
+
const statusPurpose = getAssertedProperty('statusPurpose', subject)
|
|
340
|
+
const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : undefined
|
|
341
|
+
const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
|
|
342
|
+
const ttl = subject.ttl
|
|
343
|
+
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListCredential) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
344
|
+
const statuslistLength = BitstreamStatusList.getStatusListLength(encodedList, bitsPerStatus)
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
id,
|
|
348
|
+
encodedList,
|
|
349
|
+
issuer,
|
|
350
|
+
type: StatusListType.BitstringStatusList,
|
|
351
|
+
proofFormat,
|
|
352
|
+
length: statuslistLength,
|
|
353
|
+
statusListCredential,
|
|
354
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
355
|
+
correlationId,
|
|
356
|
+
driverType,
|
|
261
357
|
statusPurpose,
|
|
262
358
|
bitsPerStatus,
|
|
263
359
|
...(validFrom && { validFrom }),
|
|
264
360
|
...(validUntil && { validUntil }),
|
|
265
361
|
...(ttl && { ttl }),
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
362
|
+
bitstringStatusList: {
|
|
363
|
+
statusPurpose,
|
|
364
|
+
bitsPerStatus,
|
|
365
|
+
...(validFrom && { validFrom }),
|
|
366
|
+
...(validUntil && { validUntil }),
|
|
367
|
+
...(ttl && { ttl }),
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
// UPDATE context
|
|
372
|
+
const { extractedDetails, statusListEntity } = args
|
|
373
|
+
const bitstringEntity = statusListEntity as BitstringStatusListEntity
|
|
374
|
+
if (!bitstringEntity.bitsPerStatus) {
|
|
375
|
+
return Promise.reject(Error('bitsPerStatus must be present for a bitstring status list'))
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const proofFormat: CredentialProofFormat =
|
|
379
|
+
CredentialMapper.detectDocumentType(statusListEntity.statusListCredential!) === DocumentFormat.JWT ? 'vc+jwt' : 'lds'
|
|
380
|
+
const statuslistLength = BitstreamStatusList.getStatusListLength(extractedDetails.encodedList, bitstringEntity.bitsPerStatus)
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
id: extractedDetails.id,
|
|
384
|
+
encodedList: extractedDetails.encodedList,
|
|
385
|
+
issuer: extractedDetails.issuer,
|
|
386
|
+
type: StatusListType.BitstringStatusList,
|
|
387
|
+
proofFormat,
|
|
388
|
+
length: statuslistLength,
|
|
389
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
390
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
391
|
+
correlationId: statusListEntity.correlationId,
|
|
392
|
+
driverType: statusListEntity.driverType,
|
|
393
|
+
statusPurpose: bitstringEntity.statusPurpose,
|
|
394
|
+
bitsPerStatus: bitstringEntity.bitsPerStatus,
|
|
395
|
+
...(bitstringEntity.validFrom && { validFrom: bitstringEntity.validFrom }),
|
|
396
|
+
...(bitstringEntity.validUntil && { validUntil: bitstringEntity.validUntil }),
|
|
397
|
+
...(bitstringEntity.ttl && { ttl: bitstringEntity.ttl }),
|
|
398
|
+
bitstringStatusList: {
|
|
399
|
+
statusPurpose: bitstringEntity.statusPurpose,
|
|
400
|
+
bitsPerStatus: bitstringEntity.bitsPerStatus,
|
|
401
|
+
...(bitstringEntity.validFrom && { validFrom: bitstringEntity.validFrom }),
|
|
402
|
+
...(bitstringEntity.validUntil && { validUntil: bitstringEntity.validUntil }),
|
|
403
|
+
...(bitstringEntity.ttl && { ttl: bitstringEntity.ttl }),
|
|
404
|
+
},
|
|
405
|
+
}
|
|
271
406
|
}
|
|
272
407
|
}
|
|
273
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Creates a credential status entry for a specific credential in a status list
|
|
411
|
+
*
|
|
412
|
+
* @param args - Parameters including the status list, entry details, and index
|
|
413
|
+
* @returns Promise resolving to the credential status entry
|
|
414
|
+
*/
|
|
274
415
|
async createCredentialStatus(args: {
|
|
275
416
|
statusList: StatusListEntity
|
|
276
417
|
statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
|
|
@@ -278,66 +419,69 @@ export class BitstringStatusListImplementation implements IStatusList {
|
|
|
278
419
|
}): Promise<BitstringStatusListEntryCredentialStatus> {
|
|
279
420
|
const { statusList, statusListEntry, statusListIndex } = args
|
|
280
421
|
|
|
281
|
-
// Type guard to ensure we have a bitstring entry
|
|
282
|
-
const isBitstringEntry = (entry: IStatusListEntryEntity | IBitstringStatusListEntryEntity): entry is IBitstringStatusListEntryEntity => {
|
|
283
|
-
return 'statusPurpose' in entry
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (!isBitstringEntry(statusListEntry)) {
|
|
287
|
-
throw new Error('Expected bitstring status list entry for bitstring status list')
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Cast to BitstringStatusListEntity to access specific properties
|
|
291
422
|
const bitstringStatusList = statusList as BitstringStatusListEntity
|
|
292
|
-
|
|
423
|
+
const bitstringStatusListEntry = statusListEntry as IBitstringStatusListEntryEntity
|
|
293
424
|
return {
|
|
294
425
|
id: `${statusList.id}#${statusListIndex}`,
|
|
295
426
|
type: 'BitstringStatusListEntry',
|
|
296
|
-
statusPurpose:
|
|
427
|
+
statusPurpose: bitstringStatusListEntry.statusPurpose,
|
|
297
428
|
statusListIndex: '' + statusListIndex,
|
|
298
429
|
statusListCredential: statusList.id,
|
|
299
430
|
bitsPerStatus: bitstringStatusList.bitsPerStatus,
|
|
431
|
+
statusMessage: bitstringStatusListEntry.statusMessage,
|
|
432
|
+
statusReference: bitstringStatusListEntry.statusReference,
|
|
300
433
|
} satisfies BitstringStatusListEntryCredentialStatus
|
|
301
434
|
}
|
|
302
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Creates a signed verifiable credential from an unsigned status list credential
|
|
438
|
+
*
|
|
439
|
+
* @param args - Parameters including the unsigned credential and signing details
|
|
440
|
+
* @param context - Veramo agent context for credential operations
|
|
441
|
+
* @returns Promise resolving to the signed credential
|
|
442
|
+
*/
|
|
303
443
|
private async createVerifiableCredential(
|
|
304
444
|
args: {
|
|
445
|
+
unsignedCredential: BitstringStatusListCredentialUnsigned
|
|
305
446
|
id: string
|
|
306
447
|
issuer: string | IIssuer
|
|
307
|
-
|
|
308
|
-
proofFormat: VeramoProofFormat
|
|
309
|
-
statusPurpose: BitstringStatusPurpose
|
|
310
|
-
validFrom?: Date
|
|
311
|
-
validUntil?: Date
|
|
312
|
-
ttl?: number
|
|
448
|
+
proofFormat: CredentialProofFormat
|
|
313
449
|
keyRef?: string
|
|
314
450
|
},
|
|
315
|
-
context: IAgentContext<
|
|
451
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
316
452
|
): Promise<BitstringStatusListCredential> {
|
|
453
|
+
const { unsignedCredential, issuer, proofFormat, keyRef } = args
|
|
454
|
+
|
|
317
455
|
const identifier = await context.agent.identifierManagedGet({
|
|
318
|
-
identifier: typeof
|
|
456
|
+
identifier: typeof issuer === 'string' ? issuer : issuer.id,
|
|
319
457
|
vmRelationship: 'assertionMethod',
|
|
320
458
|
offlineWhenNoDIDRegistered: true,
|
|
321
459
|
})
|
|
322
460
|
|
|
323
|
-
const unsignedCredential = await createStatusListCredential(args)
|
|
324
|
-
|
|
325
461
|
const verifiableCredential = await context.agent.createVerifiableCredential({
|
|
326
462
|
credential: unsignedCredential,
|
|
327
|
-
keyRef:
|
|
328
|
-
proofFormat
|
|
463
|
+
keyRef: keyRef ?? identifier.kmsKeyRef,
|
|
464
|
+
proofFormat,
|
|
329
465
|
fetchRemoteContexts: true,
|
|
330
466
|
})
|
|
331
467
|
|
|
332
468
|
return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as BitstringStatusListCredential
|
|
333
469
|
}
|
|
334
470
|
|
|
335
|
-
|
|
471
|
+
/**
|
|
472
|
+
* Builds the appropriate content type string for a given proof format
|
|
473
|
+
*
|
|
474
|
+
* @param proofFormat - The proof format to build content type for
|
|
475
|
+
* @returns The corresponding content type string
|
|
476
|
+
*/
|
|
477
|
+
private buildContentType(proofFormat: CredentialProofFormat | undefined): string {
|
|
336
478
|
switch (proofFormat) {
|
|
337
479
|
case 'jwt':
|
|
338
|
-
return
|
|
480
|
+
return 'application/statuslist+jwt'
|
|
339
481
|
case 'cbor':
|
|
340
|
-
return
|
|
482
|
+
return 'application/statuslist+cwt'
|
|
483
|
+
case 'vc+jwt':
|
|
484
|
+
return 'application/statuslist+vc+jwt'
|
|
341
485
|
case 'lds':
|
|
342
486
|
return 'application/statuslist+ld+json'
|
|
343
487
|
default:
|