diff --git a/packages/client/lib/AccessTokenClient.ts b/packages/client/lib/AccessTokenClient.ts index 7c6011b9..18fb74ca 100644 --- a/packages/client/lib/AccessTokenClient.ts +++ b/packages/client/lib/AccessTokenClient.ts @@ -132,18 +132,7 @@ export class AccessTokenClient { const credentialIssuer = opts.credentialIssuer ?? credentialOfferRequest?.credential_offer?.credential_issuer ?? opts.metadata?.issuer; await createJwtBearerClientAssertion(request, { ...opts, credentialIssuer }); - if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) { - this.assertAlphanumericPin(opts.pinMetadata, pin); - request.user_pin = pin; - request.tx_code = pin; - - request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE; - // we actually know it is there because of the isPreAuthCode call - request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL]; - - return request as AccessTokenRequest; - } - + // Prefer AUTHORIZATION_CODE over PRE_AUTHORIZED_CODE_FLOW if (!credentialOfferRequest || credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) { request.grant_type = GrantTypes.AUTHORIZATION_CODE; request.code = code; @@ -156,6 +145,18 @@ export class AccessTokenClient { return request as AccessTokenRequest; } + if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) { + this.assertAlphanumericPin(opts.pinMetadata, pin); + request.user_pin = pin; + request.tx_code = pin; + + request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE; + // we actually know it is there because of the isPreAuthCode call + request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL]; + + return request as AccessTokenRequest; + } + throw new Error('Credential offer request follows neither pre-authorized code nor authorization code flow requirements.'); } diff --git a/packages/client/lib/AccessTokenClientV1_0_11.ts b/packages/client/lib/AccessTokenClientV1_0_11.ts index 6835897f..76e25163 100644 --- a/packages/client/lib/AccessTokenClientV1_0_11.ts +++ b/packages/client/lib/AccessTokenClientV1_0_11.ts @@ -137,17 +137,7 @@ export class AccessTokenClientV1_0_11 { } await createJwtBearerClientAssertion(request, { ...opts, version: OpenId4VCIVersion.VER_1_0_11, credentialIssuer }); - if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) { - this.assertNumericPin(this.isPinRequiredValue(credentialOfferRequest.credential_offer), pin); - request.user_pin = pin; - - request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE; - // we actually know it is there because of the isPreAuthCode call - request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL]; - - return request as AccessTokenRequest; - } - + // Prefer AUTHORIZATION_CODE over PRE_AUTHORIZED_CODE_FLOW if (!credentialOfferRequest || credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) { request.grant_type = GrantTypes.AUTHORIZATION_CODE; request.code = code; @@ -160,6 +150,16 @@ export class AccessTokenClientV1_0_11 { return request as AccessTokenRequest; } + if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) { + this.assertNumericPin(this.isPinRequiredValue(credentialOfferRequest.credential_offer), pin); + request.user_pin = pin; + + request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE; + // we actually know it is there because of the isPreAuthCode call + request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL]; + + return request as AccessTokenRequest; + } throw new Error('Credential offer request does not follow neither pre-authorized code nor authorization code flow requirements.'); } diff --git a/packages/client/lib/functions/AccessTokenUtil.ts b/packages/client/lib/functions/AccessTokenUtil.ts index 1659d11d..9224b917 100644 --- a/packages/client/lib/functions/AccessTokenUtil.ts +++ b/packages/client/lib/functions/AccessTokenUtil.ts @@ -36,8 +36,8 @@ export const createJwtBearerClientAssertion = async ( sub: clientId, aud: credentialIssuer, jti: uuidv4(), - exp: Date.now() / 1000 + 60, - iat: Date.now() / 1000 - 60, + exp: Math.floor(Date.now()) / 1000 + 60, + iat: Math.floor(Date.now()) / 1000 - 60, }, }; const pop = await ProofOfPossessionBuilder.fromJwt({ diff --git a/packages/common/lib/jwt/JwtVerifier.ts b/packages/common/lib/jwt/JwtVerifier.ts index ec446987..732a5d53 100644 --- a/packages/common/lib/jwt/JwtVerifier.ts +++ b/packages/common/lib/jwt/JwtVerifier.ts @@ -66,6 +66,21 @@ export const getDidJwtVerifier = (jwt: { header: JwtHeader; payload: JwtPayload return { method: 'did', didUrl: jwt.header.kid, type: type, alg: jwt.header.alg }; }; +const getIssuer = (type: JwtType, payload: JwtPayload): string => { + // For 'request-object' the `iss` value is not required so we map the issuer to client_id + if (type === 'request-object') { + if (!payload.client_id) { + throw new Error('Missing required field client_id in request object JWT'); + } + return payload.client_id as string; + } + + if (typeof payload.iss !== 'string') { + throw new Error(`Received an invalid JWT. '${type}' contains an invalid iss claim or it is missing.`); + } + return payload.iss; +}; + export const getX5cVerifier = (jwt: { header: JwtHeader; payload: JwtPayload }, options: { type: JwtType }): X5cJwtVerifier => { const { type } = options; if (!jwt.header.x5c) throw new Error(`Received an invalid JWT. Missing x5c header.`); @@ -75,11 +90,13 @@ export const getX5cVerifier = (jwt: { header: JwtHeader; payload: JwtPayload }, throw new Error(`Received an invalid JWT.. '${type}' contains an invalid x5c header.`); } - if (typeof jwt.payload.iss !== 'string') { - throw new Error(`Received an invalid JWT. '${type}' contains an invalid iss claim.`); - } - - return { method: 'x5c', x5c: jwt.header.x5c, issuer: jwt.payload.iss, type: type, alg: jwt.header.alg }; + return { + method: 'x5c', + x5c: jwt.header.x5c, + issuer: getIssuer(type, jwt.payload), + type: type, + alg: jwt.header.alg, + }; }; export const getJwkVerifier = async (jwt: { header: JwtHeader; payload: JwtPayload }, options: { type: JwtType }): Promise => { diff --git a/packages/did-auth-siop-adapter/lib/did/DIDResolution.ts b/packages/did-auth-siop-adapter/lib/did/DIDResolution.ts index 9eb1b354..ab6b19c6 100644 --- a/packages/did-auth-siop-adapter/lib/did/DIDResolution.ts +++ b/packages/did-auth-siop-adapter/lib/did/DIDResolution.ts @@ -113,5 +113,5 @@ export async function resolveDidDocument(did: string, opts?: ResolveOpts): Promi // todo: This looks like a bug. It seems that sometimes we get back a DID document directly instead of a did resolution results return result as unknown as DIDDocument } - return result.didDocument + return result.didDocument as DIDDocument } diff --git a/packages/did-auth-siop-adapter/lib/did/DidJWT.ts b/packages/did-auth-siop-adapter/lib/did/DidJWT.ts index be2f786e..9a671d7a 100644 --- a/packages/did-auth-siop-adapter/lib/did/DidJWT.ts +++ b/packages/did-auth-siop-adapter/lib/did/DidJWT.ts @@ -259,7 +259,7 @@ export function getSubDidFromPayload(payload: JWTPayload, header?: JWTHeader): s export function isIssSelfIssued(payload: JWTPayload): boolean { return ( (payload.iss && payload.iss.includes(ResponseIss.SELF_ISSUED_V1)) || - payload.iss.includes(ResponseIss.SELF_ISSUED_V2) || + (payload.iss && payload.iss.includes(ResponseIss.SELF_ISSUED_V2)) || payload.iss === payload.sub ) } diff --git a/packages/oid4vci-common/lib/functions/ProofUtil.ts b/packages/oid4vci-common/lib/functions/ProofUtil.ts index 34101410..539854a0 100644 --- a/packages/oid4vci-common/lib/functions/ProofUtil.ts +++ b/packages/oid4vci-common/lib/functions/ProofUtil.ts @@ -146,8 +146,8 @@ const createJWT = (mode: PoPMode, jwtProps?: JwtProps, existingJwt?: Jwt): Jwt = const now = +new Date(); const jwtPayload: Partial = { ...(aud && { aud }), - iat: jwt.payload?.iat ?? Math.round(now / 1000 - 60), // Let's ensure we subtract 60 seconds for potential time offsets - exp: jwt.payload?.exp ?? Math.round(now / 1000 + 10 * 60), + iat: jwt.payload?.iat ?? Math.floor(now / 1000) - 60, // Let's ensure we subtract 60 seconds for potential time offsets + exp: jwt.payload?.exp ?? (Math.floor(now / 1000) + 10) * 60, nonce, ...(client_id && { client_id }), ...(iss && { iss }), diff --git a/packages/oid4vci-common/lib/types/Authorization.types.ts b/packages/oid4vci-common/lib/types/Authorization.types.ts index f8544d54..bc16c5a4 100644 --- a/packages/oid4vci-common/lib/types/Authorization.types.ts +++ b/packages/oid4vci-common/lib/types/Authorization.types.ts @@ -315,6 +315,7 @@ export interface AuthorizationRequestOpts { redirectUri?: string; scope?: string; requestObjectOpts?: RequestObjectOpts; + holderPreferredAuthzFlowTypeOrder?: AuthzFlowType[] } export interface AuthorizationResponse { diff --git a/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts b/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts index 47f4397f..fc330efd 100644 --- a/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts @@ -329,7 +329,7 @@ describe('create JWT from Request JWT should', () => { }, ], constraints: { - limit_disclosure: 'required', + //limit_disclosure: 'required', fields: [ { path: ['$.issuer.id'], @@ -486,7 +486,7 @@ describe('create JWT from Request JWT should', () => { }, ], constraints: { - limit_disclosure: 'required', + // limit_disclosure: 'required', fields: [ { path: ['$.issuer.id'], diff --git a/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts b/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts index 67703968..c4f56be4 100644 --- a/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/PresentationExchange.spec.ts @@ -1,5 +1,6 @@ import { SigningAlgo } from '@sphereon/oid4vc-common' -import { PresentationDefinitionV1 } from '@sphereon/pex-models' +import { PEX } from '@sphereon/pex' +import { PresentationDefinitionV1, PresentationDefinitionV2 } from '@sphereon/pex-models' import { CredentialMapper, IPresentation, IProofType, IVerifiableCredential } from '@sphereon/ssi-types' import { W3CVerifiablePresentation } from '@sphereon/ssi-types/src/types/w3c-vc' import nock from 'nock' @@ -20,7 +21,7 @@ import { } from '..' import { SIOPErrors } from '../types' -import { mockedGetEnterpriseAuthToken } from './TestUtils' +import { mockedGetEnterpriseAuthToken, pexHasher } from './TestUtils' import { VERIFIER_LOGO_FOR_CLIENT, VERIFIER_NAME_FOR_CLIENT, @@ -32,7 +33,56 @@ import { const HOLDER_DID = 'did:example:ebfeb1f712ebc6f1c276e12ec21' const EXAMPLE_PD_URL = 'http://my_own_pd.com/pd/' -async function getPayloadVID1Val(): Promise { +const KB_JWT = `${Buffer.from(JSON.stringify({ typ: 'kb+jwt' })).toString('base64url')}.${Buffer.from(JSON.stringify({ sd_hash: 'real_hash', iat: 900, nonce: 'nonce' })).toString('base64url')}.signature` +const SD_JWT_VC_CALIFORNIA_DRIVERS_LICENSE = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJpYXQiOjE3MDA0NjQ3MzYwNzYsImlzcyI6ImRpZDprZXk6c29tZS1yYW5kb20tZGlkLWtleSIsIm5iZiI6MTcwMDQ2NDczNjE3NiwidmN0IjoiaHR0cHM6Ly9kcml2ZXJzLWxpY2Vuc2UuZ292L2NhbGlmb3JuaWEiLCJ1c2VyIjp7Il9zZCI6WyI5QmhOVDVsSG5QVmpqQUp3TnR0NDIzM216MFVVMUd3RmFmLWVNWkFQV0JNIiwiSVl5d1FQZl8tNE9hY2Z2S2l1cjRlSnFMa1ZleWRxcnQ1Y2UwMGJReWNNZyIsIlNoZWM2TUNLakIxeHlCVl91QUtvLURlS3ZvQllYbUdBd2VGTWFsd05xbUEiLCJXTXpiR3BZYmhZMkdoNU9pWTRHc2hRU1dQREtSeGVPZndaNEhaQW5YS1RZIiwiajZ6ZFg1OUJYZHlTNFFaTGJITWJ0MzJpenRzWXdkZzRjNkpzWUxNc3ZaMCIsInhKR3Radm41cFM4VEhqVFlJZ3MwS1N5VC1uR3BSR3hDVnp6c1ZEbmMyWkUiXX0sImxpY2Vuc2UiOnsibnVtYmVyIjoxMH0sImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwieSI6Ilp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEifX0sIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbIl90YnpMeHBaeDBQVHVzV2hPOHRUZlVYU2ZzQjVlLUtrbzl3dmZaaFJrYVkiLCJ1WmNQaHdUTmN4LXpNQU1zemlYMkFfOXlJTGpQSEhobDhEd2pvVXJLVVdZIl19.signature~WyJHeDZHRUZvR2t6WUpWLVNRMWlDREdBIiwiZGF0ZU9mQmlydGgiLCIyMDAwMDEwMSJd~WyJ1LUt3cmJvMkZfTExQekdSZE1XLUtBIiwibmFtZSIsIkpvaG4iXQ~WyJNV1ZieGJqVFZxUXdLS3h2UGVZdWlnIiwibGFzdE5hbWUiLCJEb2UiXQ~' + + KB_JWT +const SD_JWT_VC_WASHINGTON_DRIVERS_LICENSE = + 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJpYXQiOjE3MDA0NjQ3MzYwNzYsImlzcyI6ImRpZDprZXk6c29tZS1yYW5kb20tZGlkLWtleSIsIm5iZiI6MTcwMDQ2NDczNjE3NiwidmN0IjoiaHR0cHM6Ly9kcml2ZXJzLWxpY2Vuc2UuZ292L3dhc2hpbmd0b24iLCJ1c2VyIjp7Il9zZCI6WyI5QmhOVDVsSG5QVmpqQUp3TnR0NDIzM216MFVVMUd3RmFmLWVNWkFQV0JNIiwiSVl5d1FQZl8tNE9hY2Z2S2l1cjRlSnFMa1ZleWRxcnQ1Y2UwMGJReWNNZyIsIlNoZWM2TUNLakIxeHlCVl91QUtvLURlS3ZvQllYbUdBd2VGTWFsd05xbUEiLCJXTXpiR3BZYmhZMkdoNU9pWTRHc2hRU1dQREtSeGVPZndaNEhaQW5YS1RZIiwiajZ6ZFg1OUJYZHlTNFFaTGJITWJ0MzJpenRzWXdkZzRjNkpzWUxNc3ZaMCIsInhKR3Radm41cFM4VEhqVFlJZ3MwS1N5VC1uR3BSR3hDVnp6c1ZEbmMyWkUiXX0sImxpY2Vuc2UiOnsibnVtYmVyIjoxMH0sImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwieSI6Ilp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEifX0sIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbIl90YnpMeHBaeDBQVHVzV2hPOHRUZlVYU2ZzQjVlLUtrbzl3dmZaaFJrYVkiLCJ1WmNQaHdUTmN4LXpNQU1zemlYMkFfOXlJTGpQSEhobDhEd2pvVXJLVVdZIl19.signature~WyJHeDZHRUZvR2t6WUpWLVNRMWlDREdBIiwiZGF0ZU9mQmlydGgiLCIyMDAwMDEwMSJd~WyJ1LUt3cmJvMkZfTExQekdSZE1XLUtBIiwibmFtZSIsIkpvaG4iXQ~WyJNV1ZieGJqVFZxUXdLS3h2UGVZdWlnIiwibGFzdE5hbWUiLCJEb2UiXQ~' + + KB_JWT + +const PRESENTATION_DEFINITION_SD_JWT_DRIVERS_LICENSES = { + id: '32f54163-7166-48f1-93d8-ff217bdb0653', + name: 'Conference Entry Requirements', + purpose: 'We can only allow people associated with Washington State business representatives into conference areas', + format: { + 'vc+sd-jwt': {}, + }, + input_descriptors: [ + { + id: 'wa_driver_license', + constraints: { + limit_disclosure: 'required', + fields: [ + { + path: ['$.vct'], + filter: { + type: 'string', + const: 'https://drivers-license.gov/washington', + }, + }, + ], + }, + }, + { + id: 'ca_driver_license', + constraints: { + limit_disclosure: 'required', + fields: [ + { + path: ['$.vct'], + filter: { + type: 'string', + const: 'https://drivers-license.gov/california', + }, + }, + ], + }, + }, + ], +} satisfies PresentationDefinitionV2 + +async function getPayloadVID1Val({ pd }: { pd?: PresentationDefinitionV2 } = {}): Promise { const mockEntity = await mockedGetEnterpriseAuthToken('ACME Corp') const state = getState() return { @@ -70,7 +120,7 @@ async function getPayloadVID1Val(): Promise { acr: null, }, vp_token: { - presentation_definition: { + presentation_definition: pd ?? { id: 'Insurance Plans', input_descriptors: [ { @@ -221,7 +271,7 @@ describe('presentation exchange manager tests', () => { const vcs = getVCs() vcs[0].issuer = { id: 'did:example:totallyDifferentIssuer' } await expect( - PresentationExchange.validatePresentationAgainstDefinition( + PresentationExchange.validatePresentationsAgainstDefinition( pd[0].definition, CredentialMapper.toWrappedVerifiablePresentation({ '@context': ['https://www.w3.org/2018/credentials/v1'], @@ -272,15 +322,14 @@ describe('presentation exchange manager tests', () => { const vcs = getVCs() vcs[0].issuer = { id: 'did:example:totallyDifferentIssuer' } await expect( - PresentationExchange.validatePresentationAgainstDefinition( - pd[0].definition, + PresentationExchange.validatePresentationsAgainstDefinition(pd[0].definition, [ CredentialMapper.toWrappedVerifiablePresentation({ '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation', 'PresentationSubmission'], presentation_submission, verifiableCredential: vcs, } as W3CVerifiablePresentation), - ), + ]), ).rejects.toThrow(SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD) }) @@ -297,7 +346,7 @@ describe('presentation exchange manager tests', () => { const payload = await getPayloadVID1Val() const vcs = getVCs() const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions(payload) - const result = await PresentationExchange.validatePresentationAgainstDefinition( + const result = await PresentationExchange.validatePresentationsAgainstDefinition( pd[0].definition, CredentialMapper.toWrappedVerifiablePresentation({ '@context': ['https://www.w3.org/2018/credentials/v1'], @@ -315,15 +364,14 @@ describe('presentation exchange manager tests', () => { const pex = new PresentationExchange({ allDIDs: [HOLDER_DID], allVerifiableCredentials: vcs }) const payload: AuthorizationRequestPayload = await getPayloadVID1Val() const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions(payload) - await PresentationExchange.validatePresentationAgainstDefinition( - pd[0].definition, + await PresentationExchange.validatePresentationsAgainstDefinition(pd[0].definition, [ CredentialMapper.toWrappedVerifiablePresentation({ '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation', 'PresentationSubmission'], presentation_submission, verifiableCredential: vcs, } as W3CVerifiablePresentation), - ) + ]) await pex.selectVerifiableCredentialsForSubmission(pd[0].definition) const presentationSignCallback: PresentationSignCallback = async (_args) => ({ ...(_args.presentation as IPresentation), @@ -373,8 +421,8 @@ describe('presentation exchange manager tests', () => { const result = await pex.selectVerifiableCredentialsForSubmission(pd[0].definition) expect(result.errors?.length).toBe(0) expect(result.matches?.length).toBe(1) - expect(result.matches[0].vc_path.length).toBe(1) - expect(result.matches[0].vc_path[0]).toBe('$.verifiableCredential[0]') + expect(result.matches?.[0].vc_path.length).toBe(1) + expect(result.matches?.[0].vc_path[0]).toBe('$.verifiableCredential[0]') }) it('pass if no PresentationDefinition is found', async () => { @@ -392,7 +440,7 @@ describe('presentation exchange manager tests', () => { expect(definition['input_descriptors'][0].schema.length).toBe(2) }) - it('should validate a list of VerifiablePresentations against a list of PresentationDefinitions', async () => { + it('should validate a single VerifiablePresentations against a list of PresentationDefinitions', async () => { const payload: AuthorizationRequestPayload = await getPayloadVID1Val() const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions(payload) const vcs = getVCs() @@ -411,16 +459,73 @@ describe('presentation exchange manager tests', () => { }, }) const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, vcs, presentationSignCallback, {}) - try { - await PresentationExchange.validatePresentationsAgainstDefinitions( + await PresentationExchange.validatePresentationsAgainstDefinitions( + pd, + CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentationResult.verifiablePresentation), + undefined, + { + presentationSubmission: verifiablePresentationResult.presentationSubmission, + }, + ) + }) + + it('should validate a list of VerifiablePresentations against a list of PresentationDefinitions', async () => { + const payload: AuthorizationRequestPayload = await getPayloadVID1Val({ + pd: PRESENTATION_DEFINITION_SD_JWT_DRIVERS_LICENSES, + }) + const pd = await PresentationExchange.findValidPresentationDefinitions(payload) + + // PEX/OID4VP doesn't have all functionality yet to create multi-vp submissions + // but it can make a submission based on already created presentations + const vps = [SD_JWT_VC_CALIFORNIA_DRIVERS_LICENSE, SD_JWT_VC_WASHINGTON_DRIVERS_LICENSE] + + const pex = new PEX({ hasher: pexHasher }) + const evaluated = pex.evaluatePresentation(pd[0].definition, vps, { generatePresentationSubmission: true }) + if (!evaluated.value) throw new Error('No presentation submission was generated') + + await PresentationExchange.validatePresentationsAgainstDefinitions( + pd, + vps.map((vp) => CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: pexHasher })), + undefined, + { + presentationSubmission: evaluated.value, + hasher: pexHasher, + }, + ) + }) + + it("'validatePresentationsAgainstDefinitions' should fail if not all presentations required for a presentation definition are present", async () => { + const payload: AuthorizationRequestPayload = await getPayloadVID1Val({ + pd: PRESENTATION_DEFINITION_SD_JWT_DRIVERS_LICENSES, + }) + const pd = await PresentationExchange.findValidPresentationDefinitions(payload) + + const vps = [ + SD_JWT_VC_CALIFORNIA_DRIVERS_LICENSE, + // Do not include required washington drivers license + // SD_JWT_VC_WASHINGTON_DRIVERS_LICENSE + ] + + // Manually created submission as we can't generate an invalid submission with PEX + const presentationSubmission = { + id: 'Z4JeKevZqmGFAfKmghwdf', + definition_id: '32f54163-7166-48f1-93d8-ff217bdb0653', + descriptor_map: [{ id: 'ca_driver_license', format: 'vc+sd-jwt', path: '$[0]' }], + } + + await expect( + PresentationExchange.validatePresentationsAgainstDefinitions( pd, - [CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentationResult.verifiablePresentation)], + vps.map((vp) => CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: pexHasher })), undefined, - {}, - ) - } catch (e) { - console.log(e) - } + { + presentationSubmission, + hasher: pexHasher, + }, + ), + ).rejects.toThrow( + 'Could not find VerifiableCredentials matching presentationDefinition object in the provided VC list, details: [{"status":"error","tag":"SubmissionDoesNotSatisfyDefinition","message":"Expected all input descriptors (2) to be satisfifed in submission, but found 1. Missing wa_driver_license"}]', + ) }) it("'validatePresentationsAgainstDefinitions' should fail if provided VP verification callback fails", async () => { @@ -446,11 +551,13 @@ describe('presentation exchange manager tests', () => { await expect( PresentationExchange.validatePresentationsAgainstDefinitions( pd, - [CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentationResult.verifiablePresentation)], + CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentationResult.verifiablePresentation), () => { throw new Error('Verification failed') }, - {}, + { + presentationSubmission: verifiablePresentationResult.presentationSubmission, + }, ), ).rejects.toThrow(SIOPErrors.VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID) }) diff --git a/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts b/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts index e67b7e22..2912c742 100644 --- a/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/SdJwt.spec.ts @@ -1,7 +1,5 @@ -import { createHash } from 'node:crypto' - import { SigningAlgo } from '@sphereon/oid4vc-common' -import { IPresentationDefinition, SdJwtDecodedVerifiableCredentialWithKbJwtInput } from '@sphereon/pex' +import { IPresentationDefinition } from '@sphereon/pex' import { OriginalVerifiableCredential } from '@sphereon/ssi-types' import { @@ -9,7 +7,6 @@ import { PassBy, PresentationDefinitionWithLocation, PresentationExchange, - PresentationSignCallback, PresentationVerificationCallback, PropertyTarget, ResponseIss, @@ -24,7 +21,7 @@ import { import { getVerifyJwtCallback, internalSignature } from './DidJwtTestUtils' import { getResolver } from './ResolverTestUtils' -import { mockedGetEnterpriseAuthToken, WELL_KNOWN_OPENID_FEDERATION } from './TestUtils' +import { mockedGetEnterpriseAuthToken, pexHasher, sdJwtVcPresentationSignCallback, WELL_KNOWN_OPENID_FEDERATION } from './TestUtils' import { VERIFIER_LOGO_FOR_CLIENT, VERIFIER_NAME_FOR_CLIENT, @@ -33,7 +30,6 @@ import { VERIFIERZ_PURPOSE_TO_VERIFY_NL, } from './data/mockedData' -const hasher = (data: string) => createHash('sha256').update(data).digest() jest.setTimeout(30000) const EXAMPLE_REDIRECT_URL = 'https://acme.com/hello' @@ -42,36 +38,6 @@ const HOLDER_DID = 'did:example:ebfeb1f712ebc6f1c276e12ec21' const SD_JWT_VC = 'eyJhbGciOiJFZERTQSIsInR5cCI6InZjK3NkLWp3dCJ9.eyJpYXQiOjE3MDA0NjQ3MzYwNzYsImlzcyI6ImRpZDprZXk6c29tZS1yYW5kb20tZGlkLWtleSIsIm5iZiI6MTcwMDQ2NDczNjE3NiwidmN0IjoiaHR0cHM6Ly9oaWdoLWFzc3VyYW5jZS5jb20vU3RhdGVCdXNpbmVzc0xpY2Vuc2UiLCJ1c2VyIjp7Il9zZCI6WyI5QmhOVDVsSG5QVmpqQUp3TnR0NDIzM216MFVVMUd3RmFmLWVNWkFQV0JNIiwiSVl5d1FQZl8tNE9hY2Z2S2l1cjRlSnFMa1ZleWRxcnQ1Y2UwMGJReWNNZyIsIlNoZWM2TUNLakIxeHlCVl91QUtvLURlS3ZvQllYbUdBd2VGTWFsd05xbUEiLCJXTXpiR3BZYmhZMkdoNU9pWTRHc2hRU1dQREtSeGVPZndaNEhaQW5YS1RZIiwiajZ6ZFg1OUJYZHlTNFFaTGJITWJ0MzJpenRzWXdkZzRjNkpzWUxNc3ZaMCIsInhKR3Radm41cFM4VEhqVFlJZ3MwS1N5VC1uR3BSR3hDVnp6c1ZEbmMyWkUiXX0sImxpY2Vuc2UiOnsibnVtYmVyIjoxMH0sImNuZiI6eyJqd2siOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJUQ0FFUjE5WnZ1M09IRjRqNFc0dmZTVm9ISVAxSUxpbERsczd2Q2VHZW1jIiwieSI6Ilp4amlXV2JaTVFHSFZXS1ZRNGhiU0lpcnNWZnVlY0NFNnQ0alQ5RjJIWlEifX0sIl9zZF9hbGciOiJzaGEtMjU2IiwiX3NkIjpbIl90YnpMeHBaeDBQVHVzV2hPOHRUZlVYU2ZzQjVlLUtrbzl3dmZaaFJrYVkiLCJ1WmNQaHdUTmN4LXpNQU1zemlYMkFfOXlJTGpQSEhobDhEd2pvVXJLVVdZIl19.HAcudVInhNpXkTPQGNosjKTFRJWgKj90NpfloRaDQchGd4zxc1ChWTCCPXzUXTBypASKrzgjZCiXlTr0bzmLAg~WyJHeDZHRUZvR2t6WUpWLVNRMWlDREdBIiwiZGF0ZU9mQmlydGgiLCIyMDAwMDEwMSJd~WyJ1LUt3cmJvMkZfTExQekdSZE1XLUtBIiwibmFtZSIsIkpvaG4iXQ~WyJNV1ZieGJqVFZxUXdLS3h2UGVZdWlnIiwibGFzdE5hbWUiLCJEb2UiXQ~' -const presentationSignCallback: PresentationSignCallback = async (_args) => { - const kbJwt = (_args.presentation as SdJwtDecodedVerifiableCredentialWithKbJwtInput).kbJwt - - // In real life scenario, the KB-JWT must be signed - // As the KB-JWT is a normal JWT, the user does not need an sd-jwt implementation in the presentation sign callback - // NOTE: should the presentation just be the KB-JWT header + payload instead of the whole decoded SD JWT? - expect(kbJwt).toEqual({ - header: { - typ: 'kb+jwt', - }, - payload: { - sd_hash: expect.any(String), - iat: expect.any(Number), - nonce: expect.any(String), - }, - }) - - const header = { - ...kbJwt.header, - alg: 'ES256K', - } - const payload = { - ...kbJwt.payload, - aud: '123', - } - - const kbJwtCompact = `${Buffer.from(JSON.stringify(header)).toString('base64url')}.${Buffer.from(JSON.stringify(payload)).toString('base64url')}.signature` - return SD_JWT_VC + kbJwtCompact -} - function getPresentationDefinition(): IPresentationDefinition { return { id: '32f54163-7166-48f1-93d8-ff217bdb0653', @@ -140,7 +106,7 @@ describe('RP and OP interaction should', () => { const rp = RP.builder({ requestVersion: SupportedVersion.SIOPv2_ID1 }) .withClientId(rpMockEntity.did) .withScope('test') - .withHasher(hasher) + .withHasher(pexHasher) .withResponseType([ResponseType.ID_TOKEN, ResponseType.VP_TOKEN]) .withRedirectUri(EXAMPLE_REDIRECT_URL) .withPresentationDefinition({ definition: getPresentationDefinition() }, [PropertyTarget.REQUEST_OBJECT, PropertyTarget.AUTHORIZATION_REQUEST]) @@ -170,9 +136,9 @@ describe('RP and OP interaction should', () => { .build() const op = OP.builder() - .withPresentationSignCallback(presentationSignCallback) + .withPresentationSignCallback(sdJwtVcPresentationSignCallback) .withExpiresIn(1000) - .withHasher(hasher) + .withHasher(pexHasher) .withCreateJwtCallback(internalSignature(opMockEntity.hexPrivateKey, opMockEntity.did, `${opMockEntity.did}#controller`, SigningAlgo.ES256K)) .withVerifyJwtCallback(getVerifyJwtCallback(resolver)) .withRegistration({ @@ -212,13 +178,13 @@ describe('RP and OP interaction should', () => { const pex = new PresentationExchange({ allDIDs: [HOLDER_DID], allVerifiableCredentials: getVCs(), - hasher, + hasher: pexHasher, }) const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions( parsedAuthReqURI.authorizationRequestPayload, ) await pex.selectVerifiableCredentialsForSubmission(pd[0].definition) - const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, getVCs(), presentationSignCallback, { + const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, getVCs(), sdJwtVcPresentationSignCallback, { proofOptions: { nonce: 'qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg', }, @@ -255,7 +221,7 @@ describe('RP and OP interaction should', () => { requestVersion: SupportedVersion.SIOPv2_D12_OID4VP_D18, }) .withClientId(rpMockEntity.did) - .withHasher(hasher) + .withHasher(pexHasher) .withResponseType([ResponseType.VP_TOKEN]) .withRedirectUri(EXAMPLE_REDIRECT_URL) .withPresentationDefinition({ definition: getPresentationDefinition() }, [PropertyTarget.REQUEST_OBJECT, PropertyTarget.AUTHORIZATION_REQUEST]) @@ -283,9 +249,9 @@ describe('RP and OP interaction should', () => { .withSupportedVersions(SupportedVersion.SIOPv2_ID1) .build() const op = OP.builder() - .withPresentationSignCallback(presentationSignCallback) + .withPresentationSignCallback(sdJwtVcPresentationSignCallback) .withExpiresIn(1000) - .withHasher(hasher) + .withHasher(pexHasher) .withCreateJwtCallback(internalSignature(opMockEntity.hexPrivateKey, opMockEntity.did, `${opMockEntity.did}#controller`, SigningAlgo.ES256K)) .withVerifyJwtCallback(getVerifyJwtCallback(resolver)) .withRegistration({ @@ -326,13 +292,13 @@ describe('RP and OP interaction should', () => { const pex = new PresentationExchange({ allDIDs: [HOLDER_DID], allVerifiableCredentials: getVCs(), - hasher, + hasher: pexHasher, }) const pd: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions( parsedAuthReqURI.authorizationRequestPayload, ) await pex.selectVerifiableCredentialsForSubmission(pd[0].definition) - const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, getVCs(), presentationSignCallback, { + const verifiablePresentationResult = await pex.createVerifiablePresentation(pd[0].definition, getVCs(), sdJwtVcPresentationSignCallback, { proofOptions: { nonce: 'qBrR7mqnY3Qr49dAZycPF8FzgE83m6H0c2l0bzP4xSg', }, diff --git a/packages/siop-oid4vp/lib/__tests__/TestUtils.ts b/packages/siop-oid4vp/lib/__tests__/TestUtils.ts index a3f5e364..b6afe293 100644 --- a/packages/siop-oid4vp/lib/__tests__/TestUtils.ts +++ b/packages/siop-oid4vp/lib/__tests__/TestUtils.ts @@ -1,6 +1,6 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import crypto from 'crypto' +import crypto, { createHash } from 'crypto' import { JwtPayload, parseJWT, SigningAlgo, uuidv4 } from '@sphereon/oid4vc-common' import { IProofType } from '@sphereon/ssi-types' @@ -19,6 +19,7 @@ import { DiscoveryMetadataPayload, KeyCurve, KeyType, + PresentationSignCallback, ResponseIss, ResponseType, RPRegistrationMetadataPayload, @@ -38,6 +39,7 @@ import { VERIFIERZ_PURPOSE_TO_VERIFY, VERIFIERZ_PURPOSE_TO_VERIFY_NL, } from './data/mockedData' +import { PartialSdJwtDecodedVerifiableCredential } from '@sphereon/pex/dist/main/lib' export interface TESTKEY { key: JWK @@ -281,3 +283,35 @@ export const metadata: { return assertValidMetadata(this.opMetadata, this.rpMetadata) }, } + +export const pexHasher = (data: string) => createHash('sha256').update(data).digest() + +export const sdJwtVcPresentationSignCallback: PresentationSignCallback = async (_args) => { + const presentation = _args.presentation as PartialSdJwtDecodedVerifiableCredential + + // In real life scenario, the KB-JWT must be signed + // As the KB-JWT is a normal JWT, the user does not need an sd-jwt implementation in the presentation sign callback + // NOTE: should the presentation just be the KB-JWT header + payload instead of the whole decoded SD JWT? + expect(presentation.kbJwt).toEqual({ + header: { + typ: 'kb+jwt', + }, + payload: { + sd_hash: expect.any(String), + iat: expect.any(Number), + nonce: expect.any(String), + }, + }) + + const header = { + ...presentation.kbJwt.header, + alg: 'ES256K', + } + const payload = { + ...presentation.kbJwt.payload, + aud: '123', + } + + const kbJwtCompact = `${Buffer.from(JSON.stringify(header)).toString('base64url')}.${Buffer.from(JSON.stringify(payload)).toString('base64url')}.signature` + return presentation.compactSdJwtVc + kbJwtCompact +} diff --git a/packages/siop-oid4vp/lib/__tests__/e2e/mattr.launchpad.spec.ts b/packages/siop-oid4vp/lib/__tests__/e2e/mattr.launchpad.spec.ts index 63750dff..5a8368e2 100644 --- a/packages/siop-oid4vp/lib/__tests__/e2e/mattr.launchpad.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/e2e/mattr.launchpad.spec.ts @@ -135,7 +135,8 @@ describe('OID4VCI-Client using Mattr issuer should', () => { const correlationId = 'test' - const verifiedAuthRequest = await AuthorizationRequest.verify(authorizeRequestUri, { + const authorizationRequest = await AuthorizationRequest.fromUriOrJwt(offer.authorizeRequestUri); + const verifiedAuthRequest = await authorizationRequest.verify({ correlationId, verifyJwtCallback: getVerifyJwtCallback(getResolver()), verification: {}, diff --git a/packages/siop-oid4vp/lib/__tests__/functions/LanguageTagUtils.spec.ts b/packages/siop-oid4vp/lib/__tests__/functions/LanguageTagUtils.spec.ts index 18eb5551..5fdda672 100644 --- a/packages/siop-oid4vp/lib/__tests__/functions/LanguageTagUtils.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/functions/LanguageTagUtils.spec.ts @@ -208,9 +208,10 @@ describe('Language tag util should', () => { expect(() => LanguageTagUtils.getLanguageTaggedProperties({}, null as any)).toThrowError() }) - it('throw error if list is given but not effective', async () => { + it('return empty if list is given but not effective', async () => { expect.assertions(1) - await expect(() => LanguageTagUtils.getLanguageTaggedProperties({}, [])).toThrowError() + const result = await LanguageTagUtils.getLanguageTaggedProperties({}, []) + expect(result).toEqual(new Map) }) it('throw error if list is given but no proper field names', async () => { @@ -223,9 +224,10 @@ describe('Language tag util should', () => { expect(LanguageTagUtils.getLanguageTaggedPropertiesMapped({}, null as any)).toEqual(new Map()) }) - it('throw error if mapping is given but not effective', async () => { + it('return empty map if mapping is given but not effective', async () => { expect.assertions(1) - await expect(() => LanguageTagUtils.getLanguageTaggedPropertiesMapped({}, new Map())).toThrowError() + const result = await LanguageTagUtils.getLanguageTaggedPropertiesMapped({}, new Map()) + expect(result).toEqual(new Map) }) it('throw error if mapping is given but no proper names', async () => { diff --git a/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts b/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts index baad622f..eab6b4d1 100644 --- a/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts +++ b/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts @@ -116,7 +116,7 @@ export class AuthorizationRequest { async verify(opts: VerifyAuthorizationRequestOpts): Promise { assertValidVerifyAuthorizationRequestOpts(opts) - let requestObjectPayload: RequestObjectPayload + let requestObjectPayload: RequestObjectPayload | undefined = undefined const jwt = await this.requestObjectJwt() const parsedJwt = jwt ? parseJWT(jwt) : undefined @@ -148,7 +148,7 @@ export class AuthorizationRequest { // AuthorizationRequest.assertValidRequestObject(origAuthenticationRequest); // We use the orig request for default values, but the JWT payload contains signed request object properties - const mergedPayload = { ...this.payload, ...requestObjectPayload } + const mergedPayload = { ...this.payload, ...(requestObjectPayload ? requestObjectPayload : {}) } if (opts.state && mergedPayload.state !== opts.state) { throw new Error(`${SIOPErrors.BAD_STATE} payload: ${mergedPayload.state}, supplied: ${opts.state}`) } else if (opts.nonce && mergedPayload.nonce !== opts.nonce) { diff --git a/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts b/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts index 5fa17110..090d9ecc 100644 --- a/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts +++ b/packages/siop-oid4vp/lib/authorization-response/AuthorizationResponse.ts @@ -5,7 +5,12 @@ import { assertValidVerifyAuthorizationRequestOpts } from '../authorization-requ import { IDToken } from '../id-token' import { AuthorizationResponsePayload, ResponseType, SIOPErrors, VerifiedAuthorizationRequest, VerifiedAuthorizationResponse } from '../types' -import { assertValidVerifiablePresentations, extractPresentationsFromAuthorizationResponse, verifyPresentations } from './OpenID4VP' +import { + assertValidVerifiablePresentations, + extractNonceFromWrappedVerifiablePresentation, + extractPresentationsFromAuthorizationResponse, + verifyPresentations, +} from './OpenID4VP' import { assertValidResponseOpts } from './Opts' import { createResponsePayload } from './Payload' import { AuthorizationResponseOpts, PresentationDefinitionWithLocation, VerifyAuthorizationResponseOpts } from './types' @@ -208,7 +213,7 @@ export class AuthorizationResponse { // We do not verify them, as that is done elsewhere. So we simply can take the first nonce if (!nonce) { - nonce = presentations[0].decoded.nonce + nonce = extractNonceFromWrappedVerifiablePresentation(Array.isArray(presentations) ? presentations[0] : presentations) } } diff --git a/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts b/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts index 2c3c73ad..fc5c6e22 100644 --- a/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts +++ b/packages/siop-oid4vp/lib/authorization-response/OpenID4VP.ts @@ -32,7 +32,8 @@ import { VPTokenLocation, } from './types' -function extractNonceFromWrappedVerifiablePresentation(wrappedVp: WrappedVerifiablePresentation): string | undefined { +export function extractNonceFromWrappedVerifiablePresentation(wrappedVp: WrappedVerifiablePresentation): string | undefined { + // SD-JWT uses kb-jwt for the nonce if (CredentialMapper.isWrappedSdJwtVerifiablePresentation(wrappedVp)) { // SD-JWT uses kb-jwt for the nonce // TODO: replace this once `kbJwt.payload` is available on the decoded sd-jwt (pr in ssi-sdk) @@ -95,13 +96,15 @@ export const verifyPresentations = async ( // If there are no presentations, and the `assertValidVerifiablePresentations` did not fail // it means there's no oid4vp response and also not requested - if (presentations.length === 0) { + if (Array.isArray(presentations) && presentations.length === 0) { return null } - const nonces = new Set(presentations.map(extractNonceFromWrappedVerifiablePresentation)) - if (presentations.length > 0 && nonces.size !== 1) { - throw Error(`${nonces.size} nonce values found for ${presentations.length}. Should be 1`) + const presentationsArray = Array.isArray(presentations) ? presentations : [presentations] + + const nonces = new Set(presentationsArray.map(extractNonceFromWrappedVerifiablePresentation)) + if (presentationsArray.length > 0 && nonces.size !== 1) { + throw Error(`${nonces.size} nonce values found for ${presentationsArray.length}. Should be 1`) } // Nonce may be undefined @@ -117,25 +120,24 @@ export const verifyPresentations = async ( if (!verifyOpts.verification.revocationOpts?.revocationVerificationCallback) { throw Error(`Please provide a revocation callback as revocation checking of credentials and presentations is not disabled`) } - for (const vp of presentations) { + for (const vp of presentationsArray) { await verifyRevocation(vp, verifyOpts.verification.revocationOpts.revocationVerificationCallback, revocationVerification) } } - return { nonce, presentations, presentationDefinitions, submissionData: presentationSubmission } + return { nonce, presentations: presentationsArray, presentationDefinitions, submissionData: presentationSubmission } } export const extractPresentationsFromAuthorizationResponse = async ( response: AuthorizationResponse, opts?: { hasher?: Hasher }, -): Promise => { - const wrappedVerifiablePresentations: WrappedVerifiablePresentation[] = [] - if (response.payload.vp_token) { - const presentations = Array.isArray(response.payload.vp_token) ? response.payload.vp_token : [response.payload.vp_token] - for (const presentation of presentations) { - wrappedVerifiablePresentations.push(CredentialMapper.toWrappedVerifiablePresentation(presentation, { hasher: opts?.hasher })) - } +): Promise => { + if (!response.payload.vp_token) return [] + + if (Array.isArray(response.payload.vp_token)) { + return response.payload.vp_token.map((vp) => CredentialMapper.toWrappedVerifiablePresentation(vp, { hasher: opts?.hasher })) } - return wrappedVerifiablePresentations + + return CredentialMapper.toWrappedVerifiablePresentation(response.payload.vp_token, { hasher: opts?.hasher }) } export const createPresentationSubmission = async ( verifiablePresentations: W3CVerifiablePresentation[], @@ -266,7 +268,7 @@ export const putPresentationSubmissionInLocation = async ( export const assertValidVerifiablePresentations = async (args: { presentationDefinitions: PresentationDefinitionWithLocation[] - presentations: WrappedVerifiablePresentation[] + presentations: Array | WrappedVerifiablePresentation verificationCallback: PresentationVerificationCallback opts?: { limitDisclosureSignatureSuites?: string[] @@ -276,29 +278,32 @@ export const assertValidVerifiablePresentations = async (args: { hasher?: Hasher } }) => { - const { presentations, presentationDefinitions } = args - if ( - (!presentationDefinitions || presentationDefinitions.filter((a) => a.definition).length === 0) && - (!presentations || (Array.isArray(presentations) && presentations.filter((vp) => vp.presentation).length === 0)) + (!args.presentationDefinitions || args.presentationDefinitions.filter((a) => a.definition).length === 0) && + (!args.presentations || (Array.isArray(args.presentations) && args.presentations.filter((vp) => vp.presentation).length === 0)) ) { return } + PresentationExchange.assertValidPresentationDefinitionWithLocations(args.presentationDefinitions) + const presentationsWithFormat = args.presentations - PresentationExchange.assertValidPresentationDefinitionWithLocations(presentationDefinitions) - const presentationsWithFormat = presentations - - if (presentationDefinitions && presentationDefinitions.length && (!presentationsWithFormat || presentationsWithFormat.length === 0)) { + if ( + args.presentationDefinitions && + args.presentationDefinitions.length && + (!presentationsWithFormat || (Array.isArray(presentationsWithFormat) && presentationsWithFormat.length === 0)) + ) { throw new Error(SIOPErrors.AUTH_REQUEST_EXPECTS_VP) - } else if ((!presentationDefinitions || presentationDefinitions.length === 0) && presentationsWithFormat && presentationsWithFormat.length > 0) { + } else if ( + (!args.presentationDefinitions || args.presentationDefinitions.length === 0) && + presentationsWithFormat && + ((Array.isArray(presentationsWithFormat) && presentationsWithFormat.length > 0) || !Array.isArray(presentationsWithFormat)) + ) { throw new Error(SIOPErrors.AUTH_REQUEST_DOESNT_EXPECT_VP) - } else if (presentationDefinitions && presentationsWithFormat && presentationDefinitions.length != presentationsWithFormat.length) { - throw new Error(SIOPErrors.AUTH_REQUEST_EXPECTS_VP) - } else if (presentationDefinitions && !args.opts.presentationSubmission) { + } else if (args.presentationDefinitions && !args.opts.presentationSubmission) { throw new Error(`No presentation submission present. Please use presentationSubmission opt argument!`) - } else if (presentationDefinitions && presentationsWithFormat) { + } else if (args.presentationDefinitions && presentationsWithFormat) { await PresentationExchange.validatePresentationsAgainstDefinitions( - presentationDefinitions, + args.presentationDefinitions, presentationsWithFormat, args.verificationCallback, args.opts, diff --git a/packages/siop-oid4vp/lib/authorization-response/Payload.ts b/packages/siop-oid4vp/lib/authorization-response/Payload.ts index 83997190..bdd7cc80 100644 --- a/packages/siop-oid4vp/lib/authorization-response/Payload.ts +++ b/packages/siop-oid4vp/lib/authorization-response/Payload.ts @@ -21,10 +21,9 @@ export const createResponsePayload = async ( const state: string | undefined = await authorizationRequest.getMergedProperty('state') const responsePayload: AuthorizationResponsePayload = { - ...(responseOpts.accessToken && { access_token: responseOpts.accessToken }), + ...(responseOpts.accessToken && { access_token: responseOpts.accessToken, expires_in: responseOpts.expiresIn || 3600 }), ...(responseOpts.tokenType && { token_type: responseOpts.tokenType }), ...(responseOpts.refreshToken && { refresh_token: responseOpts.refreshToken }), - expires_in: responseOpts.expiresIn || 3600, state, } diff --git a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts index 18afe984..e04af13c 100644 --- a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts +++ b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts @@ -5,10 +5,11 @@ import { PresentationSubmissionLocation, SelectResults, Status, + Validated, VerifiablePresentationFromOpts, - VerifiablePresentationResult, + VerifiablePresentationResult } from '@sphereon/pex' -import { PresentationEvaluationResults } from '@sphereon/pex/dist/main/lib/evaluation/core' +import { PresentationEvaluationResults } from '@sphereon/pex/dist/main/lib/evaluation' import { Format, PresentationDefinitionV1, PresentationDefinitionV2, PresentationSubmission } from '@sphereon/pex-models' import { CredentialMapper, @@ -16,7 +17,6 @@ import { IProofPurpose, IProofType, OriginalVerifiableCredential, - OriginalVerifiablePresentation, W3CVerifiablePresentation, WrappedVerifiablePresentation, } from '@sphereon/ssi-types' @@ -64,14 +64,14 @@ export class PresentationExchange { ...options, presentationSubmissionLocation: PresentationSubmissionLocation.EXTERNAL, proofOptions: { - ...options.proofOptions, + ...options?.proofOptions, proofPurpose: options?.proofOptions?.proofPurpose ?? IProofPurpose.authentication, type: options?.proofOptions?.type ?? IProofType.EcdsaSecp256k1Signature2019, /* challenge: options?.proofOptions?.challenge, domain: options?.proofOptions?.domain,*/ }, signatureOptions: { - ...options.signatureOptions, + ...options?.signatureOptions, // verificationMethod: options?.signatureOptions?.verificationMethod, keyEncoding: options?.signatureOptions?.keyEncoding ?? KeyEncoding.Hex, }, @@ -116,54 +116,11 @@ export class PresentationExchange { return selectResults } - /** - * validatePresentationAgainstDefinition function is called mainly by the RP - * after receiving the VP from the OP - * @param presentationDefinition object containing PD - * @param verifiablePresentation - * @param opts - */ - public static async validatePresentationAgainstDefinition( - presentationDefinition: IPresentationDefinition, - verifiablePresentation: OriginalVerifiablePresentation | WrappedVerifiablePresentation, - opts?: { - limitDisclosureSignatureSuites?: string[] - restrictToFormats?: Format - restrictToDIDMethods?: string[] - presentationSubmission?: PresentationSubmission - hasher?: Hasher - }, - ): Promise { - const wvp: WrappedVerifiablePresentation = - typeof verifiablePresentation === 'object' && 'original' in verifiablePresentation - ? (verifiablePresentation as WrappedVerifiablePresentation) - : CredentialMapper.toWrappedVerifiablePresentation(verifiablePresentation as OriginalVerifiablePresentation) - if (!presentationDefinition) { - throw new Error(SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_NOT_VALID) - } else if ( - !wvp || - !wvp.presentation || - (CredentialMapper.isWrappedW3CVerifiablePresentation(wvp) && - (!wvp.presentation.verifiableCredential || wvp.presentation.verifiableCredential.length === 0)) - ) { - throw new Error(SIOPErrors.NO_VERIFIABLE_PRESENTATION_NO_CREDENTIALS) - } - // console.log(`Presentation (validate): ${JSON.stringify(verifiablePresentation)}`); - const evaluationResults: PresentationEvaluationResults = new PEX({ hasher: opts?.hasher }).evaluatePresentation( - presentationDefinition, - wvp.original, - opts, - ) - if (evaluationResults.errors.length) { - throw new Error(`message: ${SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD}, details: ${JSON.stringify(evaluationResults.errors)}`) - } - return evaluationResults - } - public static assertValidPresentationSubmission(presentationSubmission: PresentationSubmission) { - const validationResult = PEX.validateSubmission(presentationSubmission) - if (validationResult[0].message != 'ok') { - throw new Error(`${SIOPErrors.RESPONSE_OPTS_PRESENTATIONS_SUBMISSION_IS_NOT_VALID}, details ${JSON.stringify(validationResult[0])}`) + const validationResult:Validated = PEX.validateSubmission(presentationSubmission) + if (Array.isArray(validationResult) && validationResult[0].message != 'ok' + || !Array.isArray(validationResult) && validationResult.message != 'ok') { + throw new Error(`${SIOPErrors.RESPONSE_OPTS_PRESENTATIONS_SUBMISSION_IS_NOT_VALID}, details ${JSON.stringify(validationResult)}`) } } @@ -287,15 +244,16 @@ export class PresentationExchange { private static assertValidPresentationDefinition(presentationDefinition: IPresentationDefinition) { const validationResult = PEX.validateDefinition(presentationDefinition) - if (validationResult[0].message != 'ok') { + if (Array.isArray(validationResult) && validationResult[0].message != 'ok' + || !Array.isArray(validationResult) && validationResult.message != 'ok') { throw new Error(`${SIOPErrors.REQUEST_CLAIMS_PRESENTATION_DEFINITION_NOT_VALID}`) } } static async validatePresentationsAgainstDefinitions( definitions: PresentationDefinitionWithLocation[], - vpPayloads: WrappedVerifiablePresentation[], - verifyPresentationCallback: PresentationVerificationCallback | undefined, + vpPayloads: Array | WrappedVerifiablePresentation, + verifyPresentationCallback?: PresentationVerificationCallback | undefined, opts?: { limitDisclosureSignatureSuites?: string[] restrictToFormats?: Format @@ -304,7 +262,7 @@ export class PresentationExchange { hasher?: Hasher }, ) { - if (!definitions || !vpPayloads || !definitions.length || definitions.length !== vpPayloads.length) { + if (!definitions || !vpPayloads || (Array.isArray(vpPayloads) && vpPayloads.length === 0) || !definitions.length) { throw new Error(SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD) } await Promise.all( @@ -314,10 +272,10 @@ export class PresentationExchange { ) } - private static async validatePresentationsAgainstDefinition( + static async validatePresentationsAgainstDefinition( definition: IPresentationDefinition, - vpPayloads: WrappedVerifiablePresentation[], - verifyPresentationCallback: PresentationVerificationCallback | undefined, + vpPayloads: Array | WrappedVerifiablePresentation, + verifyPresentationCallback?: PresentationVerificationCallback | undefined, opts?: { limitDisclosureSignatureSuites?: string[] restrictToFormats?: Format @@ -327,28 +285,60 @@ export class PresentationExchange { }, ) { const pex = new PEX({ hasher: opts?.hasher }) + const vpPayloadsArray = Array.isArray(vpPayloads) ? vpPayloads : [vpPayloads] + + let evaluationResults: PresentationEvaluationResults | undefined = undefined + if (opts?.presentationSubmission) { + evaluationResults = pex.evaluatePresentation( + definition, + Array.isArray(vpPayloads) ? vpPayloads.map((wvp) => wvp.original) : vpPayloads.original, + { + ...opts, + // We always have external presentation submissions here. Some older versions of OID4VP allow for submission in presentation, + // but in that case the submission will not be provided + presentationSubmissionLocation: PresentationSubmissionLocation.EXTERNAL, + }, + ) + } else { + for (const wvp of vpPayloadsArray) { + if (CredentialMapper.isWrappedW3CVerifiablePresentation(wvp) && wvp.presentation.presentation_submission) { + const presentationSubmission = wvp.presentation.presentation_submission + evaluationResults = pex.evaluatePresentation(definition, wvp.original, { + ...opts, + presentationSubmission, + presentationSubmissionLocation: PresentationSubmissionLocation.PRESENTATION, + }) + const submission = evaluationResults.value - async function filterOutCorrectPresentation() { - //TODO: add support for multiple VPs here - const matchingVps = vpPayloads.map(async (vpw: WrappedVerifiablePresentation): Promise => { - const presentationSubmission = - opts?.presentationSubmission ?? - (CredentialMapper.isWrappedW3CVerifiablePresentation(vpw) ? vpw.presentation.presentation_submission : undefined) - const presentation = vpw.presentation - if (!definition) { - throw new Error(SIOPErrors.NO_PRESENTATION_SUBMISSION) - } else if ( - !vpw.presentation || - (CredentialMapper.isWrappedW3CVerifiablePresentation(vpw) && - (!vpw.presentation.verifiableCredential || vpw.presentation.verifiableCredential.length === 0)) - ) { - throw new Error(SIOPErrors.NO_VERIFIABLE_PRESENTATION_NO_CREDENTIALS) + // Found valid submission + if (evaluationResults.areRequiredCredentialsPresent && submission && submission.definition_id === definition.id) break } - // The verifyPresentationCallback function is mandatory for RP only, - // So the behavior here is to bypass it if not present - if (verifyPresentationCallback) { + } + } + + if (!evaluationResults) { + throw new Error(SIOPErrors.NO_PRESENTATION_SUBMISSION) + } + + if (evaluationResults.areRequiredCredentialsPresent === Status.ERROR || evaluationResults.errors?.length || !evaluationResults.value) { + throw new Error(`message: ${SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD}, details: ${JSON.stringify(evaluationResults.errors)}`) + } + + if (evaluationResults.value.definition_id !== definition.id) { + throw new Error( + `${SIOPErrors.PRESENTATION_SUBMISSION_DEFINITION_ID_DOES_NOT_MATCHING_DEFINITION_ID}. submission.definition_id: ${evaluationResults.value.definition_id}, definition.id: ${definition.id}`, + ) + } + + const presentationsToVerify = Array.isArray(evaluationResults.presentation) ? evaluationResults.presentation : [evaluationResults.presentation] + // The verifyPresentationCallback function is mandatory for RP only, + // So the behavior here is to bypass it if not present + if (verifyPresentationCallback && evaluationResults.value !== undefined) { + // Verify the signature of all VPs + await Promise.all( + presentationsToVerify.map(async (presentation) => { try { - const verificationResult = await verifyPresentationCallback(vpw.original as W3CVerifiablePresentation, presentationSubmission) + const verificationResult = await verifyPresentationCallback(presentation as W3CVerifiablePresentation, evaluationResults.value!) if (!verificationResult.verified) { throw new Error( SIOPErrors.VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID + verificationResult.reason ? `. ${verificationResult.reason}` : '', @@ -357,56 +347,12 @@ export class PresentationExchange { } catch (error: unknown) { throw new Error(SIOPErrors.VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID) } - } - // console.log(`Presentation (filter): ${JSON.stringify(presentation)}`); - - const evaluationResults = pex.evaluatePresentation(definition, vpw.original, { - ...opts, - presentationSubmission, - }) - const submission = evaluationResults.value - if (!presentation || !submission) { - throw new Error(SIOPErrors.NO_PRESENTATION_SUBMISSION) - } - - // No match - if (submission.definition_id !== definition.id) { - return undefined - } - - return vpw - }) - - // Wait for all results to finish and filter out undefined (no match) values - return (await Promise.all(matchingVps)).filter((vp) => vp !== undefined) + }), + ) } - const checkedPresentations = await filterOutCorrectPresentation() - - if (checkedPresentations.length !== 1) { - throw new Error(`${SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD}`) - } - const checkedPresentation = checkedPresentations[0] - const presentation = checkedPresentation.presentation - // console.log(`Presentation (checked): ${JSON.stringify(checkedPresentation.presentation)}`); - if ( - !checkedPresentation.presentation || - (CredentialMapper.isWrappedW3CVerifiablePresentation(checkedPresentation) && - (!checkedPresentation.presentation.verifiableCredential || checkedPresentation.presentation.verifiableCredential.length === 0)) - ) { - throw new Error(SIOPErrors.NO_VERIFIABLE_PRESENTATION_NO_CREDENTIALS) - } - const presentationSubmission = - opts?.presentationSubmission ?? (CredentialMapper.isW3cPresentation(presentation) ? presentation.presentation_submission : undefined) - const evaluationResults = pex.evaluatePresentation(definition, checkedPresentation.original, { - ...opts, - presentationSubmission, - }) PresentationExchange.assertValidPresentationSubmission(evaluationResults.value) - await PresentationExchange.validatePresentationAgainstDefinition(definition, checkedPresentation, { - ...opts, - presentationSubmission, - hasher: opts?.hasher, - }) + + return evaluationResults } } diff --git a/packages/siop-oid4vp/lib/helpers/Encodings.ts b/packages/siop-oid4vp/lib/helpers/Encodings.ts index 075def11..c7a56aa3 100644 --- a/packages/siop-oid4vp/lib/helpers/Encodings.ts +++ b/packages/siop-oid4vp/lib/helpers/Encodings.ts @@ -14,10 +14,11 @@ export function decodeUriAsJson(uri: string) { } const parts = parse(queryString, { plainObjects: true, depth: 10, parameterLimit: 5000, ignoreQueryPrefix: true }) - const descriptors = parts?.claims?.['vp_token']?.presentation_definition?.['input_descriptors'] + const vpToken = (parts?.claims as { [key: string]: any })?.['vp_token']; + const descriptors = vpToken?.presentation_definition?.['input_descriptors']; // FIXME? if (descriptors && Array.isArray(descriptors)) { // Whenever we have a [{'uri': 'str1'}, 'uri': 'str2'] qs changes this to {uri: ['str1','str2']} which means schema validation fails. So we have to fix that - parts.claims['vp_token'].presentation_definition['input_descriptors'] = descriptors.map((descriptor: InputDescriptorV1) => { + vpToken.presentation_definition['input_descriptors'] = descriptors.map((descriptor: InputDescriptorV1) => { if (Array.isArray(descriptor.schema)) { descriptor.schema = descriptor.schema.flatMap((val) => { if (typeof val === 'string') { @@ -32,7 +33,7 @@ export function decodeUriAsJson(uri: string) { }) } - const json = {} + const json:Record = {} for (const key in parts) { const value = parts[key] if (!value) { @@ -56,7 +57,7 @@ export function decodeUriAsJson(uri: string) { return JSON.parse(JSON.stringify(json)) } -export function encodeJsonAsURI(json: unknown, _opts?: { arraysWithIndex?: string[] }): string { +export function encodeJsonAsURI(json: Record, _opts?: { arraysWithIndex?: string[] }): string { if (typeof json === 'string') { return encodeJsonAsURI(JSON.parse(json)) } diff --git a/packages/siop-oid4vp/lib/helpers/HttpUtils.ts b/packages/siop-oid4vp/lib/helpers/HttpUtils.ts index a88df4ac..70ba748c 100644 --- a/packages/siop-oid4vp/lib/helpers/HttpUtils.ts +++ b/packages/siop-oid4vp/lib/helpers/HttpUtils.ts @@ -61,7 +61,7 @@ const siopFetch = async ( if (!url || url.toLowerCase().startsWith('did:')) { throw Error(`Invalid URL supplied. Expected a http(s) URL. Recieved: ${url}`) } - const headers = opts?.customHeaders ? opts.customHeaders : {} + const headers:Record = opts?.customHeaders ? opts.customHeaders : {} if (opts?.bearerToken) { headers['Authorization'] = `Bearer ${opts.bearerToken}` } @@ -87,7 +87,7 @@ const siopFetch = async ( const textResponseBody = await clonedResponse.text() const isJSONResponse = - (accept === 'application/json' || origResponse.headers['Content-Type'] === 'application/json') && textResponseBody.trim().startsWith('{') + (accept === 'application/json' || origResponse.headers.get('Content-Type') === 'application/json') && textResponseBody.trim().startsWith('{') const responseBody = isJSONResponse ? JSON.parse(textResponseBody) : textResponseBody if (success || opts?.exceptionOnHttpErrorStatus) { @@ -131,7 +131,7 @@ export const fetchByReferenceOrUseByValue = async (referenceURI: string, valu response = await getWithUrl(referenceURI, textResponse) } catch (e) { console.log(e) - throw new Error(`${SIOPErrors.REG_PASS_BY_REFERENCE_INCORRECTLY}: ${e.message}, URL: ${referenceURI}`) + throw new Error(`${SIOPErrors.REG_PASS_BY_REFERENCE_INCORRECTLY}: ${(e as Error).message}, URL: ${referenceURI}`) } } return response diff --git a/packages/siop-oid4vp/lib/helpers/LanguageTagUtils.ts b/packages/siop-oid4vp/lib/helpers/LanguageTagUtils.ts index 434512a4..e3e800f5 100644 --- a/packages/siop-oid4vp/lib/helpers/LanguageTagUtils.ts +++ b/packages/siop-oid4vp/lib/helpers/LanguageTagUtils.ts @@ -13,8 +13,8 @@ export class LanguageTagUtils { * * @param source is the object from which the language enabled fields and their values will be extracted. */ - static getAllLanguageTaggedProperties(source: unknown): Map { - return this.getLanguageTaggedPropertiesMapped(source, undefined) + static getAllLanguageTaggedProperties(source: object): Map { + return this.getLanguageTaggedPropertiesMapped(source, new Map() ) } /** @@ -23,7 +23,7 @@ export class LanguageTagUtils { * @param source is the object from which the language enabled fields and their values will be extracted. * @param requiredFieldNames the fields which are supposed to be language enabled. These are the only fields which should be returned. */ - static getLanguageTaggedProperties(source: unknown, requiredFieldNames: Array): Map { + static getLanguageTaggedProperties(source: object, requiredFieldNames: Array): Map { const languageTagEnabledFieldsNamesMapping: Map = new Map() requiredFieldNames.forEach((value) => languageTagEnabledFieldsNamesMapping.set(value, value)) return this.getLanguageTaggedPropertiesMapped(source, languageTagEnabledFieldsNamesMapping) @@ -36,7 +36,7 @@ export class LanguageTagUtils { * @param requiredFieldNamesMapping the fields which are supposed to be language enabled. These are the only fields which should be returned. And * the fields names will be transformed as per the mapping provided. */ - static getLanguageTaggedPropertiesMapped(source: unknown, requiredFieldNamesMapping: Map): Map { + static getLanguageTaggedPropertiesMapped(source: object, requiredFieldNamesMapping: Map): Map { this.assertSourceIsWorthChecking(source) this.assertValidTargetFieldNames(requiredFieldNamesMapping) @@ -103,16 +103,12 @@ export class LanguageTagUtils { } private static assertValidTargetFieldNames(languageTagEnabledFieldsNamesMapping: Map): void { - if (languageTagEnabledFieldsNamesMapping) { - if (!languageTagEnabledFieldsNamesMapping.size) { - throw new Error(SIOPErrors.BAD_PARAMS + ' LanguageTagEnabledFieldsNamesMapping must be non-null or non-empty') - } else { - for (const entry of languageTagEnabledFieldsNamesMapping.entries()) { - const key = entry[0] - const value = entry[1] - if (isStringNullOrEmpty(key) || isStringNullOrEmpty(value)) { - throw new Error(SIOPErrors.BAD_PARAMS + '. languageTagEnabledFieldsName must be non-null or non-empty') - } + if (languageTagEnabledFieldsNamesMapping && languageTagEnabledFieldsNamesMapping.size) { + for (const entry of languageTagEnabledFieldsNamesMapping.entries()) { + const key = entry[0] + const value = entry[1] + if (isStringNullOrEmpty(key) || isStringNullOrEmpty(value)) { + throw new Error(SIOPErrors.BAD_PARAMS + '. languageTagEnabledFieldsName must be non-null or non-empty') } } } diff --git a/packages/siop-oid4vp/lib/helpers/Metadata.ts b/packages/siop-oid4vp/lib/helpers/Metadata.ts index 5fa7e8d1..f1b48383 100644 --- a/packages/siop-oid4vp/lib/helpers/Metadata.ts +++ b/packages/siop-oid4vp/lib/helpers/Metadata.ts @@ -13,9 +13,9 @@ export function assertValidMetadata(opMetadata: DiscoveryMetadataPayload, rpMeta const credentials = supportedCredentialsFormats(rpMetadata.vp_formats, opMetadata.vp_formats) const isValidSubjectSyntax = verifySubjectSyntaxes(rpMetadata.subject_syntax_types_supported) if (isValidSubjectSyntax && rpMetadata.subject_syntax_types_supported) { - subjectSyntaxTypesSupported = supportedSubjectSyntaxTypes(rpMetadata.subject_syntax_types_supported, opMetadata.subject_syntax_types_supported) + subjectSyntaxTypesSupported = supportedSubjectSyntaxTypes(rpMetadata.subject_syntax_types_supported, opMetadata.subject_syntax_types_supported as string[]) } else if (isValidSubjectSyntax && (!rpMetadata.subject_syntax_types_supported || !rpMetadata.subject_syntax_types_supported.length)) { - if (opMetadata.subject_syntax_types_supported || opMetadata.subject_syntax_types_supported.length) { + if (opMetadata.subject_syntax_types_supported) { subjectSyntaxTypesSupported = [...opMetadata.subject_syntax_types_supported] } } @@ -85,7 +85,7 @@ function supportedSubjectSyntaxTypes(rpMethods: string[] | string, opMethods: st } function getFormatIntersection(rpFormat: Format, opFormat: Format): Format { - const intersectionFormat: Format = {} + const intersectionFormat: Record = {} const supportedCredentials = getIntersection(Object.keys(rpFormat), Object.keys(opFormat)) if (!supportedCredentials.length) { throw new Error(SIOPErrors.CREDENTIAL_FORMATS_NOT_SUPPORTED) @@ -107,7 +107,9 @@ function getFormatIntersection(rpFormat: Format, opFormat: Format): Format { throw new Error(SIOPErrors.CREDENTIAL_FORMATS_NOT_SUPPORTED) } intersectionFormat[crFormat] = {} - intersectionFormat[crFormat][methodKeyOP] = algs + if(methodKeyOP !== undefined) { + intersectionFormat[crFormat][methodKeyOP] = algs + } }) return intersectionFormat } diff --git a/packages/siop-oid4vp/lib/helpers/ObjectUtils.ts b/packages/siop-oid4vp/lib/helpers/ObjectUtils.ts index e278902d..1e4e803b 100644 --- a/packages/siop-oid4vp/lib/helpers/ObjectUtils.ts +++ b/packages/siop-oid4vp/lib/helpers/ObjectUtils.ts @@ -9,7 +9,7 @@ export function isStringNullOrEmpty(key: string) { return !key || !key.length } -export function removeNullUndefined(data: unknown) { +export function removeNullUndefined(data: T) : T { if (!data) { return data } @@ -22,5 +22,5 @@ export function removeNullUndefined(data: unknown) { return [key, value] }) //transform the key-value pairs back to an object. - return Object.fromEntries(clean) + return Object.fromEntries(clean) as T } diff --git a/packages/siop-oid4vp/lib/helpers/Revocation.ts b/packages/siop-oid4vp/lib/helpers/Revocation.ts index 2c38e2de..7d8ba738 100644 --- a/packages/siop-oid4vp/lib/helpers/Revocation.ts +++ b/packages/siop-oid4vp/lib/helpers/Revocation.ts @@ -14,7 +14,8 @@ export const verifyRevocation = async ( throw new Error(`Revocation callback not provided`) } - const vcs = CredentialMapper.isWrappedSdJwtVerifiablePresentation(vpToken) ? [vpToken.vcs[0]] : vpToken.presentation.verifiableCredential + const vcs = (CredentialMapper.isWrappedSdJwtVerifiablePresentation(vpToken) || CredentialMapper.isWrappedMdocPresentation(vpToken)) + ? [vpToken.vcs[0]] : vpToken.presentation.verifiableCredential for (const vc of vcs) { if ( revocationVerification === RevocationVerification.ALWAYS || @@ -38,6 +39,7 @@ function originalTypeToVerifiableCredentialTypeFormat(original: WrappedVerifiabl jwt_vc: VerifiableCredentialTypeFormat.JWT_VC, ldp: VerifiableCredentialTypeFormat.LDP_VC, ldp_vc: VerifiableCredentialTypeFormat.LDP_VC, + mso_mdoc: VerifiableCredentialTypeFormat.MSO_MDOC } return mapping[original] diff --git a/packages/siop-oid4vp/lib/op/OP.ts b/packages/siop-oid4vp/lib/op/OP.ts index 6e38b452..1795b99b 100644 --- a/packages/siop-oid4vp/lib/op/OP.ts +++ b/packages/siop-oid4vp/lib/op/OP.ts @@ -237,7 +237,7 @@ export class OP { }) } - const authResponseAsURI = encodeJsonAsURI(payload, { arraysWithIndex: ['presentation_submission', 'vp_token'] }) + const authResponseAsURI = encodeJsonAsURI(payload, { arraysWithIndex: ['presentation_submission'] }) return post(responseUri, authResponseAsURI, { contentType: ContentType.FORM_URL_ENCODED, exceptionOnHttpErrorStatus: true }) .then((result: SIOPResonse) => { void this.emitEvent(AuthorizationEvents.ON_AUTH_RESPONSE_SENT_SUCCESS, { correlationId, subject: response }) diff --git a/packages/siop-oid4vp/lib/request-object/RequestObject.ts b/packages/siop-oid4vp/lib/request-object/RequestObject.ts index fba9bcb1..bd9ecbae 100644 --- a/packages/siop-oid4vp/lib/request-object/RequestObject.ts +++ b/packages/siop-oid4vp/lib/request-object/RequestObject.ts @@ -37,11 +37,11 @@ export class RequestObject { * part of the URI and which become part of the Request Object. If you generate a URI based upon the result of this class, * the URI will be constructed based on the Request Object only! */ - public static async fromOpts(authorizationRequestOpts: CreateAuthorizationRequestOpts) { + public static async fromOpts(authorizationRequestOpts: CreateAuthorizationRequestOpts): Promise { assertValidAuthorizationRequestOpts(authorizationRequestOpts) const createJwtCallback = authorizationRequestOpts.requestObject.createJwtCallback // We copy the signature separately as it can contain a function, which would be removed in the merge function below - const jwtIssuer = authorizationRequestOpts.requestObject.jwtIssuer // We copy the signature separately as it can contain a function, which would be removed in the merge function below - const requestObjectOpts = RequestObject.mergeOAuth2AndOpenIdProperties(authorizationRequestOpts) + const jwtIssuer: JwtIssuer = authorizationRequestOpts.requestObject.jwtIssuer // We copy the signature separately as it can contain a function, which would be removed in the merge function below + const requestObjectOpts: RequestObjectOpts = RequestObject.mergeOAuth2AndOpenIdProperties(authorizationRequestOpts) const mergedOpts = { ...authorizationRequestOpts, requestObject: { ...authorizationRequestOpts.requestObject, ...requestObjectOpts, createJwtCallback, jwtIssuer }, @@ -49,17 +49,17 @@ export class RequestObject { return new RequestObject(mergedOpts, await createRequestObjectPayload(mergedOpts)) } - public static async fromJwt(requestObjectJwt: RequestObjectJwt) { + public static async fromJwt(requestObjectJwt: RequestObjectJwt): Promise { return requestObjectJwt ? new RequestObject(undefined, undefined, requestObjectJwt) : undefined } - public static async fromPayload(requestObjectPayload: RequestObjectPayload, authorizationRequestOpts: CreateAuthorizationRequestOpts) { + public static async fromPayload(requestObjectPayload: RequestObjectPayload, authorizationRequestOpts: CreateAuthorizationRequestOpts): Promise { return new RequestObject(authorizationRequestOpts, requestObjectPayload) } public static async fromAuthorizationRequestPayload(payload: AuthorizationRequestPayload): Promise { const requestObjectJwt = - payload.request || payload.request_uri ? await fetchByReferenceOrUseByValue(payload.request_uri, payload.request, true) : undefined + payload.request ?? payload.request_uri ? await fetchByReferenceOrUseByValue(payload.request_uri as string, payload.request, true) : undefined return requestObjectJwt ? await RequestObject.fromJwt(requestObjectJwt) : undefined } diff --git a/packages/siop-oid4vp/lib/rp/Opts.ts b/packages/siop-oid4vp/lib/rp/Opts.ts index 952bcddd..2a4ef403 100644 --- a/packages/siop-oid4vp/lib/rp/Opts.ts +++ b/packages/siop-oid4vp/lib/rp/Opts.ts @@ -48,7 +48,7 @@ export const createRequestOptsFromBuilderOrExistingOpts = (opts: { builder?: RPB return createRequestOpts } -export const createVerifyResponseOptsFromBuilderOrExistingOpts = (opts: { builder?: RPBuilder; verifyOpts?: VerifyAuthorizationResponseOpts }) => { +export const createVerifyResponseOptsFromBuilderOrExistingOpts = (opts: { builder?: RPBuilder; verifyOpts?: VerifyAuthorizationResponseOpts }): Partial => { return opts.builder ? { hasher: opts.builder.hasher ?? defaultHasher, diff --git a/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts b/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts index 8026b5eb..c6dac39d 100644 --- a/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts +++ b/packages/siop-oid4vp/lib/schemas/AuthorizationResponseOpts.schema.ts @@ -1953,6 +1953,9 @@ export const AuthorizationResponseOptsSchemaObj = { "jwt": { "type": "string" }, + "mso_mdoc": { + "type": "string" + }, "nonce": { "type": "string" }, @@ -1983,7 +1986,9 @@ export const AuthorizationResponseOptsSchemaObj = { "JcsEd25519Signature2020", "BbsBlsSignatureProof2020", "BbsBlsBoundSignatureProof2020", - "JwtProof2020" + "JwtProof2020", + "SdJwtProof2024", + "MsoMdocProof2024" ] }, "IProofPurpose": { diff --git a/packages/siop-oid4vp/lib/types/Errors.ts b/packages/siop-oid4vp/lib/types/Errors.ts index f8f1a242..4bc763ff 100644 --- a/packages/siop-oid4vp/lib/types/Errors.ts +++ b/packages/siop-oid4vp/lib/types/Errors.ts @@ -51,6 +51,7 @@ enum SIOPErrors { VERIFY_BAD_PARAMS = 'Verify bad parameters', VERIFIABLE_PRESENTATION_SIGNATURE_NOT_VALID = 'The signature of the verifiable presentation is not valid', VERIFIABLE_PRESENTATION_VERIFICATION_FUNCTION_MISSING = 'The verifiable presentation verification function is missing', + PRESENTATION_SUBMISSION_DEFINITION_ID_DOES_NOT_MATCHING_DEFINITION_ID = "The 'definition_id' in the presentation submission does not match the id of the presentation definition.", } export default SIOPErrors diff --git a/packages/siop-oid4vp/lib/types/Events.ts b/packages/siop-oid4vp/lib/types/Events.ts index ec96e177..e3afa08b 100644 --- a/packages/siop-oid4vp/lib/types/Events.ts +++ b/packages/siop-oid4vp/lib/types/Events.ts @@ -38,7 +38,7 @@ export class AuthorizationEvent { this._error = args.error } - get subject(): T { + get subject(): T | undefined { return this._subject } @@ -46,7 +46,7 @@ export class AuthorizationEvent { return this._timestamp } - get error(): Error { + get error(): Error | undefined { return this._error } diff --git a/packages/siop-oid4vp/lib/types/SIOP.types.ts b/packages/siop-oid4vp/lib/types/SIOP.types.ts index 7ded6111..46eb7e2a 100644 --- a/packages/siop-oid4vp/lib/types/SIOP.types.ts +++ b/packages/siop-oid4vp/lib/types/SIOP.types.ts @@ -208,9 +208,9 @@ export interface RequestStateInfo { client_id: string // RP ID // sub: string - nonce: string - state: string - iat: number + nonce?: string + state?: string + iat?: number } interface DiscoveryMetadataCommonOpts { diff --git a/packages/siop-oid4vp/lib/types/VpJwtVerifier.ts b/packages/siop-oid4vp/lib/types/VpJwtVerifier.ts index 4982094b..e671b253 100644 --- a/packages/siop-oid4vp/lib/types/VpJwtVerifier.ts +++ b/packages/siop-oid4vp/lib/types/VpJwtVerifier.ts @@ -96,8 +96,9 @@ export const getRequestObjectJwtVerifier = async ( if (jwt.payload.redirect_uri && jwt.payload.redirect_uri !== clientId) { throw new Error(SIOPErrors.INVALID_CLIENT_ID_MUST_MATCH_REDIRECT_URI) } - if (options.raw.split('.').length > 2) { - throw new Error(`${SIOPErrors.INVALID_JWT} '${type}' JWT must not not be signed.`) + const parts = options.raw.split('.') + if (parts.length > 2 && parts[2]) { + throw new Error(`${SIOPErrors.INVALID_JWT} '${type}' JWT must not be signed`) } return getJwtVerifierWithContext(jwt, { type }) } else if (clientIdScheme === 'verifier_attestation') { @@ -121,7 +122,9 @@ export const getRequestObjectJwtVerifier = async ( !attestationPayload.exp || typeof attestationPayload.exp !== 'number' || typeof attestationPayload.cnf !== 'object' || - typeof attestationPayload.cnf['jwk'] !== 'object' + !attestationPayload.cnf || + (!('jwk' in attestationPayload.cnf) + || typeof attestationPayload.cnf['jwk'] !== 'object') ) { throw new Error(SIOPErrors.BAD_VERIFIER_ATTESTATION) } @@ -144,7 +147,7 @@ export const getRequestObjectJwtVerifier = async ( } // The iss claim value of the Verifier Attestation JWT MUST identify a party the Wallet trusts for issuing Verifier Attestation JWTs. // If the Wallet cannot establish trust, it MUST refuse the request. - return { method: 'jwk', type, jwk: attestationPayload.cnf['jwk'] as JWK, alg: jwk.alg ?? attestationHeader.alg } + return { method: 'jwk', type, jwk: attestationPayload.cnf['jwk'] as JWK, alg } } else if (clientIdScheme === 'entity_id') { if (!clientId.startsWith('http')) { throw new Error(SIOPErrors.INVALID_REQUEST_OBJECT_ENTITY_ID_SCHEME_CLIENT_ID) diff --git a/packages/siop-oid4vp/package.json b/packages/siop-oid4vp/package.json index 2440b9b9..7546fce7 100644 --- a/packages/siop-oid4vp/package.json +++ b/packages/siop-oid4vp/package.json @@ -18,10 +18,10 @@ "@protokoll/jarm": "^0.2.7", "@sphereon/did-uni-client": "^0.6.2", "@sphereon/oid4vc-common": "workspace:*", - "@sphereon/pex": "^4.1.0", + "@sphereon/pex": "5.0.0-unstable.2", "@sphereon/pex-models": "^2.3.1", "@sphereon/kmp-mdl-mdoc": "0.2.0-SNAPSHOT.22", - "@sphereon/ssi-types": "0.22.0", + "@sphereon/ssi-types": "0.29.1-unstable.121", "@sphereon/wellknown-dids-client": "^0.1.3", "cross-fetch": "^4.0.0", "debug": "^4.3.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0b4f9e9..540cf8c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -448,14 +448,14 @@ importers: specifier: workspace:* version: link:../common '@sphereon/pex': - specifier: ^4.1.0 - version: 4.1.0 + specifier: 5.0.0-unstable.2 + version: 5.0.0-unstable.2(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) '@sphereon/pex-models': specifier: ^2.3.1 version: 2.3.1 '@sphereon/ssi-types': - specifier: 0.22.0 - version: 0.22.0 + specifier: 0.29.1-unstable.121 + version: 0.29.1-unstable.121(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) '@sphereon/wellknown-dids-client': specifier: ^0.1.3 version: 0.1.3(encoding@0.1.13) @@ -1863,7 +1863,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {node: '>=0.10.0'} + engines: {'0': node >=0.10.0} '@expo/cli@0.7.3': resolution: {integrity: sha512-uMGHbAhApqXR2sd1KPhgvpbOhBBnspad8msEqHleT2PHXwKIwTUDzBGO9+jdOAWwCx2MJfw3+asYjzoD3DN9Bg==} @@ -2533,12 +2533,13 @@ packages: '@sphereon/kmp-mdl-mdoc@0.2.0-SNAPSHOT.22': resolution: {integrity: sha512-uAZZExVy+ug9JLircejWa5eLtAZ7bnBP6xb7DO2+86LRsHNLh2k2jMWJYxp+iWtGHTsh6RYsZl14ScQLvjiQ/A==} + bundledDependencies: [] '@sphereon/pex-models@2.3.1': resolution: {integrity: sha512-SByU4cJ0XYA6VZQ/L6lsSiRcFtBPHbFioCeQ4GP7/W/jQ+PSBD7uK2oTnKQ9/0iEiMK/6JYqhKgLs4a9UX3UTQ==} - '@sphereon/pex@4.1.0': - resolution: {integrity: sha512-FVaaStVW8ewiAVfbpLmjRuAmEA/7xwifY5m1r6RaignMpPLK3uBt+Urae/Y78lcuJttj7WvRMzddgNO3RtsO+Q==} + '@sphereon/pex@5.0.0-unstable.2': + resolution: {integrity: sha512-mA6lY/OBKKzsh4Jf4btm9Tj4ymVsX6xuVATn85LurD4bt3fhZwNJMkxhFy4tT/QyAtp05E4aaEq0wTVvOjVa7w==} engines: {node: '>=18'} '@sphereon/ssi-express-support@0.29.1-unstable.208': @@ -2555,11 +2556,38 @@ packages: passport-http-bearer: optional: true + '@sphereon/ssi-sdk-ext.did-utils@0.24.1-unstable.112': + resolution: {integrity: sha512-nc0jFPOWg0H20S8m83aQUpNym0Wx0rJCGkgpH6GdK8gBtgza8Y9DvAap1AYZug18WbqPcF6rBjvtIJqAKsSvlQ==} + + '@sphereon/ssi-sdk-ext.identifier-resolution@0.24.1-unstable.112': + resolution: {integrity: sha512-VBkJjHokFNsQ0wsHUbyCysMuShTOEuK6yrvyW64uOFcB2hzq1J/wi9CewI+YRHv7mnejBlu46uYNycvOKKRcsQ==} + + '@sphereon/ssi-sdk-ext.jwt-service@0.24.1-unstable.112': + resolution: {integrity: sha512-OrBaSg5wLSehkJ4MyuyDWKD4CRIBERnJqRT0o/y5DbaCF3k02+/lN/rWP+4qwk2w192fIEAExG4L2GwZM/5PLQ==} + + '@sphereon/ssi-sdk-ext.key-manager@0.24.1-unstable.112': + resolution: {integrity: sha512-XdXV4qj+BYTZWyGHduWQxl0mxCYt5CF0Q93p4Thbm2/hjfaAC6aJi2WAXFGTIri95QVbKW1Uscob0CjNCVkWdg==} + '@sphereon/ssi-sdk-ext.key-utils@0.23.0': resolution: {integrity: sha512-BfULXvQmcUrBq2DqYxJHKnEoB2d5icu3TJ9GP2aP1WybSULTjL96Wv5r7QKgktcodKaL+F+oQ7r8sC9qBl1exw==} - '@sphereon/ssi-types@0.22.0': - resolution: {integrity: sha512-YPJAZlKmzNALXK8ohP3ETxj1oVzL4+M9ljj3fD5xrbacvYax1JPCVKc8BWSubGcQckKHPbgbpcS7LYEeghyT9Q==} + '@sphereon/ssi-sdk-ext.key-utils@0.24.1-unstable.112': + resolution: {integrity: sha512-er6TwGUWxlao2lSP97r1DTFlUXcPSMsIToULOWQJp6wKbvCuvV6pN5luS0qKB/W0/TOUE5kXzFwNx086BPnPRA==} + + '@sphereon/ssi-sdk-ext.x509-utils@0.24.1-unstable.112': + resolution: {integrity: sha512-bbx2jFoqWhW/xYABVwg3HiUo15yztPt3s+9bJtdB8n4PCjin4Nq3+vFvaHsmu70yAGkbWfsBcBVW6Y3oFtvpAA==} + + '@sphereon/ssi-sdk.agent-config@0.29.1-unstable.161': + resolution: {integrity: sha512-ZP/TjapF/Gv/AwnNr9e1U3rjyRwdLtAj4un9j1csnKcgYe9ff2fhYbe06y9mU4tfQilH69mAW4Tz1t6N5U7XbA==} + + '@sphereon/ssi-sdk.core@0.29.1-unstable.161': + resolution: {integrity: sha512-3E/KsjTywT9BzP5bMi41JVTu9nTiu2ekwNSPobF9tAJnHJv+LkjCJ59xA8jtbq/Xe4iq3xRMU17yBvpZXN2W4A==} + + '@sphereon/ssi-types@0.29.1-unstable.121': + resolution: {integrity: sha512-g5qt2cRa/9t3WrBWMneGOrwjyU2Mqdh4LlKeO6japli5bgl5D6lim91F++m1iIIhOYP4dqPpk5PLjy9XQlLyEw==} + + '@sphereon/ssi-types@0.29.1-unstable.161': + resolution: {integrity: sha512-ifMADjk6k0f97/isK/4Qw/PX6n4k+qS5k6mmmH47MTD3KMDddVghoXycsvNw7wObJdLUalHBX630ghr+u21oMg==} '@sphereon/ssi-types@0.29.1-unstable.208': resolution: {integrity: sha512-3YAFzy//BojsYN+RYoEjndWP3w5a8a3qRZi5dS0Gh6s4yMCiykqTJM1agJVeoaLce8JxFFaCWSpkzwbmJYGTaQ==} @@ -2570,6 +2598,9 @@ packages: '@sphereon/wellknown-dids-client@0.1.3': resolution: {integrity: sha512-TAT24L3RoXD8ocrkTcsz7HuJmgjNjdoV6IXP1p3DdaI/GqkynytXE3J1+F7vUFMRYwY5nW2RaXSgDQhrFJemaA==} + '@sqltools/formatter@1.2.5': + resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + '@stablelib/aead@1.0.1': resolution: {integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==} @@ -2754,6 +2785,9 @@ packages: resolution: {integrity: sha512-y4lPzk/SY/Cy1dUCa17ES3kqvShNQwevTO16dvbuevu6YcTYBAdSCYvW9JL+ppFqPYI5NSDPUwT6kkd4wNWmsA==} engines: {node: '>=16'} + '@trust/keyto@1.0.1': + resolution: {integrity: sha512-OXTmKkrnkwktCX86XA7eWs1TQ6u64enm0syzAfNhjigbuGLy5aLhbhRYWtjt4zzdG/irWudluheRZ9Ic9pCwsA==} + '@trust/keyto@2.0.0-alpha1': resolution: {integrity: sha512-VmlOa+nOaDzhEUfprnVp7RxFQyuEwA4fJ5+smnsud5WM01gU16yQnO/ejZnDVMGXuq/sUwTa5pCej4JhkKA5Sg==} @@ -2995,6 +3029,12 @@ packages: '@veramo/core@4.2.0': resolution: {integrity: sha512-HIqbXfCbwOAJelR5Ohsm22vr63cy6ND8Ua/+9wfMDAiymUUS7NryaJ/v6NRtnmIrNZqUMDdR9/TWdp4cCq5eBg==} + '@veramo/key-manager@4.2.0': + resolution: {integrity: sha512-v/swPrxxI155iFxWjcJDmeyfMLOnAu/VRxJJE+cv8Ld9mmPi5xljaoO9/ozt0j4Cz92n6lFKqfVOxs2ECV85UA==} + + '@veramo/utils@4.2.0': + resolution: {integrity: sha512-jHkli0Qz9rFsWzPAdfJP3P2MFxvVMZPDXZvtVBm8x1fjAGrw/Htz/c5drhDAeBXnqPd9011/7cyvp6AOvdbc8Q==} + '@xmldom/xmldom@0.7.13': resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==} engines: {node: '>=10.0.0'} @@ -3141,6 +3181,10 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + app-root-path@3.1.0: + resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} + engines: {node: '>= 6.0.0'} + appdirsjs@1.2.7: resolution: {integrity: sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==} @@ -3238,6 +3282,9 @@ packages: asmcrypto.js@2.3.2: resolution: {integrity: sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA==} + asn1.js-rfc5280@3.0.0: + resolution: {integrity: sha512-Y2LZPOWeZ6qehv698ZgOGGCZXBQShObWnGthTrIFlIQjuV1gg2B8QOhWFRExq/MR1VnPpIIe7P9vX2vElxv+Pg==} + asn1.js@5.4.1: resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} @@ -3403,6 +3450,9 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + blakejs@1.2.1: + resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} @@ -3506,6 +3556,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + cacache@15.3.0: resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} engines: {node: '>= 10'} @@ -4153,6 +4207,9 @@ packages: deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -5171,6 +5228,11 @@ packages: engines: {node: '>=4.0'} hasBin: true + image-size@2.0.0-beta.2: + resolution: {integrity: sha512-1nDNnVxJixMWBynFgQ1q8+aVqK60TiNHpMyFAXt9xpzGZV+2lHI1IXjgdcAjBxPc4nx2ed1NdYs2I+Zfq+Zn7w==} + engines: {node: '>=18.18.0'} + hasBin: true + import-fresh@2.0.0: resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} engines: {node: '>=4'} @@ -5701,12 +5763,45 @@ packages: js-binary-schema-parser@2.0.3: resolution: {integrity: sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==} + js-crypto-aes@1.0.6: + resolution: {integrity: sha512-E2hu9z5+YtpDg9Un/bDfmH+I5dv/8aN+ozxv9L0ybZldcQ9T5iYDbBKdlKGBUKI3IvzoWSBSdnZnhwZaRIN46w==} + + js-crypto-ec@1.0.7: + resolution: {integrity: sha512-vou6cW3wGAQ75RzS++I/rthELPFp0nhHCmaAKQvdhwD480Q3FltLgyNkTMgcLTdN+Ghj8BRU1/+3oIEIOOK/MA==} + + js-crypto-env@1.0.5: + resolution: {integrity: sha512-8/UNN3sG8J+yMzqwSNVaobaWhIz4MqZFoOg5OB0DFXqS8eFjj2YvdmLJqIWXPl57Yw10SvYx0DQOtkfsWIV9Aw==} + + js-crypto-hash@1.0.7: + resolution: {integrity: sha512-GdbcVKjplbXJdR9oF2ks8+sBCLD7BUZ144Bc+Ie8OJuBHSIiHyMzdg2eD+ZYf87awTsKckNn1xIv+31+V2ewcA==} + + js-crypto-hmac@1.0.7: + resolution: {integrity: sha512-OVn2wjAuOV7ToQYvRKY2VoElCHoRW7BepycPPuH73xbLygDczkef41YsXMpKLnVAyS5kdwMJQy9qlMR9touHTg==} + + js-crypto-key-utils@1.0.7: + resolution: {integrity: sha512-8/y/hpKevnAgr5EXz2x4IXMfqjzYZAzzXXc9OnAyI5JNdUtAufJkGfwlmZ+o40lTHv3k1egCiP/6pG/dZiqiEA==} + + js-crypto-pbkdf@1.0.7: + resolution: {integrity: sha512-FGs1PZeqGWM8k8k5JlAhHbBhLYtls+iVmeJEC22DUJ98Q3qo9Ki4cu3i0oxhjA2VpZ8V4MmV1DJHDTFYY4iOwg==} + + js-crypto-random@1.0.5: + resolution: {integrity: sha512-WydEQ5rrWLzgSkX1QNsuGinkv7z57UkYnDGo5f5oGtBe9QeUWUehdmPNNG4a4Sf8xGkjZBOhKaZqT1ACnyYCBA==} + + js-crypto-rsa@1.0.7: + resolution: {integrity: sha512-HLBCWNGzuUZMNbZ3nndrVAqth1m1mvuCO4tW7PpBDn4nsdLSnPnPd+SA7NvjsufWry38DnZdpFrK2gqbsrksGw==} + + js-encoding-utils@0.7.3: + resolution: {integrity: sha512-cfjcyPOzkZ2esMAi6eAjuto7GiT6YpPan5xIeQyN/CFqFHTt1sdqP0PJPgzi3HqAqXKN9j9hduynkgwk+AAJOw==} + js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-x509-utils@1.0.7: + resolution: {integrity: sha512-IDB3CtWyvkNJVbDPZvzM9o3Y6CyzDiMls6R23ZPwfmHHil7nRrpLxtA098SENhqjv1t/6WTeeCKQ5dhIMOGiUw==} + js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -5820,6 +5915,10 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + just-diff-apply@5.5.0: resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} @@ -6332,6 +6431,11 @@ packages: engines: {node: '>=10'} hasBin: true + mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} + engines: {node: '>=10'} + hasBin: true + mkdirp@3.0.1: resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} engines: {node: '>=10'} @@ -6366,6 +6470,9 @@ packages: resolution: {integrity: sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==} engines: {node: '>=16.0.0', npm: '>=7.0.0'} + multiformats@9.7.1: + resolution: {integrity: sha512-TaVmGEBt0fhxiNJMGphBfB+oGvUxFs8KgGvgl8d3C+GWtrFcvXdJ2196eg+dYhmSFClmgFfSfJEklo+SZzdNuw==} + multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} @@ -6945,6 +7052,10 @@ packages: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} + pkijs@3.2.4: + resolution: {integrity: sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg==} + engines: {node: '>=12.0.0'} + plist@3.1.0: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} @@ -7240,6 +7351,9 @@ packages: resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==} engines: {node: '>=6'} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + regenerate-unicode-properties@10.1.1: resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} engines: {node: '>=4'} @@ -7511,6 +7625,9 @@ packages: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true + sha3@2.1.4: + resolution: {integrity: sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==} + shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -8163,6 +8280,64 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + typeorm@0.3.20: + resolution: {integrity: sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==} + engines: {node: '>=16.13.0'} + hasBin: true + peerDependencies: + '@google-cloud/spanner': ^5.18.0 + '@sap/hana-client': ^2.12.25 + better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0 + hdb-pool: ^0.1.6 + ioredis: ^5.0.4 + mongodb: ^5.8.0 + mssql: ^9.1.1 || ^10.0.1 + mysql2: ^2.2.5 || ^3.0.1 + oracledb: ^6.3.0 + pg: ^8.5.1 + pg-native: ^3.0.0 + pg-query-stream: ^4.0.0 + redis: ^3.1.1 || ^4.0.0 + sql.js: ^1.4.0 + sqlite3: ^5.0.3 + ts-node: ^10.7.0 + typeorm-aurora-data-api-driver: ^2.0.0 + peerDependenciesMeta: + '@google-cloud/spanner': + optional: true + '@sap/hana-client': + optional: true + better-sqlite3: + optional: true + hdb-pool: + optional: true + ioredis: + optional: true + mongodb: + optional: true + mssql: + optional: true + mysql2: + optional: true + oracledb: + optional: true + pg: + optional: true + pg-native: + optional: true + pg-query-stream: + optional: true + redis: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + ts-node: + optional: true + typeorm-aurora-data-api-driver: + optional: true + typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -8611,6 +8786,11 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -11849,20 +12029,40 @@ snapshots: '@sphereon/pex-models@2.3.1': {} - '@sphereon/pex@4.1.0': + '@sphereon/pex@5.0.0-unstable.2(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5))': dependencies: '@astronautlabs/jsonpath': 1.1.2 '@sd-jwt/decode': 0.6.1 '@sd-jwt/present': 0.6.1 '@sd-jwt/types': 0.6.1 '@sphereon/pex-models': 2.3.1 - '@sphereon/ssi-types': 0.22.0 + '@sphereon/ssi-types': 0.29.1-unstable.121(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) ajv: 8.17.1 ajv-formats: 2.1.1(ajv@8.17.1) jwt-decode: 3.1.2 nanoid: 3.3.7 string.prototype.matchall: 4.0.11 uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver '@sphereon/ssi-express-support@0.29.1-unstable.208': dependencies: @@ -11882,6 +12082,120 @@ snapshots: transitivePeerDependencies: - supports-color + '@sphereon/ssi-sdk-ext.did-utils@0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5))': + dependencies: + '@ethersproject/networks': 5.7.1 + '@ethersproject/transactions': 5.7.0 + '@sphereon/did-uni-client': 0.6.3(encoding@0.1.13) + '@sphereon/ssi-sdk-ext.key-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk-ext.x509-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk.agent-config': 0.29.1-unstable.161(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) + '@sphereon/ssi-sdk.core': 0.29.1-unstable.161(encoding@0.1.13) + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@stablelib/ed25519': 1.0.3 + '@veramo/core': 4.2.0 + '@veramo/utils': 4.2.0(encoding@0.1.13) + did-jwt: 6.11.6 + did-resolver: 4.1.0 + elliptic: 6.5.7 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-sdk-ext.identifier-resolution@0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5))': + dependencies: + '@sphereon/ssi-sdk-ext.did-utils': 0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) + '@sphereon/ssi-sdk-ext.key-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk-ext.x509-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk.agent-config': 0.29.1-unstable.161(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@veramo/core': 4.2.0 + '@veramo/utils': 4.2.0(encoding@0.1.13) + debug: 4.3.7 + pkijs: 3.2.4 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-sdk-ext.jwt-service@0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5))': + dependencies: + '@sphereon/ssi-sdk-ext.did-utils': 0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) + '@sphereon/ssi-sdk-ext.identifier-resolution': 0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) + '@sphereon/ssi-sdk-ext.key-manager': 0.24.1-unstable.112 + '@sphereon/ssi-sdk-ext.key-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk-ext.x509-utils': 0.24.1-unstable.112 + '@sphereon/ssi-sdk.agent-config': 0.29.1-unstable.161(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@veramo/core': 4.2.0 + '@veramo/utils': 4.2.0(encoding@0.1.13) + debug: 4.3.7 + jwt-decode: 4.0.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-sdk-ext.key-manager@0.24.1-unstable.112': + dependencies: + '@veramo/core': 4.2.0 + '@veramo/key-manager': 4.2.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - supports-color + '@sphereon/ssi-sdk-ext.key-utils@0.23.0(expo@48.0.21(@babel/core@7.25.2)(encoding@0.1.13))(msrcrypto@1.5.8)(react-native-securerandom@1.0.1)': dependencies: '@ethersproject/random': 5.7.0 @@ -11907,10 +12221,115 @@ snapshots: - react-native-securerandom - supports-color - '@sphereon/ssi-types@0.22.0': + '@sphereon/ssi-sdk-ext.key-utils@0.24.1-unstable.112': + dependencies: + '@ethersproject/random': 5.7.0 + '@sphereon/ssi-sdk-ext.x509-utils': 0.24.1-unstable.112 + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@stablelib/ed25519': 1.0.3 + '@stablelib/sha256': 1.0.1 + '@stablelib/sha512': 1.0.1 + '@trust/keyto': 1.0.1 + '@veramo/core': 4.2.0 + base64url: 3.0.1 + debug: 4.3.7 + did-resolver: 4.1.0 + elliptic: 6.5.7 + lodash.isplainobject: 4.0.6 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + varint: 6.0.0 + web-encoding: 1.1.5 + transitivePeerDependencies: + - supports-color + + '@sphereon/ssi-sdk-ext.x509-utils@0.24.1-unstable.112': + dependencies: + '@trust/keyto': 1.0.1 + debug: 4.3.7 + js-x509-utils: 1.0.7 + pkijs: 3.2.4 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@sphereon/ssi-sdk.agent-config@0.29.1-unstable.161(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5))': + dependencies: + '@veramo/core': 4.2.0 + debug: 4.3.7 + jsonpointer: 5.0.1 + typeorm: 0.3.20(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) + url-parse: 1.5.10 + yaml: 2.5.1 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-sdk.core@0.29.1-unstable.161(encoding@0.1.13)': + dependencies: + '@sphereon/ssi-types': 0.29.1-unstable.161 + '@veramo/core': 4.2.0 + cross-fetch: 3.1.8(encoding@0.1.13) + debug: 4.3.7 + image-size: 2.0.0-beta.2 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@sphereon/ssi-types@0.29.1-unstable.121(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5))': + dependencies: + '@sd-jwt/decode': 0.6.1 + '@sphereon/kmp-mdl-mdoc': 0.2.0-SNAPSHOT.22 + '@sphereon/ssi-sdk-ext.jwt-service': 0.24.1-unstable.112(encoding@0.1.13)(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)) + debug: 4.3.7 + events: 3.3.0 + jwt-decode: 3.1.2 + transitivePeerDependencies: + - '@google-cloud/spanner' + - '@sap/hana-client' + - better-sqlite3 + - encoding + - hdb-pool + - ioredis + - mongodb + - mssql + - mysql2 + - oracledb + - pg + - pg-native + - pg-query-stream + - redis + - sql.js + - sqlite3 + - supports-color + - ts-node + - typeorm-aurora-data-api-driver + + '@sphereon/ssi-types@0.29.1-unstable.161': dependencies: '@sd-jwt/decode': 0.6.1 + debug: 4.3.7 + events: 3.3.0 jwt-decode: 3.1.2 + transitivePeerDependencies: + - supports-color '@sphereon/ssi-types@0.29.1-unstable.208': dependencies: @@ -11934,6 +12353,8 @@ snapshots: transitivePeerDependencies: - encoding + '@sqltools/formatter@1.2.5': {} + '@stablelib/aead@1.0.1': {} '@stablelib/aes-kw@1.0.1': @@ -12238,6 +12659,12 @@ snapshots: '@stablelib/x25519': 1.0.3 '@transmute/ld-key-pair': 0.7.0-unstable.82 + '@trust/keyto@1.0.1': + dependencies: + asn1.js: 5.4.1 + base64url: 3.0.1 + elliptic: 6.5.7 + '@trust/keyto@2.0.0-alpha1': dependencies: asn1.js: 5.4.1 @@ -12611,6 +13038,37 @@ snapshots: transitivePeerDependencies: - supports-color + '@veramo/key-manager@4.2.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@stablelib/ed25519': 1.0.3 + '@veramo/core': 4.2.0 + did-jwt: 6.11.6 + uint8arrays: 3.1.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + '@veramo/utils@4.2.0(encoding@0.1.13)': + dependencies: + '@ethersproject/transactions': 5.7.0 + '@stablelib/ed25519': 1.0.3 + '@veramo/core': 4.2.0 + blakejs: 1.2.1 + cross-fetch: 3.1.8(encoding@0.1.13) + debug: 4.3.7 + did-jwt: 6.11.6 + did-jwt-vc: 3.2.15 + did-resolver: 4.1.0 + elliptic: 6.5.7 + multiformats: 9.7.1 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + '@xmldom/xmldom@0.7.13': {} '@xmldom/xmldom@0.8.10': {} @@ -12743,6 +13201,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + app-root-path@3.1.0: {} + appdirsjs@1.2.7: {} application-config-path@0.1.1: {} @@ -12840,6 +13300,10 @@ snapshots: asmcrypto.js@2.3.2: {} + asn1.js-rfc5280@3.0.0: + dependencies: + asn1.js: 5.4.1 + asn1.js@5.4.1: dependencies: bn.js: 4.12.0 @@ -13090,6 +13554,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + blakejs@1.2.1: {} + blueimp-md5@2.19.0: {} bn.js@4.12.0: {} @@ -13206,6 +13672,8 @@ snapshots: bytes@3.1.2: {} + bytestreamjs@2.0.1: {} + cacache@15.3.0: dependencies: '@npmcli/fs': 1.1.1 @@ -13980,6 +14448,11 @@ snapshots: deprecation@2.3.1: {} + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + destroy@1.2.0: {} detect-indent@5.0.0: {} @@ -15367,6 +15840,8 @@ snapshots: image-size@0.6.3: {} + image-size@2.0.0-beta.2: {} + import-fresh@2.0.0: dependencies: caller-path: 2.0.0 @@ -16161,10 +16636,87 @@ snapshots: js-binary-schema-parser@2.0.3: {} + js-crypto-aes@1.0.6: + dependencies: + js-crypto-env: 1.0.5 + + js-crypto-ec@1.0.7: + dependencies: + asn1.js: 5.4.1 + buffer: 6.0.3 + elliptic: 6.5.7 + js-crypto-env: 1.0.5 + js-crypto-hash: 1.0.7 + js-crypto-key-utils: 1.0.7 + js-crypto-random: 1.0.5 + js-encoding-utils: 0.7.3 + + js-crypto-env@1.0.5: {} + + js-crypto-hash@1.0.7: + dependencies: + buffer: 6.0.3 + hash.js: 1.1.7 + js-crypto-env: 1.0.5 + md5: 2.3.0 + sha3: 2.1.4 + + js-crypto-hmac@1.0.7: + dependencies: + js-crypto-env: 1.0.5 + js-crypto-hash: 1.0.7 + + js-crypto-key-utils@1.0.7: + dependencies: + asn1.js: 5.4.1 + buffer: 6.0.3 + des.js: 1.1.0 + elliptic: 6.5.7 + js-crypto-aes: 1.0.6 + js-crypto-hash: 1.0.7 + js-crypto-pbkdf: 1.0.7 + js-crypto-random: 1.0.5 + js-encoding-utils: 0.7.3 + lodash.clonedeep: 4.5.0 + + js-crypto-pbkdf@1.0.7: + dependencies: + js-crypto-hash: 1.0.7 + js-crypto-hmac: 1.0.7 + js-encoding-utils: 0.7.3 + + js-crypto-random@1.0.5: + dependencies: + js-crypto-env: 1.0.5 + + js-crypto-rsa@1.0.7: + dependencies: + bn.js: 5.2.1 + buffer: 6.0.3 + js-crypto-env: 1.0.5 + js-crypto-hash: 1.0.7 + js-crypto-key-utils: 1.0.7 + js-crypto-random: 1.0.5 + js-encoding-utils: 0.7.3 + + js-encoding-utils@0.7.3: {} + js-sha3@0.8.0: {} js-tokens@4.0.0: {} + js-x509-utils@1.0.7: + dependencies: + asn1.js: 5.4.1 + asn1.js-rfc5280: 3.0.0 + bn.js: 5.2.1 + buffer: 6.0.3 + js-crypto-ec: 1.0.7 + js-crypto-key-utils: 1.0.7 + js-crypto-random: 1.0.5 + js-crypto-rsa: 1.0.7 + js-encoding-utils: 0.7.3 + js-yaml@3.14.1: dependencies: argparse: 1.0.10 @@ -16304,6 +16856,8 @@ snapshots: jsonparse@1.3.1: {} + jsonpointer@5.0.1: {} + just-diff-apply@5.5.0: {} just-diff@6.0.2: {} @@ -17160,6 +17714,8 @@ snapshots: mkdirp@1.0.4: {} + mkdirp@2.1.6: {} + mkdirp@3.0.1: {} modify-values@1.0.1: {} @@ -17188,6 +17744,8 @@ snapshots: multiformats@12.1.3: {} + multiformats@9.7.1: {} + multiformats@9.9.0: {} multimatch@5.0.0: @@ -17864,6 +18422,15 @@ snapshots: dependencies: find-up: 3.0.0 + pkijs@3.2.4: + dependencies: + '@noble/hashes': 1.5.0 + asn1js: 3.0.5 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.5 + pvutils: 1.1.3 + tslib: 2.7.0 + plist@3.1.0: dependencies: '@xmldom/xmldom': 0.8.10 @@ -18208,6 +18775,8 @@ snapshots: reduce-flatten@2.0.0: optional: true + reflect-metadata@0.2.2: {} + regenerate-unicode-properties@10.1.1: dependencies: regenerate: 1.4.2 @@ -18493,6 +19062,10 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + sha3@2.1.4: + dependencies: + buffer: 6.0.3 + shallow-clone@3.0.1: dependencies: kind-of: 6.0.3 @@ -19237,6 +19810,28 @@ snapshots: typedarray@0.0.6: {} + typeorm@0.3.20(ts-node@10.9.2(@types/node@18.19.50)(typescript@5.4.5)): + dependencies: + '@sqltools/formatter': 1.2.5 + app-root-path: 3.1.0 + buffer: 6.0.3 + chalk: 4.1.2 + cli-highlight: 2.1.11 + dayjs: 1.11.13 + debug: 4.3.7 + dotenv: 16.4.5 + glob: 10.4.5 + mkdirp: 2.1.6 + reflect-metadata: 0.2.2 + sha.js: 2.4.11 + tslib: 2.7.0 + uuid: 9.0.1 + yargs: 17.7.2 + optionalDependencies: + ts-node: 10.9.2(@types/node@18.19.50)(typescript@5.4.5) + transitivePeerDependencies: + - supports-color + typescript@5.3.3: {} typescript@5.4.5: {} @@ -19609,6 +20204,8 @@ snapshots: yallist@4.0.0: {} + yaml@2.5.1: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1