@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/dist/index.cjs +234 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -32
- package/dist/index.d.ts +43 -32
- package/dist/index.js +235 -86
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/src/functions.ts +39 -33
- package/src/impl/BitstringStatusListImplementation.ts +210 -67
- package/src/impl/IStatusList.ts +71 -6
- package/src/impl/OAuthStatusList.ts +48 -11
- package/src/impl/StatusList2021.ts +50 -14
- package/src/types/index.ts +32 -34
- package/src/utils.ts +1 -1
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,
|
|
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<(
|
|
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<
|
|
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
|
-
|
|
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<
|
|
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<
|
|
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
|
-
|
|
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 {
|
|
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<
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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:
|
|
62
|
-
statusListCredential
|
|
109
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
110
|
+
statusListCredential,
|
|
63
111
|
bitstringStatusList: {
|
|
64
112
|
statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
|
|
65
|
-
...(
|
|
66
|
-
...(
|
|
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<
|
|
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
|
-
|
|
175
|
+
unsignedCredential,
|
|
111
176
|
id,
|
|
112
177
|
issuer,
|
|
113
|
-
|
|
114
|
-
|
|
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:
|
|
186
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
126
187
|
bitstringStatusList: {
|
|
127
188
|
statusPurpose,
|
|
128
|
-
...(
|
|
129
|
-
...(
|
|
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
|
|
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<
|
|
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
|
-
|
|
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:
|
|
256
|
+
encodedList: unsignedCredential.credentialSubject.encodedList,
|
|
184
257
|
bitstringStatusList: {
|
|
185
258
|
statusPurpose,
|
|
186
259
|
bitsPerStatus,
|
|
187
|
-
...(
|
|
188
|
-
...(
|
|
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
|
|
194
|
-
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
|
|
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
|
-
|
|
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
|
-
|
|
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<
|
|
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
|
|
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:
|
|
282
|
-
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
|
-
|
|
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
|
|
433
|
+
return 'application/statuslist+jwt'
|
|
293
434
|
case 'cbor':
|
|
294
|
-
return
|
|
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:
|