@sphereon/ssi-sdk.vc-status-list 0.34.1-feature.SSISDK.17.bitstring.sl.11 → 0.34.1-feature.SSISDK.17.bitstring.sl.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +395 -216
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -41
- package/dist/index.d.ts +75 -41
- package/dist/index.js +396 -217
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/src/functions.ts +50 -62
- package/src/impl/BitstringStatusListImplementation.ts +265 -121
- package/src/impl/IStatusList.ts +43 -11
- package/src/impl/OAuthStatusList.ts +81 -33
- package/src/impl/StatusList2021.ts +79 -39
- package/src/index.ts +1 -0
- package/src/types/index.ts +22 -36
- package/src/utils.ts +48 -19
package/src/impl/IStatusList.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import type { IAgentContext
|
|
1
|
+
import type { IAgentContext } from '@veramo/core'
|
|
2
2
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
3
3
|
import {
|
|
4
|
-
BitstringStatus,
|
|
5
|
-
BitstringStatusListEntryCredentialStatus,
|
|
6
4
|
CheckStatusIndexArgs,
|
|
7
5
|
CreateStatusListArgs,
|
|
6
|
+
IMergeDetailsWithEntityArgs,
|
|
7
|
+
IToDetailsFromCredentialArgs,
|
|
8
8
|
Status2021,
|
|
9
9
|
StatusList2021EntryCredentialStatus,
|
|
10
10
|
StatusListOAuthEntryCredentialStatus,
|
|
11
11
|
StatusListResult,
|
|
12
12
|
StatusOAuth,
|
|
13
|
-
ToStatusListDetailsArgs,
|
|
14
13
|
UpdateStatusListFromEncodedListArgs,
|
|
15
14
|
UpdateStatusListIndexArgs,
|
|
16
15
|
} from '../types'
|
|
@@ -24,37 +23,70 @@ import {
|
|
|
24
23
|
StatusListType,
|
|
25
24
|
StatusPurpose2021,
|
|
26
25
|
} from '@sphereon/ssi-types'
|
|
27
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
BitstringStatusListEntryCredentialStatus,
|
|
28
|
+
IBitstringStatusListEntryEntity,
|
|
29
|
+
IStatusListEntryEntity,
|
|
30
|
+
StatusListEntity,
|
|
31
|
+
} from '@sphereon/ssi-sdk.data-store'
|
|
32
|
+
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
33
|
+
import { DecodedStatusListPayload } from './encoding/common'
|
|
34
|
+
|
|
35
|
+
export interface IExtractedCredentialDetails {
|
|
36
|
+
id: string
|
|
37
|
+
issuer: string | IIssuer
|
|
38
|
+
encodedList: string
|
|
39
|
+
decodedPayload?: DecodedStatusListPayload
|
|
40
|
+
}
|
|
28
41
|
|
|
29
42
|
export interface IStatusList {
|
|
30
43
|
/**
|
|
31
44
|
* Creates a new status list of the specific type
|
|
32
45
|
*/
|
|
33
|
-
createNewStatusList(args: CreateStatusListArgs, context: IAgentContext<
|
|
46
|
+
createNewStatusList(args: CreateStatusListArgs, context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>): Promise<StatusListResult>
|
|
34
47
|
|
|
35
48
|
/**
|
|
36
49
|
* Updates a status at the given index in the status list
|
|
37
50
|
*/
|
|
38
|
-
updateStatusListIndex(
|
|
51
|
+
updateStatusListIndex(
|
|
52
|
+
args: UpdateStatusListIndexArgs,
|
|
53
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
54
|
+
): Promise<StatusListResult>
|
|
39
55
|
|
|
40
56
|
/**
|
|
41
57
|
* Updates a status list using a base64 encoded list of statuses
|
|
42
58
|
*/
|
|
43
59
|
updateStatusListFromEncodedList(
|
|
44
60
|
args: UpdateStatusListFromEncodedListArgs,
|
|
45
|
-
context: IAgentContext<
|
|
61
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
46
62
|
): Promise<StatusListResult>
|
|
47
63
|
|
|
48
64
|
/**
|
|
49
65
|
* Checks the status at a given index in the status list
|
|
50
66
|
*/
|
|
51
|
-
checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth
|
|
67
|
+
checkStatusIndex(args: CheckStatusIndexArgs): Promise<number | Status2021 | StatusOAuth>
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Performs the initial parsing of a StatusListCredential.
|
|
71
|
+
* This method handles expensive operations like JWT/CWT decoding once.
|
|
72
|
+
* It extracts all details available from the credential payload itself.
|
|
73
|
+
*/
|
|
74
|
+
extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails>
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Converts a credential and its known metadata into a full StatusListResult.
|
|
78
|
+
*/
|
|
79
|
+
toStatusListDetails(
|
|
80
|
+
args: IToDetailsFromCredentialArgs,
|
|
81
|
+
): Promise<
|
|
82
|
+
StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
|
|
83
|
+
>
|
|
52
84
|
|
|
53
85
|
/**
|
|
54
|
-
*
|
|
86
|
+
* Merges pre-parsed details from a new credential with an existing database entity.
|
|
55
87
|
*/
|
|
56
88
|
toStatusListDetails(
|
|
57
|
-
args:
|
|
89
|
+
args: IMergeDetailsWithEntityArgs,
|
|
58
90
|
): Promise<
|
|
59
91
|
StatusListResult & (IStatusList2021ImplementationResult | IOAuthStatusListImplementationResult | IBitstringStatusListImplementationResult)
|
|
60
92
|
>
|
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
import type { IAgentContext,
|
|
1
|
+
import type { IAgentContext, IKeyManager } from '@veramo/core'
|
|
2
2
|
import { type CompactJWT, type CredentialProofFormat, type CWT, StatusListType } from '@sphereon/ssi-types'
|
|
3
3
|
import type {
|
|
4
4
|
CheckStatusIndexArgs,
|
|
5
5
|
CreateStatusListArgs,
|
|
6
|
+
IMergeDetailsWithEntityArgs,
|
|
7
|
+
IToDetailsFromCredentialArgs,
|
|
6
8
|
SignedStatusListData,
|
|
7
9
|
StatusListOAuthEntryCredentialStatus,
|
|
8
10
|
StatusListResult,
|
|
9
11
|
StatusOAuth,
|
|
10
|
-
ToStatusListDetailsArgs,
|
|
11
12
|
UpdateStatusListFromEncodedListArgs,
|
|
12
13
|
UpdateStatusListIndexArgs,
|
|
13
14
|
} from '../types'
|
|
14
15
|
import { determineProofFormat, ensureDate, getAssertedValue, getAssertedValues } from '../utils'
|
|
15
|
-
import type { IOAuthStatusListImplementationResult, IStatusList } from './IStatusList'
|
|
16
|
+
import type { IExtractedCredentialDetails, IOAuthStatusListImplementationResult, IStatusList } from './IStatusList'
|
|
16
17
|
import { StatusList } from '@sd-jwt/jwt-status-list'
|
|
17
18
|
import type { IJwtService } from '@sphereon/ssi-sdk-ext.jwt-service'
|
|
18
19
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
19
20
|
import { createSignedJwt, decodeStatusListJWT } from './encoding/jwt'
|
|
20
21
|
import { createSignedCbor, decodeStatusListCWT } from './encoding/cbor'
|
|
21
22
|
import { IBitstringStatusListEntryEntity, IStatusListEntryEntity, OAuthStatusListEntity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
23
|
+
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
22
24
|
|
|
23
|
-
type IRequiredContext = IAgentContext<
|
|
25
|
+
type IRequiredContext = IAgentContext<IVcdmCredentialPlugin & IJwtService & IIdentifierResolution & IKeyManager>
|
|
24
26
|
|
|
25
27
|
export const DEFAULT_BITS_PER_STATUS = 1 // 1 bit is sufficient for 0x00 - "VALID" 0x01 - "INVALID" saving space in the process
|
|
26
28
|
export const DEFAULT_LIST_LENGTH = 250000
|
|
@@ -163,41 +165,87 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
163
165
|
return statusList.getStatus(index)
|
|
164
166
|
}
|
|
165
167
|
|
|
166
|
-
async
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const { statusList, issuer, id, exp } = decoded
|
|
168
|
+
async extractCredentialDetails(credential: CompactJWT | CWT): Promise<IExtractedCredentialDetails> {
|
|
169
|
+
if (typeof credential !== 'string') {
|
|
170
|
+
return Promise.reject('statusListCredential must be a JWT or CWT string')
|
|
171
|
+
}
|
|
171
172
|
|
|
172
|
-
const
|
|
173
|
-
const
|
|
173
|
+
const proofFormat = determineProofFormat(credential)
|
|
174
|
+
const decoded = proofFormat === 'jwt' ? decodeStatusListJWT(credential) : decodeStatusListCWT(credential)
|
|
174
175
|
|
|
175
176
|
return {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
encodedList: statusList.compressStatusList(),
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
length: statusList.statusList.length,
|
|
183
|
-
statusListCredential: statusListPayload,
|
|
184
|
-
statuslistContentType: this.buildContentType(proofFormat),
|
|
185
|
-
correlationId: args.correlationId, // FIXME these do not need to be inside the impl
|
|
186
|
-
driverType: args.driverType, // FIXME these do not need to be inside the impl
|
|
177
|
+
id: decoded.id,
|
|
178
|
+
issuer: decoded.issuer,
|
|
179
|
+
encodedList: decoded.statusList.compressStatusList(),
|
|
180
|
+
decodedPayload: decoded,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
187
183
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
// For CREATE and READ contexts
|
|
185
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult>
|
|
186
|
+
// For UPDATE contexts
|
|
187
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IOAuthStatusListImplementationResult>
|
|
188
|
+
async toStatusListDetails(
|
|
189
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
190
|
+
): Promise<StatusListResult & IOAuthStatusListImplementationResult> {
|
|
191
|
+
if ('statusListCredential' in args) {
|
|
192
|
+
// CREATE/READ context
|
|
193
|
+
const { statusListCredential, bitsPerStatus, correlationId, driverType } = args
|
|
194
|
+
if (!bitsPerStatus || bitsPerStatus < 1) {
|
|
195
|
+
return Promise.reject(Error('bitsPerStatus must be set for OAuth status lists and must be 1 or higher'))
|
|
196
|
+
}
|
|
191
197
|
|
|
192
|
-
|
|
193
|
-
|
|
198
|
+
const proofFormat = determineProofFormat(statusListCredential as string)
|
|
199
|
+
const decoded =
|
|
200
|
+
proofFormat === 'jwt' ? decodeStatusListJWT(statusListCredential as string) : decodeStatusListCWT(statusListCredential as string)
|
|
201
|
+
const { statusList, issuer, id, exp } = decoded
|
|
202
|
+
const expiresAt = exp ? new Date(exp * 1000) : undefined
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
id,
|
|
206
|
+
encodedList: statusList.compressStatusList(),
|
|
207
|
+
issuer,
|
|
208
|
+
type: StatusListType.OAuthStatusList,
|
|
209
|
+
proofFormat,
|
|
210
|
+
length: statusList.statusList.length,
|
|
211
|
+
statusListCredential: statusListCredential as CompactJWT | CWT,
|
|
212
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
213
|
+
correlationId,
|
|
214
|
+
driverType,
|
|
194
215
|
bitsPerStatus,
|
|
195
216
|
...(expiresAt && { expiresAt }),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
217
|
+
oauthStatusList: {
|
|
218
|
+
bitsPerStatus,
|
|
219
|
+
...(expiresAt && { expiresAt }),
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
// UPDATE context
|
|
224
|
+
const { extractedDetails, statusListEntity } = args
|
|
225
|
+
const oauthEntity = statusListEntity as OAuthStatusListEntity
|
|
226
|
+
const decoded = extractedDetails.decodedPayload as { statusList: StatusList; exp?: number }
|
|
227
|
+
|
|
228
|
+
const proofFormat = determineProofFormat(statusListEntity.statusListCredential as string)
|
|
229
|
+
const expiresAt = decoded.exp ? new Date(decoded.exp * 1000) : undefined
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
id: extractedDetails.id,
|
|
233
|
+
encodedList: extractedDetails.encodedList,
|
|
234
|
+
issuer: extractedDetails.issuer,
|
|
235
|
+
type: StatusListType.OAuthStatusList,
|
|
236
|
+
proofFormat,
|
|
237
|
+
length: decoded.statusList.statusList.length,
|
|
238
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
239
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
240
|
+
correlationId: statusListEntity.correlationId,
|
|
241
|
+
driverType: statusListEntity.driverType,
|
|
242
|
+
bitsPerStatus: oauthEntity.bitsPerStatus,
|
|
243
|
+
...(expiresAt && { expiresAt }),
|
|
244
|
+
oauthStatusList: {
|
|
245
|
+
bitsPerStatus: oauthEntity.bitsPerStatus,
|
|
246
|
+
...(expiresAt && { expiresAt }),
|
|
247
|
+
},
|
|
248
|
+
}
|
|
201
249
|
}
|
|
202
250
|
}
|
|
203
251
|
|
|
@@ -227,7 +275,7 @@ export class OAuthStatusListImplementation implements IStatusList {
|
|
|
227
275
|
|
|
228
276
|
private async createSignedStatusList(
|
|
229
277
|
proofFormat: CredentialProofFormat,
|
|
230
|
-
context: IAgentContext<
|
|
278
|
+
context: IAgentContext<IVcdmCredentialPlugin & IJwtService & IIdentifierResolution & IKeyManager>,
|
|
231
279
|
statusList: StatusList,
|
|
232
280
|
issuerString: string,
|
|
233
281
|
id: string,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IAgentContext,
|
|
1
|
+
import type { IAgentContext, ProofFormat as VeramoProofFormat } from '@veramo/core'
|
|
2
2
|
import type { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
3
3
|
import {
|
|
4
4
|
CredentialMapper,
|
|
@@ -10,18 +10,20 @@ import {
|
|
|
10
10
|
} from '@sphereon/ssi-types'
|
|
11
11
|
|
|
12
12
|
import { StatusList } from '@sphereon/vc-status-list'
|
|
13
|
-
import type { IStatusList, IStatusList2021ImplementationResult } from './IStatusList'
|
|
13
|
+
import type { IExtractedCredentialDetails, IStatusList, IStatusList2021ImplementationResult } from './IStatusList'
|
|
14
14
|
import type {
|
|
15
15
|
CheckStatusIndexArgs,
|
|
16
16
|
CreateStatusListArgs,
|
|
17
|
+
IMergeDetailsWithEntityArgs,
|
|
18
|
+
IToDetailsFromCredentialArgs,
|
|
17
19
|
StatusListResult,
|
|
18
|
-
ToStatusListDetailsArgs,
|
|
19
20
|
UpdateStatusListFromEncodedListArgs,
|
|
20
21
|
UpdateStatusListIndexArgs,
|
|
21
22
|
} from '../types'
|
|
22
23
|
import { Status2021, StatusList2021EntryCredentialStatus } from '../types'
|
|
23
24
|
import { assertValidProofType, getAssertedProperty, getAssertedValue, getAssertedValues } from '../utils'
|
|
24
25
|
import { IBitstringStatusListEntryEntity, IStatusListEntryEntity, StatusList2021Entity, StatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
26
|
+
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
25
27
|
|
|
26
28
|
export const DEFAULT_LIST_LENGTH = 250000
|
|
27
29
|
export const DEFAULT_PROOF_FORMAT = 'lds' as CredentialProofFormat
|
|
@@ -29,7 +31,7 @@ export const DEFAULT_PROOF_FORMAT = 'lds' as CredentialProofFormat
|
|
|
29
31
|
export class StatusList2021Implementation implements IStatusList {
|
|
30
32
|
async createNewStatusList(
|
|
31
33
|
args: CreateStatusListArgs,
|
|
32
|
-
context: IAgentContext<
|
|
34
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
33
35
|
): Promise<StatusListResult> {
|
|
34
36
|
const length = args?.length ?? DEFAULT_LIST_LENGTH
|
|
35
37
|
const proofFormat: CredentialProofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT
|
|
@@ -71,7 +73,7 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
71
73
|
|
|
72
74
|
async updateStatusListIndex(
|
|
73
75
|
args: UpdateStatusListIndexArgs,
|
|
74
|
-
context: IAgentContext<
|
|
76
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
75
77
|
): Promise<StatusListResult> {
|
|
76
78
|
const credential = args.statusListCredential
|
|
77
79
|
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
@@ -118,7 +120,7 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
118
120
|
|
|
119
121
|
async updateStatusListFromEncodedList(
|
|
120
122
|
args: UpdateStatusListFromEncodedListArgs,
|
|
121
|
-
context: IAgentContext<
|
|
123
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
122
124
|
): Promise<StatusListResult> {
|
|
123
125
|
if (!args.statusList2021) {
|
|
124
126
|
throw new Error('statusList2021 options required for type StatusList2021')
|
|
@@ -170,44 +172,82 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
170
172
|
return status ? Status2021.Invalid : Status2021.Valid
|
|
171
173
|
}
|
|
172
174
|
|
|
173
|
-
async
|
|
174
|
-
const
|
|
175
|
-
const uniform = CredentialMapper.toUniformCredential(statusListPayload)
|
|
175
|
+
async extractCredentialDetails(credential: StatusListCredential): Promise<IExtractedCredentialDetails> {
|
|
176
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
176
177
|
const { issuer, credentialSubject } = uniform
|
|
177
|
-
const
|
|
178
|
-
const encodedList = getAssertedProperty('encodedList', credentialSubject)
|
|
179
|
-
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListPayload) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
180
|
-
|
|
181
|
-
const statusPurpose = getAssertedProperty('statusPurpose', credentialSubject)
|
|
182
|
-
const indexingDirection = 'rightToLeft'
|
|
183
|
-
const list = await StatusList.decode({ encodedList })
|
|
178
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
184
179
|
|
|
185
180
|
return {
|
|
186
|
-
|
|
187
|
-
id,
|
|
188
|
-
encodedList,
|
|
181
|
+
id: getAssertedValue('id', uniform.id),
|
|
189
182
|
issuer,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
statusListCredential: statusListPayload,
|
|
194
|
-
statuslistContentType: this.buildContentType(proofFormat),
|
|
195
|
-
correlationId: args.correlationId, // FIXME these do not need to be inside the impl
|
|
196
|
-
driverType: args.driverType, // FIXME these do not need to be inside the impl
|
|
197
|
-
|
|
198
|
-
// Flattened StatusList2021-specific fields
|
|
199
|
-
indexingDirection,
|
|
200
|
-
statusPurpose,
|
|
183
|
+
encodedList: getAssertedProperty('encodedList', subject),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
201
186
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
187
|
+
async toStatusListDetails(args: IToDetailsFromCredentialArgs): Promise<StatusListResult & IStatusList2021ImplementationResult>
|
|
188
|
+
// For UPDATE contexts
|
|
189
|
+
async toStatusListDetails(args: IMergeDetailsWithEntityArgs): Promise<StatusListResult & IStatusList2021ImplementationResult>
|
|
190
|
+
async toStatusListDetails(
|
|
191
|
+
args: IToDetailsFromCredentialArgs | IMergeDetailsWithEntityArgs,
|
|
192
|
+
): Promise<StatusListResult & IStatusList2021ImplementationResult> {
|
|
193
|
+
if ('statusListCredential' in args) {
|
|
194
|
+
// CREATE/READ context
|
|
195
|
+
const { statusListCredential, correlationId, driverType } = args
|
|
196
|
+
const uniform = CredentialMapper.toUniformCredential(statusListCredential)
|
|
197
|
+
const { issuer, credentialSubject } = uniform
|
|
198
|
+
const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject
|
|
199
|
+
|
|
200
|
+
const id = getAssertedValue('id', uniform.id)
|
|
201
|
+
const encodedList = getAssertedProperty('encodedList', subject)
|
|
202
|
+
const statusPurpose = getAssertedProperty('statusPurpose', subject)
|
|
203
|
+
const proofFormat: CredentialProofFormat = CredentialMapper.detectDocumentType(statusListCredential) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
204
|
+
const list = await StatusList.decode({ encodedList })
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
id,
|
|
208
|
+
encodedList,
|
|
209
|
+
issuer,
|
|
210
|
+
type: StatusListType.StatusList2021,
|
|
211
|
+
proofFormat,
|
|
212
|
+
length: list.length,
|
|
213
|
+
statusListCredential,
|
|
214
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
215
|
+
correlationId,
|
|
216
|
+
driverType,
|
|
217
|
+
indexingDirection: 'rightToLeft',
|
|
205
218
|
statusPurpose,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
219
|
+
statusList2021: {
|
|
220
|
+
indexingDirection: 'rightToLeft',
|
|
221
|
+
statusPurpose,
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
// UPDATE context
|
|
226
|
+
const { extractedDetails, statusListEntity } = args
|
|
227
|
+
const statusList2021Entity = statusListEntity as StatusList2021Entity
|
|
228
|
+
|
|
229
|
+
const proofFormat: CredentialProofFormat =
|
|
230
|
+
CredentialMapper.detectDocumentType(statusListEntity.statusListCredential!) === DocumentFormat.JWT ? 'jwt' : 'lds'
|
|
231
|
+
const list = await StatusList.decode({ encodedList: extractedDetails.encodedList })
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
id: extractedDetails.id,
|
|
235
|
+
encodedList: extractedDetails.encodedList,
|
|
236
|
+
issuer: extractedDetails.issuer,
|
|
237
|
+
type: StatusListType.StatusList2021,
|
|
238
|
+
proofFormat,
|
|
239
|
+
length: list.length,
|
|
240
|
+
statusListCredential: statusListEntity.statusListCredential!,
|
|
241
|
+
statuslistContentType: this.buildContentType(proofFormat),
|
|
242
|
+
correlationId: statusListEntity.correlationId,
|
|
243
|
+
driverType: statusListEntity.driverType,
|
|
244
|
+
indexingDirection: statusList2021Entity.indexingDirection,
|
|
245
|
+
statusPurpose: statusList2021Entity.statusPurpose,
|
|
246
|
+
statusList2021: {
|
|
247
|
+
indexingDirection: statusList2021Entity.indexingDirection,
|
|
248
|
+
statusPurpose: statusList2021Entity.statusPurpose,
|
|
249
|
+
},
|
|
250
|
+
}
|
|
211
251
|
}
|
|
212
252
|
}
|
|
213
253
|
|
|
@@ -238,7 +278,7 @@ export class StatusList2021Implementation implements IStatusList {
|
|
|
238
278
|
proofFormat: VeramoProofFormat
|
|
239
279
|
keyRef?: string
|
|
240
280
|
},
|
|
241
|
-
context: IAgentContext<
|
|
281
|
+
context: IAgentContext<IVcdmCredentialPlugin & IIdentifierResolution>,
|
|
242
282
|
): Promise<StatusListCredential> {
|
|
243
283
|
const identifier = await context.agent.identifierManagedGet({
|
|
244
284
|
identifier: typeof args.issuer === 'string' ? args.issuer : args.issuer.id,
|
package/src/index.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -12,20 +12,15 @@ import {
|
|
|
12
12
|
StatusListType,
|
|
13
13
|
type StatusPurpose2021,
|
|
14
14
|
} from '@sphereon/ssi-types'
|
|
15
|
-
import type {
|
|
16
|
-
CredentialPayload,
|
|
17
|
-
IAgentContext,
|
|
18
|
-
ICredentialIssuer,
|
|
19
|
-
ICredentialPlugin,
|
|
20
|
-
ICredentialVerifier,
|
|
21
|
-
IKeyManager,
|
|
22
|
-
IPluginMethodMap,
|
|
23
|
-
} from '@veramo/core'
|
|
15
|
+
import type { CredentialPayload, IAgentContext, ICredentialIssuer, ICredentialVerifier, IKeyManager, IPluginMethodMap } from '@veramo/core'
|
|
24
16
|
import { DataSource } from 'typeorm'
|
|
25
17
|
import type { BitsPerStatus } from '@sd-jwt/jwt-status-list'
|
|
26
18
|
import type { SdJwtVcPayload } from '@sd-jwt/sd-jwt-vc'
|
|
27
19
|
import type { StatusListOpts } from '@sphereon/oid4vci-common'
|
|
28
20
|
import { BitstringStatusPurpose } from '@4sure-tech/vc-bitstring-status-lists'
|
|
21
|
+
import { IVcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
|
|
22
|
+
import { IExtractedCredentialDetails } from '../impl/IStatusList'
|
|
23
|
+
import { BitstringStatusListArgs, IStatusListEntity } from '@sphereon/ssi-sdk.data-store'
|
|
29
24
|
|
|
30
25
|
export enum StatusOAuth {
|
|
31
26
|
Valid = 0,
|
|
@@ -38,8 +33,6 @@ export enum Status2021 {
|
|
|
38
33
|
Invalid = 1,
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
export type BitstringStatus = number
|
|
42
|
-
|
|
43
36
|
export type StatusList2021Args = {
|
|
44
37
|
indexingDirection: StatusListIndexingDirection
|
|
45
38
|
statusPurpose?: StatusPurpose2021
|
|
@@ -51,14 +44,6 @@ export type OAuthStatusListArgs = {
|
|
|
51
44
|
expiresAt?: Date
|
|
52
45
|
}
|
|
53
46
|
|
|
54
|
-
export type BitstringStatusListArgs = {
|
|
55
|
-
statusPurpose: BitstringStatusPurpose
|
|
56
|
-
bitsPerStatus: number
|
|
57
|
-
ttl?: number
|
|
58
|
-
validFrom?: Date
|
|
59
|
-
validUntil?: Date
|
|
60
|
-
}
|
|
61
|
-
|
|
62
47
|
export type BaseCreateNewStatusListArgs = {
|
|
63
48
|
type: StatusListType
|
|
64
49
|
id: string
|
|
@@ -109,7 +94,7 @@ export interface UpdateStatusListFromStatusListCredentialArgs {
|
|
|
109
94
|
statusListCredential: StatusListCredential // | CompactJWT
|
|
110
95
|
keyRef?: string
|
|
111
96
|
statusListIndex: number | string
|
|
112
|
-
value: number | Status2021 | StatusOAuth
|
|
97
|
+
value: number | Status2021 | StatusOAuth
|
|
113
98
|
}
|
|
114
99
|
|
|
115
100
|
export interface StatusListResult {
|
|
@@ -170,16 +155,6 @@ export interface StatusListOAuthEntryCredentialStatus extends ICredentialStatus
|
|
|
170
155
|
expiresAt?: Date
|
|
171
156
|
}
|
|
172
157
|
|
|
173
|
-
export interface BitstringStatusListEntryCredentialStatus extends ICredentialStatus {
|
|
174
|
-
type: 'BitstringStatusListEntry'
|
|
175
|
-
statusPurpose: BitstringStatusPurpose | BitstringStatusPurpose[]
|
|
176
|
-
statusListIndex: string
|
|
177
|
-
statusListCredential: string
|
|
178
|
-
bitsPerStatus?: number
|
|
179
|
-
statusMessage?: Array<BitstringStatus>
|
|
180
|
-
statusReference?: string | string[]
|
|
181
|
-
}
|
|
182
|
-
|
|
183
158
|
export interface StatusList2021ToVerifiableCredentialArgs {
|
|
184
159
|
issuer: string | IIssuer
|
|
185
160
|
id: string
|
|
@@ -205,7 +180,7 @@ export interface CreateStatusListArgs {
|
|
|
205
180
|
export interface UpdateStatusListIndexArgs {
|
|
206
181
|
statusListCredential: StatusListCredential // | CompactJWT
|
|
207
182
|
statusListIndex: number | string
|
|
208
|
-
value: number | Status2021 | StatusOAuth
|
|
183
|
+
value: number | Status2021 | StatusOAuth
|
|
209
184
|
bitsPerStatus?: number
|
|
210
185
|
keyRef?: string
|
|
211
186
|
expiresAt?: Date
|
|
@@ -217,11 +192,22 @@ export interface CheckStatusIndexArgs {
|
|
|
217
192
|
bitsPerStatus?: number
|
|
218
193
|
}
|
|
219
194
|
|
|
220
|
-
|
|
221
|
-
|
|
195
|
+
// For the CREATE and READ contexts
|
|
196
|
+
export interface IToDetailsFromCredentialArgs {
|
|
197
|
+
// The source credential we are converting
|
|
198
|
+
statusListCredential: StatusListCredential
|
|
199
|
+
|
|
200
|
+
// The required metadata that is NOT in the credential itself
|
|
201
|
+
statusListType: StatusListType
|
|
202
|
+
bitsPerStatus?: number
|
|
222
203
|
correlationId?: string
|
|
223
204
|
driverType?: StatusListDriverType
|
|
224
|
-
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// For the UPDATE context
|
|
208
|
+
export interface IMergeDetailsWithEntityArgs {
|
|
209
|
+
extractedDetails: IExtractedCredentialDetails
|
|
210
|
+
statusListEntity: IStatusListEntity
|
|
225
211
|
}
|
|
226
212
|
|
|
227
213
|
/**
|
|
@@ -309,5 +295,5 @@ export type SignedStatusListData = {
|
|
|
309
295
|
encodedList: string
|
|
310
296
|
}
|
|
311
297
|
|
|
312
|
-
export type IRequiredPlugins =
|
|
313
|
-
export type IRequiredContext = IAgentContext<ICredentialIssuer & ICredentialVerifier & IIdentifierResolution & IKeyManager &
|
|
298
|
+
export type IRequiredPlugins = IVcdmCredentialPlugin & IIdentifierResolution
|
|
299
|
+
export type IRequiredContext = IAgentContext<ICredentialIssuer & ICredentialVerifier & IIdentifierResolution & IKeyManager & IVcdmCredentialPlugin>
|
package/src/utils.ts
CHANGED
|
@@ -41,7 +41,7 @@ export function getAssertedProperty<T extends object>(propertyName: string, obj:
|
|
|
41
41
|
const ValidProofTypeMap = new Map<StatusListType, CredentialProofFormat[]>([
|
|
42
42
|
[StatusListType.StatusList2021, ['jwt', 'lds']],
|
|
43
43
|
[StatusListType.OAuthStatusList, ['jwt', 'cbor']],
|
|
44
|
-
[StatusListType.BitstringStatusList, ['lds']],
|
|
44
|
+
[StatusListType.BitstringStatusList, ['lds', 'vc+jwt']],
|
|
45
45
|
])
|
|
46
46
|
|
|
47
47
|
export function assertValidProofType(type: StatusListType, proofFormat: CredentialProofFormat) {
|
|
@@ -53,31 +53,60 @@ export function assertValidProofType(type: StatusListType, proofFormat: Credenti
|
|
|
53
53
|
|
|
54
54
|
export function determineStatusListType(credential: StatusListCredential): StatusListType {
|
|
55
55
|
const proofFormat = determineProofFormat(credential)
|
|
56
|
+
|
|
56
57
|
switch (proofFormat) {
|
|
57
58
|
case 'jwt':
|
|
58
|
-
|
|
59
|
-
const keys = Object.keys(payload)
|
|
60
|
-
if (keys.includes('status_list')) {
|
|
61
|
-
return StatusListType.OAuthStatusList
|
|
62
|
-
} else if (keys.includes('vc')) {
|
|
63
|
-
return StatusListType.StatusList2021
|
|
64
|
-
}
|
|
65
|
-
break
|
|
59
|
+
return determineJwtStatusListType(credential as string)
|
|
66
60
|
case 'lds':
|
|
67
|
-
|
|
68
|
-
const type = uniform.type.find((t) => {
|
|
69
|
-
return Object.values(StatusListType).some((statusType) => t.includes(statusType))
|
|
70
|
-
})
|
|
71
|
-
if (!type) {
|
|
72
|
-
throw new Error('Invalid status list credential type')
|
|
73
|
-
}
|
|
74
|
-
return type.replace('Credential', '') as StatusListType
|
|
75
|
-
|
|
61
|
+
return determineLdsStatusListType(credential)
|
|
76
62
|
case 'cbor':
|
|
77
63
|
return StatusListType.OAuthStatusList
|
|
64
|
+
default:
|
|
65
|
+
throw new Error('Cannot determine status list type from credential payload')
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function determineJwtStatusListType(credential: string): StatusListType {
|
|
70
|
+
const payload: any = jwtDecode(credential)
|
|
71
|
+
|
|
72
|
+
// OAuth status list format
|
|
73
|
+
if ('status_list' in payload) {
|
|
74
|
+
return StatusListType.OAuthStatusList
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Direct credential subject
|
|
78
|
+
if ('credentialSubject' in payload) {
|
|
79
|
+
return getStatusListTypeFromSubject(payload.credentialSubject)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Wrapped VC format
|
|
83
|
+
if ('vc' in payload && 'credentialSubject' in payload.vc) {
|
|
84
|
+
return getStatusListTypeFromSubject(payload.vc.credentialSubject)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
throw new Error('Invalid status list credential: credentialSubject not found')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function determineLdsStatusListType(credential: StatusListCredential): StatusListType {
|
|
91
|
+
const uniform = CredentialMapper.toUniformCredential(credential)
|
|
92
|
+
const statusListType = uniform.type.find((type) => Object.values(StatusListType).some((statusType) => type.includes(statusType)))
|
|
93
|
+
|
|
94
|
+
if (!statusListType) {
|
|
95
|
+
throw new Error('Invalid status list credential type')
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
|
|
98
|
+
return statusListType.replace('Credential', '') as StatusListType
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getStatusListTypeFromSubject(credentialSubject: any): StatusListType {
|
|
102
|
+
switch (credentialSubject.type) {
|
|
103
|
+
case 'StatusList2021':
|
|
104
|
+
return StatusListType.StatusList2021
|
|
105
|
+
case 'BitstringStatusList':
|
|
106
|
+
return StatusListType.BitstringStatusList
|
|
107
|
+
default:
|
|
108
|
+
throw new Error(`Unknown credential subject type: ${credentialSubject.type}`)
|
|
109
|
+
}
|
|
81
110
|
}
|
|
82
111
|
|
|
83
112
|
export function determineProofFormat(credential: StatusListCredential): CredentialProofFormat {
|