@sphereon/ssi-sdk.vc-status-list 0.34.1-feature.SSISDK.17.bitstring.sl.2 → 0.34.1-feature.SSISDK.17.bitstring.sl.24
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 +545 -242
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +158 -78
- package/dist/index.d.ts +158 -78
- package/dist/index.js +546 -243
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
- package/src/functions.ts +126 -48
- package/src/impl/BitstringStatusListImplementation.ts +319 -157
- package/src/impl/IStatusList.ts +102 -9
- package/src/impl/OAuthStatusList.ts +126 -35
- package/src/impl/StatusList2021.ts +112 -32
- package/src/index.ts +1 -0
- package/src/types/BitstringStatusList.ts +3 -41
- package/src/types/index.ts +48 -80
- package/src/utils.ts +81 -19
|
@@ -1,7 +1,25 @@
|
|
|
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
|
-
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 {
|
|
27
|
-
import {
|
|
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 = '
|
|
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<
|
|
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,
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
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<
|
|
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 =
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
181
|
+
unsignedCredential,
|
|
112
182
|
id,
|
|
113
183
|
issuer,
|
|
114
|
-
|
|
115
|
-
|
|
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.
|
|
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
|
}
|
|
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 =
|
|
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
|
|
159
|
-
|
|
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
|
-
|
|
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:
|
|
262
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
182
263
|
bitstringStatusList: {
|
|
183
264
|
statusPurpose,
|
|
184
|
-
|
|
185
|
-
|
|
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.
|
|
270
|
+
length: statusList.getLength(),
|
|
189
271
|
proofFormat: args.proofFormat ?? 'lds',
|
|
190
|
-
id
|
|
191
|
-
issuer
|
|
272
|
+
id,
|
|
273
|
+
issuer,
|
|
192
274
|
statuslistContentType: this.buildContentType(proofFormat),
|
|
193
275
|
}
|
|
194
276
|
}
|
|
195
277
|
|
|
196
|
-
|
|
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
|
|
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
|
-
|
|
206
|
-
|
|
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
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
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<
|
|
270
|
-
): Promise<
|
|
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
|
|
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:
|
|
315
|
-
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
|
|
473
|
+
return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as BitstringStatusListCredential
|
|
320
474
|
}
|
|
321
475
|
|
|
322
|
-
|
|
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
|
|
485
|
+
return 'application/statuslist+jwt'
|
|
326
486
|
case 'cbor':
|
|
327
|
-
return
|
|
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:
|