@sphereon/ssi-sdk.vc-status-list 0.32.1-feature.MWALL.717.jwt.decode.crash.15 → 0.32.1-feature.SDK.56.oauth.status.list.39

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.
Files changed (51) hide show
  1. package/dist/functions.d.ts +13 -13
  2. package/dist/functions.d.ts.map +1 -1
  3. package/dist/functions.js +38 -108
  4. package/dist/functions.js.map +1 -1
  5. package/dist/impl/IStatusList.d.ts +22 -0
  6. package/dist/impl/IStatusList.d.ts.map +1 -0
  7. package/dist/impl/IStatusList.js +3 -0
  8. package/dist/impl/IStatusList.js.map +1 -0
  9. package/dist/impl/OAuthStatusList.d.ts +21 -0
  10. package/dist/impl/OAuthStatusList.d.ts.map +1 -0
  11. package/dist/impl/OAuthStatusList.js +142 -0
  12. package/dist/impl/OAuthStatusList.js.map +1 -0
  13. package/dist/impl/StatusList2021.d.ts +14 -0
  14. package/dist/impl/StatusList2021.d.ts.map +1 -0
  15. package/dist/impl/StatusList2021.js +152 -0
  16. package/dist/impl/StatusList2021.js.map +1 -0
  17. package/dist/impl/StatusListFactory.d.ts +11 -0
  18. package/dist/impl/StatusListFactory.d.ts.map +1 -0
  19. package/dist/impl/StatusListFactory.js +32 -0
  20. package/dist/impl/StatusListFactory.js.map +1 -0
  21. package/dist/impl/encoding/cbor.d.ts +6 -0
  22. package/dist/impl/encoding/cbor.d.ts.map +1 -0
  23. package/dist/impl/encoding/cbor.js +132 -0
  24. package/dist/impl/encoding/cbor.js.map +1 -0
  25. package/dist/impl/encoding/common.d.ts +12 -0
  26. package/dist/impl/encoding/common.d.ts.map +1 -0
  27. package/dist/impl/encoding/common.js +26 -0
  28. package/dist/impl/encoding/common.js.map +1 -0
  29. package/dist/impl/encoding/jwt.d.ts +7 -0
  30. package/dist/impl/encoding/jwt.d.ts.map +1 -0
  31. package/dist/impl/encoding/jwt.js +56 -0
  32. package/dist/impl/encoding/jwt.js.map +1 -0
  33. package/dist/types/index.d.ts +107 -29
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/index.js +12 -0
  36. package/dist/types/index.js.map +1 -1
  37. package/dist/utils.d.ts +17 -0
  38. package/dist/utils.d.ts.map +1 -0
  39. package/dist/utils.js +87 -0
  40. package/dist/utils.js.map +1 -0
  41. package/package.json +11 -3
  42. package/src/functions.ts +57 -164
  43. package/src/impl/IStatusList.ts +36 -0
  44. package/src/impl/OAuthStatusList.ts +165 -0
  45. package/src/impl/StatusList2021.ts +194 -0
  46. package/src/impl/StatusListFactory.ts +34 -0
  47. package/src/impl/encoding/cbor.ts +160 -0
  48. package/src/impl/encoding/common.ts +25 -0
  49. package/src/impl/encoding/jwt.ts +54 -0
  50. package/src/types/index.ts +123 -41
  51. package/src/utils.ts +91 -0
package/src/functions.ts CHANGED
@@ -1,41 +1,35 @@
1
1
  import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
2
- import {
3
- CredentialMapper,
4
- DocumentFormat,
5
- IIssuer,
6
- OriginalVerifiableCredential,
7
- StatusListDriverType,
8
- StatusListType,
9
- StatusPurpose2021,
10
- } from '@sphereon/ssi-types'
2
+ import { CredentialMapper, ProofFormat, StatusListDriverType, StatusListType, StatusListCredential, StatusPurpose2021 } from '@sphereon/ssi-types'
3
+ import { CredentialStatus, DIDDocument, IAgentContext, ICredentialPlugin, ProofFormat as VmoProofFormat } from '@veramo/core'
11
4
 
12
- import { checkStatus, StatusList } from '@sphereon/vc-status-list'
13
- import { CredentialStatus, DIDDocument, IAgentContext, ICredentialPlugin, ProofFormat } from '@veramo/core'
5
+ import { checkStatus } from '@sphereon/vc-status-list'
14
6
  import { CredentialJwtOrJSON, StatusMethod } from 'credential-status'
15
7
  import {
16
8
  CreateNewStatusListFuncArgs,
9
+ Status2021,
17
10
  StatusList2021ToVerifiableCredentialArgs,
18
- StatusListDetails,
19
11
  StatusListResult,
12
+ StatusOAuth,
20
13
  UpdateStatusListFromEncodedListArgs,
21
14
  UpdateStatusListFromStatusListCredentialArgs,
22
15
  } from './types'
16
+ import { assertValidProofType, determineStatusListType, getAssertedValue, getAssertedValues } from './utils'
17
+ import { getStatusListImplementation } from './impl/StatusListFactory'
23
18
 
24
- export async function fetchStatusListCredential(args: { statusListCredential: string }): Promise<OriginalVerifiableCredential> {
19
+ export async function fetchStatusListCredential(args: { statusListCredential: string }): Promise<StatusListCredential> {
25
20
  const url = getAssertedValue('statusListCredential', args.statusListCredential)
26
21
  try {
27
22
  const response = await fetch(url)
28
23
  if (!response.ok) {
29
- const error = `Fetching status list ${url} resulted in an error: ${response.status} : ${response.statusText}`
30
- throw Error(error)
24
+ throw Error(`Fetching status list ${url} resulted in an error: ${response.status} : ${response.statusText}`)
31
25
  }
32
26
  const responseAsText = await response.text()
33
27
  if (responseAsText.trim().startsWith('{')) {
34
- return JSON.parse(responseAsText) as OriginalVerifiableCredential
28
+ return JSON.parse(responseAsText) as StatusListCredential
35
29
  }
36
- return responseAsText as OriginalVerifiableCredential
30
+ return responseAsText as StatusListCredential
37
31
  } catch (error) {
38
- console.log(`Fetching status list ${url} resulted in an unexpected error: ${error instanceof Error ? error.message : JSON.stringify(error)}`)
32
+ console.error(`Fetching status list ${url} resulted in an unexpected error: ${error instanceof Error ? error.message : JSON.stringify(error)}`)
39
33
  throw error
40
34
  }
41
35
  }
@@ -52,7 +46,7 @@ export function statusPluginStatusFunction(args: {
52
46
  const result = await checkStatusForCredential({
53
47
  ...args,
54
48
  documentLoader: args.documentLoader,
55
- credential: credential as OriginalVerifiableCredential,
49
+ credential: credential as StatusListCredential,
56
50
  errorUnknownListType: args.errorUnknownListType,
57
51
  })
58
52
 
@@ -75,7 +69,7 @@ export function vcLibCheckStatusFunction(args: {
75
69
  }) {
76
70
  const { mandatoryCredentialStatus, verifyStatusListCredential, verifyMatchingIssuers, errorUnknownListType } = args
77
71
  return (args: {
78
- credential: OriginalVerifiableCredential
72
+ credential: StatusListCredential
79
73
  documentLoader: any
80
74
  suite: any
81
75
  }): Promise<{
@@ -93,7 +87,7 @@ export function vcLibCheckStatusFunction(args: {
93
87
  }
94
88
 
95
89
  export async function checkStatusForCredential(args: {
96
- credential: OriginalVerifiableCredential
90
+ credential: StatusListCredential
97
91
  documentLoader: any
98
92
  suite: any
99
93
  mandatoryCredentialStatus?: boolean
@@ -132,7 +126,7 @@ export async function simpleCheckStatusFromStatusListUrl(args: {
132
126
  type?: StatusListType | 'StatusList2021Entry'
133
127
  id?: string
134
128
  statusListIndex: string
135
- }): Promise<boolean> {
129
+ }): Promise<number | Status2021 | StatusOAuth> {
136
130
  return checkStatusIndexFromStatusListCredential({
137
131
  ...args,
138
132
  statusListCredential: await fetchStatusListCredential(args),
@@ -140,163 +134,84 @@ export async function simpleCheckStatusFromStatusListUrl(args: {
140
134
  }
141
135
 
142
136
  export async function checkStatusIndexFromStatusListCredential(args: {
143
- statusListCredential: OriginalVerifiableCredential
137
+ statusListCredential: StatusListCredential
144
138
  statusPurpose?: StatusPurpose2021
145
139
  type?: StatusListType | 'StatusList2021Entry'
146
140
  id?: string
147
141
  statusListIndex: string | number
148
- }): Promise<boolean> {
149
- const requestedType = getAssertedStatusListType(args.type?.replace('Entry', '') as StatusListType)
150
- const uniform = CredentialMapper.toUniformCredential(args.statusListCredential)
151
- const { issuer, type, credentialSubject, id } = uniform
152
- getAssertedValue('issuer', issuer) // We are only checking the value here
153
- getAssertedValue('credentialSubject', credentialSubject)
154
- if (args.statusPurpose && 'statusPurpose' in credentialSubject) {
155
- if (args.statusPurpose !== credentialSubject.statusPurpose) {
156
- throw Error(
157
- `Status purpose in StatusList credential with id ${id} and value ${credentialSubject.statusPurpose} does not match supplied purpose: ${args.statusPurpose}`,
158
- )
159
- }
160
- } else if (args.id && args.id !== id) {
161
- throw Error(`Status list id ${id} did not match required supplied id: ${args.id}`)
162
- }
163
- if (!type || !(type.includes(requestedType) || type.includes(requestedType + 'Credential'))) {
164
- throw Error(`Credential type ${JSON.stringify(type)} does not contain requested type ${requestedType}`)
165
- }
166
- // @ts-ignore
167
- const encodedList = getAssertedValue('encodedList', credentialSubject['encodedList'])
168
-
169
- const statusList = await StatusList.decode({ encodedList })
170
- const status = statusList.getStatus(typeof args.statusListIndex === 'number' ? args.statusListIndex : Number.parseInt(args.statusListIndex))
171
- return status
142
+ }): Promise<number | Status2021 | StatusOAuth> {
143
+ const statusListType: StatusListType = determineStatusListType(args.statusListCredential)
144
+ const implementation = getStatusListImplementation(statusListType)
145
+ return implementation.checkStatusIndex(args)
172
146
  }
173
147
 
174
148
  export async function createNewStatusList(
175
149
  args: CreateNewStatusListFuncArgs,
176
150
  context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
177
151
  ): Promise<StatusListResult> {
178
- const length = args?.length ?? 250000
179
- const proofFormat = args?.proofFormat ?? 'lds'
180
- const { issuer, type, id } = getAssertedValues(args)
181
- const correlationId = getAssertedValue('correlationId', args.correlationId)
182
-
183
- const list = new StatusList({ length })
184
- const encodedList = await list.encode()
185
- const statusPurpose = args.statusPurpose ?? 'revocation'
186
- const statusListCredential = await statusList2021ToVerifiableCredential(
187
- {
188
- ...args,
189
- type,
190
- proofFormat,
191
- encodedList,
192
- },
193
- context,
194
- )
195
-
196
- return {
197
- encodedList,
198
- statusListCredential,
199
- length,
200
- type,
201
- proofFormat,
202
- id,
203
- correlationId,
204
- issuer,
205
- statusPurpose,
206
- indexingDirection: 'rightToLeft',
207
- } as StatusListResult
152
+ const { type } = getAssertedValues(args)
153
+ const implementation = getStatusListImplementation(type)
154
+ return implementation.createNewStatusList(args, context)
208
155
  }
209
156
 
210
157
  export async function updateStatusIndexFromStatusListCredential(
211
158
  args: UpdateStatusListFromStatusListCredentialArgs,
212
159
  context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
213
- ): Promise<StatusListDetails> {
214
- return updateStatusListIndexFromEncodedList(
215
- {
216
- ...(await statusListCredentialToDetails(args)),
217
- statusListIndex: args.statusListIndex,
218
- value: args.value,
219
- },
220
- context,
221
- )
160
+ ): Promise<StatusListResult> {
161
+ const credential = getAssertedValue('statusListCredential', args.statusListCredential)
162
+ const statusListType: StatusListType = determineStatusListType(credential)
163
+ const implementation = getStatusListImplementation(statusListType)
164
+ return implementation.updateStatusListIndex(args, context)
222
165
  }
223
166
 
167
+ // Keeping helper function for backward compatibility
224
168
  export async function statusListCredentialToDetails(args: {
225
- statusListCredential: OriginalVerifiableCredential
169
+ statusListCredential: StatusListCredential
226
170
  correlationId?: string
227
171
  driverType?: StatusListDriverType
228
- }): Promise<StatusListDetails> {
172
+ }): Promise<StatusListResult> {
229
173
  const credential = getAssertedValue('statusListCredential', args.statusListCredential)
230
174
  const uniform = CredentialMapper.toUniformCredential(credential)
231
- const { issuer, type, credentialSubject } = uniform
232
- if (!type.includes('StatusList2021Credential')) {
233
- throw Error('StatusList2021Credential type should be present in the Verifiable Credential')
234
- }
235
- const id = getAssertedValue('id', uniform.id)
236
- // @ts-ignore
237
- const { encodedList, statusPurpose } = credentialSubject
238
- const proofFormat: ProofFormat = CredentialMapper.detectDocumentType(credential) === DocumentFormat.JWT ? 'jwt' : 'lds'
239
- return {
240
- id,
241
- encodedList,
242
- issuer,
243
- type: StatusListType.StatusList2021,
244
- proofFormat,
245
- indexingDirection: 'rightToLeft',
246
- length: (await StatusList.decode({ encodedList })).length,
247
- statusPurpose,
248
- statusListCredential: credential,
249
- ...(args.correlationId && { correlationId: args.correlationId }),
250
- ...(args.driverType && { driverType: args.driverType }),
175
+ const type = uniform.type.find((t) => t.includes('StatusList2021') || t.includes('OAuth2StatusList'))
176
+ if (!type) {
177
+ throw new Error('Invalid status list credential type')
251
178
  }
179
+ const statusListType = type.replace('Credential', '') as StatusListType
180
+ const implementation = getStatusListImplementation(statusListType)
181
+ return implementation.updateStatusListIndex(
182
+ {
183
+ statusListCredential: args.statusListCredential,
184
+ statusListIndex: 0,
185
+ value: 0,
186
+ },
187
+ {} as IAgentContext<ICredentialPlugin & IIdentifierResolution>,
188
+ )
252
189
  }
253
190
 
254
191
  export async function updateStatusListIndexFromEncodedList(
255
192
  args: UpdateStatusListFromEncodedListArgs,
256
193
  context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
257
- ): Promise<StatusListDetails> {
258
- const { issuer, type, id } = getAssertedValues(args)
259
- const proofFormat = args?.proofFormat ?? 'lds'
260
- const origEncodedList = getAssertedValue('encodedList', args.encodedList)
261
- const index = getAssertedValue('index', typeof args.statusListIndex === 'number' ? args.statusListIndex : Number.parseInt(args.statusListIndex))
262
- const value = getAssertedValue('value', args.value)
263
- const statusPurpose = getAssertedValue('statusPurpose', args.statusPurpose)
264
-
265
- const statusList = await StatusList.decode({ encodedList: origEncodedList })
266
- statusList.setStatus(index, value)
267
- const encodedList = await statusList.encode()
268
- const statusListCredential = await statusList2021ToVerifiableCredential(
269
- {
270
- ...args,
271
- type,
272
- proofFormat,
273
- encodedList,
274
- },
275
- context,
276
- )
277
- return {
278
- encodedList,
279
- statusListCredential,
280
- length: statusList.length - 1,
281
- type,
282
- proofFormat,
283
- id,
284
- issuer,
285
- statusPurpose,
286
- indexingDirection: 'rightToLeft',
287
- }
194
+ ): Promise<StatusListResult> {
195
+ const { type } = getAssertedValue('type', args)
196
+ const implementation = getStatusListImplementation(type!)
197
+ return implementation.updateStatusListFromEncodedList(args, context)
288
198
  }
289
199
 
200
+ // TODO Is this still in use? Or do we need to redesign this after having multiple status list types?
290
201
  export async function statusList2021ToVerifiableCredential(
291
202
  args: StatusList2021ToVerifiableCredentialArgs,
292
203
  context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
293
- ): Promise<OriginalVerifiableCredential> {
204
+ ): Promise<StatusListCredential> {
294
205
  const { issuer, id, type } = getAssertedValues(args)
295
206
  const identifier = await context.agent.identifierManagedGet({
296
207
  identifier: typeof issuer === 'string' ? issuer : issuer.id,
297
208
  vmRelationship: 'assertionMethod',
298
209
  offlineWhenNoDIDRegistered: true, // FIXME Fix identifier resolution for EBSI
299
210
  })
211
+ const proofFormat: ProofFormat = args?.proofFormat ?? 'lds'
212
+ assertValidProofType(StatusListType.StatusList2021, proofFormat)
213
+ const vmoProofFormat: VmoProofFormat = proofFormat as VmoProofFormat
214
+
300
215
  const encodedList = getAssertedValue('encodedList', args.encodedList)
301
216
  const statusPurpose = getAssertedValue('statusPurpose', args.statusPurpose)
302
217
  const credential = {
@@ -316,31 +231,9 @@ export async function statusList2021ToVerifiableCredential(
316
231
  const verifiableCredential = await context.agent.createVerifiableCredential({
317
232
  credential,
318
233
  keyRef: identifier.kmsKeyRef,
319
- proofFormat: args.proofFormat ?? 'lds',
234
+ proofFormat: vmoProofFormat,
320
235
  fetchRemoteContexts: true,
321
236
  })
322
237
 
323
- return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as OriginalVerifiableCredential).original
324
- }
325
-
326
- function getAssertedStatusListType(type?: StatusListType) {
327
- const assertedType = type ?? StatusListType.StatusList2021
328
- if (assertedType !== StatusListType.StatusList2021) {
329
- throw Error(`StatusList type ${assertedType} is not supported (yet)`)
330
- }
331
- return assertedType
332
- }
333
-
334
- function getAssertedValue<T>(name: string, value: T): NonNullable<T> {
335
- if (value === undefined || value === null) {
336
- throw Error(`Missing required ${name} value`)
337
- }
338
- return value
339
- }
340
-
341
- function getAssertedValues(args: { issuer: string | IIssuer; id: string; type?: StatusListType }) {
342
- const type = getAssertedStatusListType(args?.type)
343
- const id = getAssertedValue('id', args.id)
344
- const issuer = getAssertedValue('issuer', args.issuer)
345
- return { id, issuer, type }
238
+ return CredentialMapper.toWrappedVerifiableCredential(verifiableCredential as StatusListCredential).original as StatusListCredential
346
239
  }
@@ -0,0 +1,36 @@
1
+ import { IAgentContext, ICredentialPlugin } from '@veramo/core'
2
+ import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
3
+ import {
4
+ CheckStatusIndexArgs,
5
+ CreateStatusListArgs,
6
+ Status2021,
7
+ StatusListResult,
8
+ StatusOAuth,
9
+ UpdateStatusListFromEncodedListArgs,
10
+ UpdateStatusListIndexArgs,
11
+ } from '../types'
12
+
13
+ export interface IStatusList {
14
+ /**
15
+ * Creates a new status list of the specific type
16
+ */
17
+ createNewStatusList(args: CreateStatusListArgs, context: IAgentContext<ICredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
18
+
19
+ /**
20
+ * Updates a status at the given index in the status list
21
+ */
22
+ updateStatusListIndex(args: UpdateStatusListIndexArgs, context: IAgentContext<ICredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
23
+
24
+ /**
25
+ * Updates a status list using a base64 encoded list of statuses
26
+ */
27
+ updateStatusListFromEncodedList(
28
+ args: UpdateStatusListFromEncodedListArgs,
29
+ context: IAgentContext<ICredentialPlugin & IIdentifierResolution>,
30
+ ): Promise<StatusListResult>
31
+
32
+ /**
33
+ * Checks the status at a given index in the status list
34
+ */
35
+ checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth>
36
+ }
@@ -0,0 +1,165 @@
1
+ import { IAgentContext, ICredentialPlugin } from '@veramo/core'
2
+ import { ProofFormat, StatusListType } from '@sphereon/ssi-types'
3
+ import {
4
+ CheckStatusIndexArgs,
5
+ CreateStatusListArgs,
6
+ SignedStatusListData,
7
+ StatusListResult,
8
+ StatusOAuth,
9
+ UpdateStatusListFromEncodedListArgs,
10
+ UpdateStatusListIndexArgs,
11
+ } from '../types'
12
+ import { determineProofFormat, getAssertedValue, getAssertedValues } from '../utils'
13
+ import { IStatusList } from './IStatusList'
14
+ import { StatusList, StatusListJWTHeaderParameters } from '@sd-jwt/jwt-status-list'
15
+ import { IJwtService } from '@sphereon/ssi-sdk-ext.jwt-service'
16
+ import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
17
+ import { createSignedJwt, decodeStatusListJWT } from './encoding/jwt'
18
+ import { createSignedCbor, decodeStatusListCWT } from './encoding/cbor'
19
+
20
+ type IRequiredContext = IAgentContext<ICredentialPlugin & IJwtService & IIdentifierResolution>
21
+
22
+ export const BITS_PER_STATUS_DEFAULT = 2 // 2 bits are sufficient for 0x00 - "VALID" 0x01 - "INVALID" & 0x02 - "SUSPENDED"
23
+ export const DEFAULT_LIST_LENGTH = 250000
24
+ export const DEFAULT_PROOF_FORMAT = 'jwt' as ProofFormat
25
+ export const STATUS_LIST_JWT_HEADER: StatusListJWTHeaderParameters = {
26
+ alg: 'EdDSA',
27
+ typ: 'statuslist+jwt',
28
+ }
29
+
30
+ export class OAuthStatusListImplementation implements IStatusList {
31
+ async createNewStatusList(args: CreateStatusListArgs, context: IRequiredContext): Promise<StatusListResult> {
32
+ if (!args.oauthStatusList) {
33
+ throw new Error('OAuthStatusList options are required for type OAuthStatusList')
34
+ }
35
+
36
+ const proofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
37
+ const { issuer, id, keyRef } = args
38
+ const length = args.length ?? DEFAULT_LIST_LENGTH
39
+ const bitsPerStatus = args.oauthStatusList.bitsPerStatus ?? BITS_PER_STATUS_DEFAULT
40
+ const issuerString = typeof issuer === 'string' ? issuer : issuer.id
41
+ const correlationId = getAssertedValue('correlationId', args.correlationId)
42
+
43
+ const statusList = new StatusList(new Array(length).fill(0), bitsPerStatus)
44
+ const encodedList = statusList.compressStatusList()
45
+ const { statusListCredential } = await this.createSignedStatusList(proofFormat, context, statusList, issuerString, id, keyRef)
46
+
47
+ return {
48
+ encodedList,
49
+ statusListCredential,
50
+ oauthStatusList: { bitsPerStatus },
51
+ length,
52
+ type: StatusListType.OAuthStatusList,
53
+ proofFormat,
54
+ id,
55
+ correlationId,
56
+ issuer,
57
+ }
58
+ }
59
+
60
+ async updateStatusListIndex(args: UpdateStatusListIndexArgs, context: IRequiredContext): Promise<StatusListResult> {
61
+ const { statusListCredential, value, keyRef } = args
62
+ if (typeof statusListCredential !== 'string') {
63
+ return Promise.reject('statusListCredential in neither JWT nor CWT')
64
+ }
65
+
66
+ const proofFormat = determineProofFormat(statusListCredential)
67
+ const decoded = proofFormat === 'jwt' ? decodeStatusListJWT(statusListCredential) : decodeStatusListCWT(statusListCredential)
68
+ const { statusList, issuer, id } = decoded
69
+
70
+ const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
71
+ if (index < 0 || index >= statusList.statusList.length) {
72
+ throw new Error('Status list index out of bounds')
73
+ }
74
+
75
+ statusList.setStatus(index, value)
76
+ const { statusListCredential: signedCredential, encodedList } = await this.createSignedStatusList(
77
+ proofFormat,
78
+ context,
79
+ statusList,
80
+ issuer,
81
+ id,
82
+ keyRef,
83
+ )
84
+
85
+ return {
86
+ statusListCredential: signedCredential,
87
+ encodedList,
88
+ oauthStatusList: {
89
+ bitsPerStatus: statusList.getBitsPerStatus(),
90
+ },
91
+ length: statusList.statusList.length,
92
+ type: StatusListType.OAuthStatusList,
93
+ proofFormat,
94
+ id,
95
+ issuer,
96
+ }
97
+ }
98
+
99
+ async updateStatusListFromEncodedList(args: UpdateStatusListFromEncodedListArgs, context: IRequiredContext): Promise<StatusListResult> {
100
+ if (!args.oauthStatusList) {
101
+ throw new Error('OAuthStatusList options are required for type OAuthStatusList')
102
+ }
103
+
104
+ const proofFormat = args.proofFormat ?? DEFAULT_PROOF_FORMAT
105
+ const { issuer, id } = getAssertedValues(args)
106
+ const bitsPerStatus = args.oauthStatusList.bitsPerStatus ?? BITS_PER_STATUS_DEFAULT
107
+ const issuerString = typeof issuer === 'string' ? issuer : issuer.id
108
+
109
+ const listToUpdate = StatusList.decompressStatusList(args.encodedList, bitsPerStatus)
110
+ const index = typeof args.statusListIndex === 'number' ? args.statusListIndex : parseInt(args.statusListIndex)
111
+ listToUpdate.setStatus(index, args.value ? 1 : 0)
112
+
113
+ const { statusListCredential, encodedList } = await this.createSignedStatusList(proofFormat, context, listToUpdate, issuerString, id, args.keyRef)
114
+
115
+ return {
116
+ encodedList,
117
+ statusListCredential,
118
+ oauthStatusList: {
119
+ bitsPerStatus,
120
+ },
121
+ length: listToUpdate.statusList.length,
122
+ type: StatusListType.OAuthStatusList,
123
+ proofFormat,
124
+ id,
125
+ issuer,
126
+ }
127
+ }
128
+
129
+ async checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | StatusOAuth> {
130
+ const { statusListCredential, statusListIndex } = args
131
+ if (typeof statusListCredential !== 'string') {
132
+ return Promise.reject('statusListCredential in neither JWT nor CWT')
133
+ }
134
+
135
+ const proofFormat = determineProofFormat(statusListCredential)
136
+ const { statusList } = proofFormat === 'jwt' ? decodeStatusListJWT(statusListCredential) : decodeStatusListCWT(statusListCredential)
137
+
138
+ const index = typeof statusListIndex === 'number' ? statusListIndex : parseInt(statusListIndex)
139
+ if (index < 0 || index >= statusList.statusList.length) {
140
+ throw new Error('Status list index out of bounds')
141
+ }
142
+
143
+ return statusList.getStatus(index)
144
+ }
145
+
146
+ private async createSignedStatusList(
147
+ proofFormat: 'jwt' | 'lds' | 'EthereumEip712Signature2021' | 'cbor',
148
+ context: IAgentContext<ICredentialPlugin & IJwtService & IIdentifierResolution>,
149
+ statusList: StatusList,
150
+ issuerString: string,
151
+ id: string,
152
+ keyRef?: string,
153
+ ): Promise<SignedStatusListData> {
154
+ switch (proofFormat) {
155
+ case 'jwt': {
156
+ return await createSignedJwt(context, statusList, issuerString, id, keyRef)
157
+ }
158
+ case 'cbor': {
159
+ return await createSignedCbor(context, statusList, issuerString, id, keyRef)
160
+ }
161
+ default:
162
+ throw new Error(`Invalid proof format '${proofFormat}' for OAuthStatusList`)
163
+ }
164
+ }
165
+ }