@sphereon/ssi-sdk.credential-vcdm2-jose-provider 0.33.1-feature.jose.vcdm.67 → 0.33.1-feature.vcdm.verification.69

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.
@@ -1,14 +1,13 @@
1
- import type {
2
- IAgentContext,
3
- IDIDManager,
4
- IIdentifier,
5
- IKey,
6
- IKeyManager,
7
- IResolver,
8
- IVerifyResult,
9
- VerifiableCredential,
10
- VerifierAgentContext,
11
- } from '@veramo/core'
1
+ import {
2
+ type ExternalIdentifierDidOpts,
3
+ ExternalIdentifierResult,
4
+ type IIdentifierResolution,
5
+ isDidIdentifier
6
+ } from '@sphereon/ssi-sdk-ext.identifier-resolution'
7
+ import type { IJwtService, JwsHeader, JwsPayload } from '@sphereon/ssi-sdk-ext.jwt-service'
8
+ import { signatureAlgorithmFromKey } from '@sphereon/ssi-sdk-ext.key-utils'
9
+ import { contextHasPlugin } from '@sphereon/ssi-sdk.agent-config'
10
+ import { asArray, intersect, type VerifiableCredentialSP, type VerifiablePresentationSP } from '@sphereon/ssi-sdk.core'
12
11
  import {
13
12
  type ICanIssueCredentialTypeArgs,
14
13
  type ICanVerifyDocumentTypeArgs,
@@ -17,29 +16,39 @@ import {
17
16
  type IVcdmCredentialProvider,
18
17
  type IVcdmIssuerAgentContext,
19
18
  IVcdmVerifierAgentContext,
20
- IVerifyCredentialLDArgs,
19
+ IVerifyCredentialVcdmArgs,
21
20
  IVerifyPresentationLDArgs,
22
21
  pickSigningKey,
23
22
  preProcessCredentialPayload,
24
- preProcessPresentation,
23
+ preProcessPresentation
25
24
  } from '@sphereon/ssi-sdk.credential-vcdm'
25
+ import {
26
+ CredentialMapper,
27
+ isVcdm2Credential,
28
+ type IVerifyResult,
29
+ type OriginalVerifiableCredential
30
+ } from '@sphereon/ssi-types'
31
+ import type {
32
+ IAgentContext,
33
+ IDIDManager,
34
+ IIdentifier,
35
+ IKey,
36
+ IKeyManager,
37
+ IResolver,
38
+ VerifiableCredential,
39
+ VerifierAgentContext
40
+ } from '@veramo/core'
41
+
42
+ import Debug from 'debug'
43
+
44
+ import { decodeJWT, JWT_ERROR } from 'did-jwt'
26
45
 
27
46
  // @ts-ignore
28
47
  import { normalizeCredential, normalizePresentation, verifyPresentation as verifyPresentationJWT } from 'did-jwt-vc'
29
48
 
30
49
  import { type Resolvable } from 'did-resolver'
31
50
 
32
- import { decodeJWT, JWT_ERROR } from 'did-jwt'
33
-
34
- import Debug from 'debug'
35
- import { asArray, intersect, VerifiableCredentialSP, VerifiablePresentationSP } from '@sphereon/ssi-sdk.core'
36
- import { contextHasPlugin } from '@sphereon/ssi-sdk.agent-config'
37
- import { IJwtService, JwsHeader, JwsPayload } from '@sphereon/ssi-sdk-ext.jwt-service'
38
- import { ExternalIdentifierDidOpts, IIdentifierResolution, isDidIdentifier } from '@sphereon/ssi-sdk-ext.identifier-resolution'
39
- import { CredentialMapper, isVcdm2Credential, OriginalVerifiableCredential } from '@sphereon/ssi-types'
40
-
41
51
  import { SELF_ISSUED_V0_1, SELF_ISSUED_V2, SELF_ISSUED_V2_VC_INTEROP } from '../did-jwt/JWT'
42
- import { signatureAlgorithmFromKey } from '@sphereon/ssi-sdk-ext.key-utils'
43
52
  // import {validateCredentialPayload} from "did-jwt-vc/src";
44
53
 
45
54
  const debug = Debug('sphereon:ssi-sdk:credential-jwt')
@@ -96,7 +105,7 @@ export class CredentialProviderVcdm2Jose implements IVcdmCredentialProvider {
96
105
  const key = await pickSigningKey({ identifier, kmsKeyRef: keyRef }, context)
97
106
 
98
107
  // TODO: Probably wise to give control to caller as well, as some key types allow multiple signature algos
99
- const alg = await signatureAlgorithmFromKey({key}) as string
108
+ const alg = (await signatureAlgorithmFromKey({ key })) as string
100
109
  debug('Signing VC with', identifier.did, alg)
101
110
  const header: JwsHeader = {
102
111
  kid: key.meta?.verificationMethod?.id ?? key.kid,
@@ -118,12 +127,12 @@ export class CredentialProviderVcdm2Jose implements IVcdmCredentialProvider {
118
127
  }
119
128
 
120
129
  /** {@inheritdoc ICredentialVerifier.verifyCredential} */
121
- async verifyCredential(args: IVerifyCredentialLDArgs, context: VerifierAgentContext): Promise<IVerifyResult> {
130
+ async verifyCredential(args: IVerifyCredentialVcdmArgs, context: VerifierAgentContext): Promise<IVerifyResult> {
122
131
  let { credential /*policies, ...otherOptions*/ } = args
123
132
  const uniform = CredentialMapper.toUniformCredential(credential as OriginalVerifiableCredential)
124
133
  // let verifiedCredential: VerifiableCredential
125
134
  if (!isVcdm2Credential(uniform)) {
126
- return Promise.reject(new Error('invalid_argument: credential must be a VCDM2 credential. Context: ' + credential['@context']))
135
+ return Promise.reject(new Error('invalid_argument: credential must be a VCDM2 credential. Context: ' + uniform['@context']))
127
136
  }
128
137
  let verificationResult: IVerifyResult = { verified: false }
129
138
  let jwt: string | undefined = typeof credential === 'string' ? credential : asArray(uniform.proof)?.[0]?.jwt
@@ -153,25 +162,25 @@ export class CredentialProviderVcdm2Jose implements IVcdmCredentialProvider {
153
162
  },
154
163
  })
155
164
  verifiedCredential = verificationResult.verifiableCredential
156
-
165
+
157
166
  const nbf = policies?.issuanceDate === false ? false : undefined
158
167
  const exp = policies?.expirationDate === false ? false : undefined
159
168
  const options = { ...otherOptions, policies: { ...policies, nbf, exp, iat: nbf, format: policies?.format ?? true } }
160
-
169
+
161
170
  const verified: Partial<VerifiedCredential> = await verifyDIDJWT(asArray(uniform.proof)[0].jwt, { resolver, ...options }, context)
162
171
  verified.verifiableCredential = normalizeCredential(verified.jwt as string, true)
163
172
  if (options?.policies?.format !== false) {
164
173
  validateCredentialPayload(verified.verifiableCredential)
165
174
  }
166
-
175
+
167
176
  // if credential was presented with other fields, make sure those fields match what's in the JWT
168
177
  if (typeof credential !== 'string' && asArray(credential.proof)[0].type === 'JwtProof2020') {
169
178
  const credentialCopy = JSON.parse(JSON.stringify(credential))
170
179
  delete credentialCopy.proof.jwt
171
-
180
+
172
181
  const verifiedCopy = JSON.parse(JSON.stringify(verifiedCredential))
173
182
  delete verifiedCopy.proof.jwt
174
-
183
+
175
184
  if (canonicalize(credentialCopy) !== canonicalize(verifiedCopy)) {
176
185
  verificationResult.verified = false
177
186
  verificationResult.error = new Error('invalid_credential: Credential JSON does not match JWT payload')
@@ -202,7 +211,13 @@ export class CredentialProviderVcdm2Jose implements IVcdmCredentialProvider {
202
211
 
203
212
  const managedIdentifier = await agent.identifierManagedGetByDid({ identifier: holder, kmsKeyRef: keyRef })
204
213
  const identifier = managedIdentifier.identifier
205
- const key = await pickSigningKey({ identifier: managedIdentifier.identifier, kmsKeyRef: managedIdentifier.kmsKeyRef }, context)
214
+ const key = await pickSigningKey(
215
+ {
216
+ identifier: managedIdentifier.identifier,
217
+ kmsKeyRef: managedIdentifier.kmsKeyRef,
218
+ },
219
+ context,
220
+ )
206
221
 
207
222
  debug('Signing VC with', identifier.did)
208
223
  let alg = 'ES256'
@@ -283,10 +298,28 @@ export class CredentialProviderVcdm2Jose implements IVcdmCredentialProvider {
283
298
  ...otherOptions,
284
299
  })
285
300
  if (result) {
301
+ /**
302
+ * {id: 'valid_signature', valid: true},
303
+ * // {id: 'issuer_did_resolves', valid: true},
304
+ * // {id: 'expiration', valid: true},
305
+ * // {id: 'revocation_status', valid: true},
306
+ * // {id: 'suspension_status', valid: true}
307
+ */
286
308
  return {
287
309
  verified: true,
288
- verifiablePresentation: result,
289
- }
310
+ results: [
311
+ {
312
+ verified: true,
313
+ presentation: result.verifiablePresentation,
314
+ log: [
315
+ {
316
+ id: 'valid_signature',
317
+ valid: true,
318
+ },
319
+ ],
320
+ },
321
+ ],
322
+ } satisfies IVerifyResult
290
323
  }
291
324
  } catch (e: any) {
292
325
  message = e.message
@@ -374,26 +407,79 @@ export async function verifierSignature(
374
407
  if (!credIssuer) {
375
408
  throw new Error(`${JWT_ERROR.INVALID_JWT}: No DID has been found in the JWT`)
376
409
  }
377
- const resolution = await agent.identifierExternalResolve({ identifier: credIssuer })
410
+ let resolution: ExternalIdentifierResult | undefined = undefined
411
+ try {
412
+ resolution = await agent.identifierExternalResolve({ identifier: credIssuer })
413
+ } catch (e: any) {}
414
+ const credential = CredentialMapper.toUniformCredential(jwt)
415
+
416
+ const expired = 'validUntil' in credential && !!credential.validUntil && Date.parse(credential.validUntil) < new Date().getTime() / 1000
378
417
 
379
418
  const didOpts = { method: 'did', identifier: credIssuer } satisfies ExternalIdentifierDidOpts
380
419
  const jwtResult = await agent.jwtVerifyJwsSignature({
381
420
  jws: jwt,
382
421
  // @ts-ignore
383
- jwk: resolution.jwks[0].jwk,
422
+ jwk: resolution?.jwks[0].jwk,
384
423
  opts: { ...(isDidIdentifier(credIssuer) && { did: didOpts }) },
385
424
  })
386
-
387
- if (jwtResult.error) {
425
+ const error = jwtResult.error || expired || !resolution
426
+ const errorMessage = expired ? 'Credential is expired' : !resolution ? `Issuer ${credIssuer} could not be resolved` : jwtResult.message
427
+
428
+ if (error) {
429
+ const log = [
430
+ {
431
+ id: 'valid_signature',
432
+ valid: false,
433
+ },
434
+ { id: 'issuer_did_resolves', valid: resolution != undefined },
435
+ { id: 'expiration', valid: !expired },
436
+ ]
388
437
  return {
389
438
  verified: false,
390
- error: { message: jwtResult.message, errorCode: jwtResult.name },
439
+ error: { message: errorMessage, errorCode: jwtResult.name },
440
+ log,
441
+ results: [
442
+ {
443
+ verified: false,
444
+ credential: jwt,
445
+ log,
446
+ error: { message: errorMessage, errorCode: jwtResult.name },
447
+ },
448
+ ],
391
449
  payload,
392
450
  didResolutionResult: resolution,
393
451
  jwt,
394
- } as IVerifyResult
452
+ } satisfies IVerifyResult
395
453
  }
396
- return { verified: true, payload, didResolutionResult: resolution, jwt } as IVerifyResult
454
+
455
+ const log = [
456
+ {
457
+ id: 'valid_signature',
458
+ valid: true,
459
+ },
460
+ {
461
+ id: 'issuer_did_resolves',
462
+ valid: true,
463
+ },
464
+ {
465
+ id: 'expiration',
466
+ valid: true,
467
+ },
468
+ ]
469
+ return {
470
+ verified: true,
471
+ log,
472
+ results: [
473
+ {
474
+ verified: true,
475
+ credential,
476
+ log,
477
+ },
478
+ ],
479
+ payload,
480
+ didResolutionResult: resolution,
481
+ jwt,
482
+ } satisfies IVerifyResult
397
483
  }
398
484
 
399
485
  /*