@sphereon/ssi-sdk.vc-status-list 0.34.0 → 0.34.1-feature.SSISDK.17.bitstring.sl.11
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 +424 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -26
- package/dist/index.d.ts +76 -26
- package/dist/index.js +425 -46
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/src/functions.ts +50 -14
- package/src/impl/BitstringStatusListImplementation.ts +347 -0
- package/src/impl/IStatusList.ts +66 -4
- package/src/impl/OAuthStatusList.ts +60 -18
- package/src/impl/StatusList2021.ts +47 -12
- package/src/impl/StatusListFactory.ts +2 -0
- package/src/types/BitstringStatusList.ts +4 -0
- package/src/types/index.ts +71 -22
- package/src/utils.ts +35 -2
package/src/functions.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
2
2
|
import {
|
|
3
3
|
CredentialMapper,
|
|
4
|
-
DocumentFormat,
|
|
5
4
|
type CredentialProofFormat,
|
|
5
|
+
DocumentFormat,
|
|
6
6
|
type StatusListCredential,
|
|
7
7
|
StatusListDriverType,
|
|
8
8
|
StatusListType,
|
|
@@ -10,14 +10,20 @@ import {
|
|
|
10
10
|
} from '@sphereon/ssi-types'
|
|
11
11
|
import type { CredentialStatus, DIDDocument, IAgentContext, ICredentialPlugin, ProofFormat as VeramoProofFormat } from '@veramo/core'
|
|
12
12
|
|
|
13
|
+
import { IAddStatusListArgs, IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
14
|
+
|
|
13
15
|
import { checkStatus } from '@sphereon/vc-status-list'
|
|
14
16
|
|
|
15
17
|
// @ts-ignore
|
|
16
18
|
import { CredentialJwtOrJSON, StatusMethod } from 'credential-status'
|
|
17
19
|
import {
|
|
20
|
+
BitstringStatus,
|
|
21
|
+
BitstringStatusListEntryCredentialStatus,
|
|
18
22
|
CreateNewStatusListFuncArgs,
|
|
19
23
|
Status2021,
|
|
24
|
+
StatusList2021EntryCredentialStatus,
|
|
20
25
|
StatusList2021ToVerifiableCredentialArgs,
|
|
26
|
+
StatusListOAuthEntryCredentialStatus,
|
|
21
27
|
StatusListResult,
|
|
22
28
|
StatusOAuth,
|
|
23
29
|
UpdateStatusListFromEncodedListArgs,
|
|
@@ -117,7 +123,7 @@ export async function checkStatusForCredential(args: {
|
|
|
117
123
|
return { verified: true }
|
|
118
124
|
}
|
|
119
125
|
if ('credentialStatus' in uniform && uniform.credentialStatus) {
|
|
120
|
-
if (uniform.credentialStatus.type === 'StatusList2021Entry') {
|
|
126
|
+
if (uniform.credentialStatus.type === 'StatusList2021Entry' || uniform.credentialStatus.type === 'BitstringStatusListEntry') {
|
|
121
127
|
return checkStatus({ ...args, verifyStatusListCredential, verifyMatchingIssuers })
|
|
122
128
|
} else if (args?.errorUnknownListType) {
|
|
123
129
|
const error = `Credential status type ${uniform.credentialStatus.type} is not supported, and check status has been configured to not allow for that`
|
|
@@ -136,7 +142,7 @@ export async function simpleCheckStatusFromStatusListUrl(args: {
|
|
|
136
142
|
type?: StatusListType | 'StatusList2021Entry'
|
|
137
143
|
id?: string
|
|
138
144
|
statusListIndex: string
|
|
139
|
-
}): Promise<number | Status2021 | StatusOAuth> {
|
|
145
|
+
}): Promise<number | Status2021 | StatusOAuth | BitstringStatus> {
|
|
140
146
|
return checkStatusIndexFromStatusListCredential({
|
|
141
147
|
...args,
|
|
142
148
|
statusListCredential: await fetchStatusListCredential(args),
|
|
@@ -145,11 +151,12 @@ export async function simpleCheckStatusFromStatusListUrl(args: {
|
|
|
145
151
|
|
|
146
152
|
export async function checkStatusIndexFromStatusListCredential(args: {
|
|
147
153
|
statusListCredential: StatusListCredential
|
|
148
|
-
statusPurpose?: StatusPurpose2021
|
|
149
|
-
type?: StatusListType | 'StatusList2021Entry'
|
|
154
|
+
statusPurpose?: StatusPurpose2021 | string | string[]
|
|
155
|
+
type?: StatusListType | 'StatusList2021Entry' | 'BitstringStatusListEntry'
|
|
150
156
|
id?: string
|
|
151
157
|
statusListIndex: string | number
|
|
152
|
-
|
|
158
|
+
bitsPerStatus?: number
|
|
159
|
+
}): Promise<number | Status2021 | StatusOAuth | BitstringStatus> {
|
|
153
160
|
const statusListType: StatusListType = determineStatusListType(args.statusListCredential)
|
|
154
161
|
const implementation = getStatusListImplementation(statusListType)
|
|
155
162
|
return implementation.checkStatusIndex(args)
|
|
@@ -174,13 +181,18 @@ export async function updateStatusIndexFromStatusListCredential(
|
|
|
174
181
|
return implementation.updateStatusListIndex(args, context)
|
|
175
182
|
}
|
|
176
183
|
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
export async function statusListCredentialToDetails({
|
|
185
|
+
correlationId,
|
|
186
|
+
driverType,
|
|
187
|
+
statusListCredential,
|
|
188
|
+
bitsPerStatus,
|
|
189
|
+
}: {
|
|
179
190
|
statusListCredential: StatusListCredential
|
|
180
191
|
correlationId?: string
|
|
181
192
|
driverType?: StatusListDriverType
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
bitsPerStatus?: number
|
|
194
|
+
}): Promise<StatusListResult & Partial<IAddStatusListArgs>> {
|
|
195
|
+
const credential = getAssertedValue('statusListCredential', statusListCredential)
|
|
184
196
|
|
|
185
197
|
let statusListType: StatusListType | undefined
|
|
186
198
|
const documentFormat = CredentialMapper.detectDocumentType(credential)
|
|
@@ -197,7 +209,7 @@ export async function statusListCredentialToDetails(args: {
|
|
|
197
209
|
}
|
|
198
210
|
if (!statusListType) {
|
|
199
211
|
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
200
|
-
const type = uniform.type.find((t) => t.includes('StatusList2021') || t.includes('OAuth2StatusList'))
|
|
212
|
+
const type = uniform.type.find((t) => t.includes('StatusList2021') || t.includes('OAuth2StatusList') || t.includes('BitstringStatusList'))
|
|
201
213
|
if (!type) {
|
|
202
214
|
throw new Error('Invalid status list credential type')
|
|
203
215
|
}
|
|
@@ -205,10 +217,34 @@ export async function statusListCredentialToDetails(args: {
|
|
|
205
217
|
}
|
|
206
218
|
|
|
207
219
|
const implementation = getStatusListImplementation(statusListType)
|
|
208
|
-
|
|
220
|
+
|
|
221
|
+
// The implementation should now return all the type-specific fields needed for the entity
|
|
222
|
+
const result = await implementation.toStatusListDetails({
|
|
209
223
|
statusListPayload: credential,
|
|
210
|
-
correlationId:
|
|
211
|
-
driverType:
|
|
224
|
+
correlationId: correlationId,
|
|
225
|
+
driverType: driverType,
|
|
226
|
+
bitsPerStatus: bitsPerStatus,
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
return result
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function createCredentialStatusFromStatusList(args: {
|
|
233
|
+
statusList: StatusListEntity
|
|
234
|
+
statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
|
|
235
|
+
statusListIndex: number
|
|
236
|
+
}): Promise<StatusList2021EntryCredentialStatus | StatusListOAuthEntryCredentialStatus | BitstringStatusListEntryCredentialStatus> {
|
|
237
|
+
const { statusList, statusListEntry, statusListIndex } = args
|
|
238
|
+
|
|
239
|
+
// Determine the status list type and delegate to appropriate implementation
|
|
240
|
+
const statusListType = determineStatusListType(statusList.statusListCredential!)
|
|
241
|
+
const implementation = getStatusListImplementation(statusListType)
|
|
242
|
+
|
|
243
|
+
// Each implementation should have a method to create credential status
|
|
244
|
+
return implementation.createCredentialStatus({
|
|
245
|
+
statusList,
|
|
246
|
+
statusListEntry,
|
|
247
|
+
statusListIndex,
|
|
212
248
|
})
|
|
213
249
|
}
|
|
214
250
|
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import type { IAgentContext, ICredentialPlugin, ProofFormat as VeramoProofFormat } from '@veramo/core'
|
|
2
|
+
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CredentialMapper,
|
|
6
|
+
type CredentialProofFormat,
|
|
7
|
+
DocumentFormat,
|
|
8
|
+
type IIssuer,
|
|
9
|
+
type StatusListCredential,
|
|
10
|
+
StatusListType,
|
|
11
|
+
} from '@sphereon/ssi-types'
|
|
12
|
+
|
|
13
|
+
import type { IBitstringStatusListImplementationResult, IStatusList } from './IStatusList'
|
|
14
|
+
import {
|
|
15
|
+
BitstringStatus,
|
|
16
|
+
BitstringStatusListEntryCredentialStatus,
|
|
17
|
+
CheckStatusIndexArgs,
|
|
18
|
+
CreateStatusListArgs,
|
|
19
|
+
StatusListResult,
|
|
20
|
+
ToStatusListDetailsArgs,
|
|
21
|
+
UpdateStatusListFromEncodedListArgs,
|
|
22
|
+
UpdateStatusListIndexArgs,
|
|
23
|
+
} from '../types'
|
|
24
|
+
|
|
25
|
+
import { assertValidProofType, ensureDate, getAssertedProperty, getAssertedValue, getAssertedValues } from '../utils'
|
|
26
|
+
import { BitstringStatusListCredential } from '../types/BitstringStatusList'
|
|
27
|
+
import { BitstreamStatusList, BitstringStatusPurpose, createStatusListCredential } from '@4sure-tech/vc-bitstring-status-lists'
|
|
28
|
+
import { BitstringStatusListEntity, IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
29
|
+
|
|
30
|
+
export const DEFAULT_LIST_LENGTH = 131072 // W3C spec minimum
|
|
31
|
+
export const DEFAULT_PROOF_FORMAT = 'lds' as CredentialProofFormat
|
|
32
|
+
export const DEFAULT_STATUS_PURPOSE: BitstringStatusPurpose = 'revocation'
|
|
33
|
+
|
|
34
|
+
export class BitstringStatusListImplementation implements IStatusList {
|
|
35
|
+
async createNewStatusList(
|
|
36
|
+
args: CreateStatusListArgs,
|
|
37
|
+
context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
|
|
38
|
+
): Promise<StatusListResult> {
|
|
39
|
+
if (!args.bitstringStatusList) {
|
|
40
|
+
throw new Error('BitstringStatusList options are required for type BitstringStatusList')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const length = args?.length ?? DEFAULT_LIST_LENGTH
|
|
44
|
+
const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
|
|
45
|
+
assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
|
|
46
|
+
const veramoProofFormat: VeramoProofFormat = proofFormat as VeramoProofFormat
|
|
47
|
+
|
|
48
|
+
const { issuer, id } = args
|
|
49
|
+
const correlationId = getAssertedValue('correlationId', args.correlationId)
|
|
50
|
+
const { statusPurpose, bitsPerStatus, validFrom, validUntil, ttl } = args.bitstringStatusList
|
|
51
|
+
const statusListCredential = await this.createVerifiableCredential(
|
|
52
|
+
{
|
|
53
|
+
...args,
|
|
54
|
+
proofFormat: veramoProofFormat,
|
|
55
|
+
statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
|
|
56
|
+
validFrom: ensureDate(validFrom),
|
|
57
|
+
validUntil: ensureDate(validUntil),
|
|
58
|
+
ttl,
|
|
59
|
+
},
|
|
60
|
+
context,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
encodedList: statusListCredential.credentialSubject.encodedList,
|
|
65
|
+
statusListCredential: statusListCredential,
|
|
66
|
+
bitstringStatusList: {
|
|
67
|
+
statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE,
|
|
68
|
+
...(statusListCredential.validFrom && { validFrom: new Date(statusListCredential.validFrom) }),
|
|
69
|
+
...(statusListCredential.validUntil && { validUntil: new Date(statusListCredential.validUntil) }),
|
|
70
|
+
ttl,
|
|
71
|
+
bitsPerStatus,
|
|
72
|
+
},
|
|
73
|
+
length,
|
|
74
|
+
type: StatusListType.BitstringStatusList,
|
|
75
|
+
proofFormat,
|
|
76
|
+
id,
|
|
77
|
+
correlationId,
|
|
78
|
+
issuer,
|
|
79
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async updateStatusListIndex(
|
|
84
|
+
args: UpdateStatusListIndexArgs,
|
|
85
|
+
context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
|
|
86
|
+
): Promise<StatusListResult> {
|
|
87
|
+
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)')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const credential = args.statusListCredential
|
|
92
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
93
|
+
const { issuer, credentialSubject } = uniform
|
|
94
|
+
const id = getAssertedValue('id', uniform.id)
|
|
95
|
+
const origEncodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
96
|
+
|
|
97
|
+
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
98
|
+
const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: origEncodedList, statusSize: args.bitsPerStatus })
|
|
99
|
+
statusList.setStatus(index, args.value)
|
|
100
|
+
|
|
101
|
+
const proofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
102
|
+
|
|
103
|
+
const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
104
|
+
|
|
105
|
+
const statusPurpose = getAssertedProperty('statusPurpose', credSubject)
|
|
106
|
+
|
|
107
|
+
const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : undefined
|
|
108
|
+
const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : undefined
|
|
109
|
+
const ttl = credSubject.ttl
|
|
110
|
+
|
|
111
|
+
const updatedCredential = await this.createVerifiableCredential(
|
|
112
|
+
{
|
|
113
|
+
...args,
|
|
114
|
+
id,
|
|
115
|
+
issuer,
|
|
116
|
+
statusList,
|
|
117
|
+
proofFormat: proofFormat,
|
|
118
|
+
statusPurpose,
|
|
119
|
+
ttl,
|
|
120
|
+
validFrom,
|
|
121
|
+
validUntil,
|
|
122
|
+
},
|
|
123
|
+
context,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
statusListCredential: updatedCredential,
|
|
128
|
+
encodedList: updatedCredential.credentialSubject.encodedList,
|
|
129
|
+
bitstringStatusList: {
|
|
130
|
+
statusPurpose,
|
|
131
|
+
...(updatedCredential.validFrom && { validFrom: new Date(updatedCredential.validFrom) }),
|
|
132
|
+
...(updatedCredential.validUntil && { validUntil: new Date(updatedCredential.validUntil) }),
|
|
133
|
+
bitsPerStatus: args.bitsPerStatus,
|
|
134
|
+
ttl,
|
|
135
|
+
},
|
|
136
|
+
length: statusList.getLength(),
|
|
137
|
+
type: StatusListType.BitstringStatusList,
|
|
138
|
+
proofFormat: proofFormat,
|
|
139
|
+
id,
|
|
140
|
+
issuer,
|
|
141
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async updateStatusListFromEncodedList(
|
|
146
|
+
args: UpdateStatusListFromEncodedListArgs,
|
|
147
|
+
context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
|
|
148
|
+
): Promise<StatusListResult> {
|
|
149
|
+
if (!args.bitstringStatusList) {
|
|
150
|
+
throw new Error('bitstringStatusList options required for type BitstringStatusList')
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (args.bitstringStatusList.bitsPerStatus < 1) {
|
|
154
|
+
return Promise.reject('bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListFromEncodedList)')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const { statusPurpose, bitsPerStatus, ttl, validFrom, validUntil } = args.bitstringStatusList
|
|
158
|
+
|
|
159
|
+
const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
|
|
160
|
+
assertValidProofType(StatusListType.BitstringStatusList, proofFormat)
|
|
161
|
+
const veramoProofFormat: VeramoProofFormat = proofFormat as VeramoProofFormat
|
|
162
|
+
|
|
163
|
+
const { issuer, id } = getAssertedValues(args)
|
|
164
|
+
const statusList: BitstreamStatusList = await BitstreamStatusList.decode({ encodedList: args.encodedList, statusSize: bitsPerStatus })
|
|
165
|
+
const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
166
|
+
statusList.setStatus(index, args.value)
|
|
167
|
+
|
|
168
|
+
const credential = await this.createVerifiableCredential(
|
|
169
|
+
{
|
|
170
|
+
id,
|
|
171
|
+
issuer,
|
|
172
|
+
statusList,
|
|
173
|
+
proofFormat: veramoProofFormat,
|
|
174
|
+
keyRef: args.keyRef,
|
|
175
|
+
statusPurpose,
|
|
176
|
+
validFrom: ensureDate(validFrom),
|
|
177
|
+
validUntil: ensureDate(validUntil),
|
|
178
|
+
ttl,
|
|
179
|
+
},
|
|
180
|
+
context,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
type: StatusListType.BitstringStatusList,
|
|
185
|
+
statusListCredential: credential,
|
|
186
|
+
encodedList: credential.credentialSubject.encodedList,
|
|
187
|
+
bitstringStatusList: {
|
|
188
|
+
statusPurpose,
|
|
189
|
+
bitsPerStatus,
|
|
190
|
+
...(credential.validFrom && { validFrom: new Date(credential.validFrom) }),
|
|
191
|
+
...(credential.validUntil && { validUntil: new Date(credential.validUntil) }),
|
|
192
|
+
ttl,
|
|
193
|
+
},
|
|
194
|
+
length: statusList.getLength(),
|
|
195
|
+
proofFormat: args.proofFormat ?? 'lds',
|
|
196
|
+
id: id,
|
|
197
|
+
issuer: issuer,
|
|
198
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async checkStatusIndex(args: CheckStatusIndexArgs): Promise<BitstringStatus> {
|
|
203
|
+
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)')
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const uniform = CredentialMapper.toUniformCredential(args.statusListCredential)
|
|
208
|
+
const { credentialSubject } = uniform
|
|
209
|
+
const encodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
210
|
+
|
|
211
|
+
const statusSize = args.bitsPerStatus
|
|
212
|
+
const statusList = await BitstreamStatusList.decode({ encodedList, statusSize })
|
|
213
|
+
|
|
214
|
+
const numIndex = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
|
|
215
|
+
if (statusList.getLength() <= numIndex) {
|
|
216
|
+
throw new Error(`Status list index out of bounds, has ${statusList.getLength()} entries, requested ${numIndex}`)
|
|
217
|
+
}
|
|
218
|
+
return statusList.getStatus(numIndex)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async toStatusListDetails(args: ToStatusListDetailsArgs): Promise<StatusListResult & IBitstringStatusListImplementationResult> {
|
|
222
|
+
const { statusListPayload, bitsPerStatus } = args
|
|
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)
|
|
228
|
+
const { issuer, credentialSubject } = uniform
|
|
229
|
+
const id = getAssertedValue('id', uniform.id)
|
|
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)
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
// Base implementation fields
|
|
241
|
+
id,
|
|
242
|
+
encodedList,
|
|
243
|
+
issuer,
|
|
244
|
+
type: StatusListType.BitstringStatusList,
|
|
245
|
+
proofFormat,
|
|
246
|
+
length: statuslistLength,
|
|
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 }),
|
|
258
|
+
|
|
259
|
+
// Legacy nested structure for backward compatibility
|
|
260
|
+
bitstringStatusList: {
|
|
261
|
+
statusPurpose,
|
|
262
|
+
bitsPerStatus,
|
|
263
|
+
...(validFrom && { validFrom }),
|
|
264
|
+
...(validUntil && { validUntil }),
|
|
265
|
+
...(ttl && { ttl }),
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
// Optional fields from args
|
|
269
|
+
...(args.correlationId && { correlationId: args.correlationId }),
|
|
270
|
+
...(args.driverType && { driverType: args.driverType }),
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async createCredentialStatus(args: {
|
|
275
|
+
statusList: StatusListEntity
|
|
276
|
+
statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
|
|
277
|
+
statusListIndex: number
|
|
278
|
+
}): Promise<BitstringStatusListEntryCredentialStatus> {
|
|
279
|
+
const { statusList, statusListEntry, statusListIndex } = args
|
|
280
|
+
|
|
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
|
+
const bitstringStatusList = statusList as BitstringStatusListEntity
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
id: `${statusList.id}#${statusListIndex}`,
|
|
295
|
+
type: 'BitstringStatusListEntry',
|
|
296
|
+
statusPurpose: statusListEntry.statusPurpose,
|
|
297
|
+
statusListIndex: '' + statusListIndex,
|
|
298
|
+
statusListCredential: statusList.id,
|
|
299
|
+
bitsPerStatus: bitstringStatusList.bitsPerStatus,
|
|
300
|
+
} satisfies BitstringStatusListEntryCredentialStatus
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private async createVerifiableCredential(
|
|
304
|
+
args: {
|
|
305
|
+
id: string
|
|
306
|
+
issuer: string | IIssuer
|
|
307
|
+
statusList?: BitstreamStatusList
|
|
308
|
+
proofFormat: VeramoProofFormat
|
|
309
|
+
statusPurpose: BitstringStatusPurpose
|
|
310
|
+
validFrom?: Date
|
|
311
|
+
validUntil?: Date
|
|
312
|
+
ttl?: number
|
|
313
|
+
keyRef?: string
|
|
314
|
+
},
|
|
315
|
+
context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
|
|
316
|
+
): Promise<BitstringStatusListCredential> {
|
|
317
|
+
const identifier = await context.agent.identifierManagedGet({
|
|
318
|
+
identifier: typeof args.issuer === 'string' ? args.issuer : args.issuer.id,
|
|
319
|
+
vmRelationship: 'assertionMethod',
|
|
320
|
+
offlineWhenNoDIDRegistered: true,
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
const unsignedCredential = await createStatusListCredential(args)
|
|
324
|
+
|
|
325
|
+
const verifiableCredential = await context.agent.createVerifiableCredential({
|
|
326
|
+
credential: unsignedCredential,
|
|
327
|
+
keyRef: args.keyRef ?? identifier.kmsKeyRef,
|
|
328
|
+
proofFormat: args.proofFormat,
|
|
329
|
+
fetchRemoteContexts: true,
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as BitstringStatusListCredential
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private buildContentType(proofFormat: CredentialProofFormat | undefined) {
|
|
336
|
+
switch (proofFormat) {
|
|
337
|
+
case 'jwt':
|
|
338
|
+
return `application/statuslist+jwt`
|
|
339
|
+
case 'cbor':
|
|
340
|
+
return `application/statuslist+cwt`
|
|
341
|
+
case 'lds':
|
|
342
|
+
return 'application/statuslist+ld+json'
|
|
343
|
+
default:
|
|
344
|
+
throw Error(`Unsupported content type '${proofFormat}' for status lists`)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
package/src/impl/IStatusList.ts
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import type { IAgentContext, ICredentialPlugin } from '@veramo/core'
|
|
2
2
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
BitstringStatus,
|
|
5
|
+
BitstringStatusListEntryCredentialStatus,
|
|
4
6
|
CheckStatusIndexArgs,
|
|
5
7
|
CreateStatusListArgs,
|
|
6
8
|
Status2021,
|
|
9
|
+
StatusList2021EntryCredentialStatus,
|
|
10
|
+
StatusListOAuthEntryCredentialStatus,
|
|
7
11
|
StatusListResult,
|
|
8
12
|
StatusOAuth,
|
|
9
13
|
ToStatusListDetailsArgs,
|
|
10
14
|
UpdateStatusListFromEncodedListArgs,
|
|
11
15
|
UpdateStatusListIndexArgs,
|
|
12
16
|
} from '../types'
|
|
17
|
+
import { BitstringStatusPurpose } from '@4sure-tech/vc-bitstring-status-lists'
|
|
18
|
+
import {
|
|
19
|
+
CredentialProofFormat,
|
|
20
|
+
IIssuer,
|
|
21
|
+
StatusListCredential,
|
|
22
|
+
StatusListDriverType,
|
|
23
|
+
StatusListIndexingDirection,
|
|
24
|
+
StatusListType,
|
|
25
|
+
StatusPurpose2021,
|
|
26
|
+
} from '@sphereon/ssi-types'
|
|
27
|
+
import { IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
13
28
|
|
|
14
29
|
export interface IStatusList {
|
|
15
30
|
/**
|
|
@@ -33,10 +48,57 @@ export interface IStatusList {
|
|
|
33
48
|
/**
|
|
34
49
|
* Checks the status at a given index in the status list
|
|
35
50
|
*/
|
|
36
|
-
checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth>
|
|
51
|
+
checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth | BitstringStatus>
|
|
37
52
|
|
|
38
53
|
/**
|
|
39
|
-
* Collects the status list details
|
|
54
|
+
* Collects the status list details - returns flattened entity data ready for storage
|
|
40
55
|
*/
|
|
41
|
-
toStatusListDetails(
|
|
56
|
+
toStatusListDetails(
|
|
57
|
+
args: ToStatusListDetailsArgs,
|
|
58
|
+
): Promise<
|
|
59
|
+
StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
|
|
60
|
+
>
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a credential status object from a status list and entry
|
|
64
|
+
*/
|
|
65
|
+
createCredentialStatus(args: {
|
|
66
|
+
statusList: StatusListEntity
|
|
67
|
+
statusListEntry: IStatusListEntryEntity | IBitstringStatusListEntryEntity
|
|
68
|
+
statusListIndex: number
|
|
69
|
+
}): Promise<StatusList2021EntryCredentialStatus | StatusListOAuthEntryCredentialStatus | BitstringStatusListEntryCredentialStatus>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface IStatusListImplementationResult {
|
|
73
|
+
id: string
|
|
74
|
+
encodedList: string
|
|
75
|
+
issuer: string | IIssuer
|
|
76
|
+
type: StatusListType
|
|
77
|
+
proofFormat: CredentialProofFormat
|
|
78
|
+
length: number
|
|
79
|
+
statusListCredential: StatusListCredential
|
|
80
|
+
statuslistContentType: string
|
|
81
|
+
correlationId?: string
|
|
82
|
+
driverType?: StatusListDriverType
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface IStatusList2021ImplementationResult extends IStatusListImplementationResult {
|
|
86
|
+
type: StatusListType.StatusList2021
|
|
87
|
+
indexingDirection: StatusListIndexingDirection
|
|
88
|
+
statusPurpose: StatusPurpose2021
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface IOAuthStatusListImplementationResult extends IStatusListImplementationResult {
|
|
92
|
+
type: StatusListType.OAuthStatusList
|
|
93
|
+
bitsPerStatus: number
|
|
94
|
+
expiresAt?: Date
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface IBitstringStatusListImplementationResult extends IStatusListImplementationResult {
|
|
98
|
+
type: StatusListType.BitstringStatusList
|
|
99
|
+
statusPurpose: BitstringStatusPurpose | BitstringStatusPurpose[]
|
|
100
|
+
bitsPerStatus?: number
|
|
101
|
+
validFrom?: Date
|
|
102
|
+
validUntil?: Date
|
|
103
|
+
ttl?: number
|
|
42
104
|
}
|