From 9cd97fe635da19ac1d5b7c251bdf14840c2748d4 Mon Sep 17 00:00:00 2001 From: sksadjad Date: Mon, 22 Apr 2024 07:50:35 +0200 Subject: [PATCH] feat: added the schema version SIOPv2_D13_OID4VP_D20 implementing to oid4vp01_0_20 --- generator/schemaGenerator.ts | 10 +- package.json | 2 + .../AuthorizationRequest.ts | 75 +- src/helpers/SIOPSpecVersion.ts | 8 +- src/id-token/Payload.ts | 2 +- src/op/OP.ts | 2 +- ...ationRequestPayloadVD12OID4VPD18.schema.ts | 1059 ----------------- src/types/Errors.ts | 6 + src/types/JWT.types.ts | 1 + src/types/SIOP.types.ts | 8 +- test/e2e/mattr.launchpad.spec.ts | 2 +- test/interop/EBSI/EBSI.spec.ts | 2 +- yarn.lock | 12 + 13 files changed, 110 insertions(+), 1079 deletions(-) delete mode 100644 src/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts diff --git a/generator/schemaGenerator.ts b/generator/schemaGenerator.ts index 27ba28a..2e65810 100644 --- a/generator/schemaGenerator.ts +++ b/generator/schemaGenerator.ts @@ -165,12 +165,12 @@ const authorizationRequestPayloadVD11 = { skipTypeCheck: true }; -const authorizationRequestPayloadVD12OID4VPD18 = { +const authorizationRequestPayloadVD13OID4VPD20 = { path: '../src/types/SIOP.types.ts', tsconfig: 'tsconfig.json', - type: 'AuthorizationRequestPayloadVD12OID4VPD18', // Or if you want to generate schema for that one type only - schemaId: 'AuthorizationRequestPayloadVD12OID4VPD18Schema', - outputPath: 'src/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts', + type: 'AuthorizationRequestPayloadVD13OID4VPD20', // Or if you want to generate schema for that one type only + schemaId: 'AuthorizationRequestPayloadVD13OID4VPD20Schema', + outputPath: 'src/schemas/AuthorizationRequestPayloadVD13OID4VPD20.schema.ts', // outputConstName: 'AuthorizationRequestPayloadSchemaVD11', skipTypeCheck: true }; @@ -178,7 +178,7 @@ const authorizationRequestPayloadVD12OID4VPD18 = { let schemas: Schema[] = [ writeSchema(authorizationRequestPayloadVID1), writeSchema(authorizationRequestPayloadVD11), - writeSchema(authorizationRequestPayloadVD12OID4VPD18), + writeSchema(authorizationRequestPayloadVD13OID4VPD20), // writeSchema(requestOptsConf), writeSchema(responseOptsConf), writeSchema(rPRegistrationMetadataPayload), diff --git a/package.json b/package.json index 66a391b..c3ac9fe 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "events": "^3.3.0", "language-tags": "^1.0.9", "multiformats": "^12.1.3", + "node-forge": "^1.3.1", "qs": "^6.11.2", "sha.js": "^2.4.11", "uint8arrays": "^3.1.1", @@ -64,6 +65,7 @@ "@transmute/ed25519-signature-2018": "^0.7.0-unstable.82", "@types/jest": "^29.5.11", "@types/language-tags": "^1.0.4", + "@types/node-forge": "^1.3.11", "@types/qs": "^6.9.11", "@types/sha.js": "^2.4.4", "@types/uuid": "^9.0.7", diff --git a/src/authorization-request/AuthorizationRequest.ts b/src/authorization-request/AuthorizationRequest.ts index 4e9b80c..aa1ae15 100644 --- a/src/authorization-request/AuthorizationRequest.ts +++ b/src/authorization-request/AuthorizationRequest.ts @@ -1,4 +1,7 @@ import { JWTVerifyOptions } from 'did-jwt'; +import { decodeJWT } from 'did-jwt'; +import { JWTDecoded } from 'did-jwt/lib/JWT'; +import forge from 'node-forge'; import { PresentationDefinitionWithLocation } from '../authorization-response'; import { PresentationExchange } from '../authorization-response/PresentationExchange'; @@ -186,11 +189,15 @@ export class AuthorizationRequest { throw new Error(`${SIOPErrors.INVALID_REQUEST}, redirect_uri or response_uri is needed`); } + if (mergedPayload.client_id_scheme === 'verifier_attestation') { + verifiedJwt = await AuthorizationRequest.verifyAttestationJWT(jwt, mergedPayload.client_id); + } else if (mergedPayload.client_id_scheme === 'x509_san_dns') { + await this.checkX509SanDNSScheme(jwt, mergedPayload.client_id); + } else if (mergedPayload.client_id_scheme === 'x509_san_uri') { + throw new Error(SIOPErrors.VERIFICATION_X509_SAN_URI_SCHEME_NOT_IMPLEMENTED_ERROR) + } await checkWellknownDIDFromRequest(mergedPayload, opts); - // TODO: we need to verify somewhere that if response_mode is direct_post, that the response_uri may be present, - // BUT not both redirect_uri and response_uri. What is the best place to do this? - const presentationDefinitions = await PresentationExchange.findValidPresentationDefinitions(mergedPayload, await this.getSupportedVersion()); return { ...verifiedJwt, @@ -248,6 +255,68 @@ export class AuthorizationRequest { }; } + /** + * Verifies a JWT according to the 'verifier_attestation' client_id_scheme, where the JWT must be + * signed with a private key corresponding to the public key specified within the JWT itself. This method + * ensures that the JWT's 'sub' claim matches the provided clientId, and it extracts and validates the + * public key from the JWT's 'cnf' (confirmation) claim, which must contain a JWK. + * + * @param jwt The JSON Web Token string to be verified. It is expected that this JWT is formatted correctly + * and includes a 'cnf' claim with a JWK representing the public key used for signing the JWT. + * @param clientId The client identifier expected to match the 'sub' claim in the JWT. This is used to + * validate that the JWT is intended for the correct recipient/client. + */ + private static async verifyAttestationJWT(jwt: string, clientId: string): Promise { + if (!jwt) { + throw new Error(SIOPErrors.NO_JWT); + } + const payload = decodeJWT(jwt); + const sub = payload['sub']; + const cnf = payload['cnf']; + + if (sub !== clientId || !cnf || typeof cnf !== 'object' || !cnf['jwk'] || typeof cnf['jwk'] !== 'object') { + throw new Error(SIOPErrors.VERIFICATION_VERIFIER_ATTESTATION_SCHEME_ERROR); + } + + return { + jwt, + payload: payload.payload, + issuer: payload['iss'], + jwk: cnf['jwk'], + }; + } + + /** + * verifying JWTs against X.509 certificates focusing on DNS SAN compliance, which is crucial for environments where certificate-based security is pivotal. + * @param jwt The encoded JWT from which the certificate needs to be extracted. + * @param clientId The DNS name to match against the certificate's SANs. + */ + private async checkX509SanDNSScheme(jwt: string, clientId: string): Promise { + const jwtDecoded: JWTDecoded = decodeJWT(jwt); + const x5c = jwtDecoded.header['x5c']; + + if (x5c == null || !Array.isArray(x5c) || x5c.length === 0) { + throw new Error(SIOPErrors.VERIFICATION_X509_SAN_DNS_SCHEME_ERROR); + } + + const certificate = x5c[0]; + if (!certificate) { + throw new Error(SIOPErrors.VERIFICATION_X509_SAN_DNS_SCHEME_NO_CERTIFICATE_ERROR); + } + + const der = forge.util.decode64(certificate); + const asn1 = forge.asn1.fromDer(der); + const cert = forge.pki.certificateFromAsn1(asn1); + + const subjectAltNames = cert.getExtension('subjectAltName'); + if (!subjectAltNames || !Array.isArray(subjectAltNames['altNames'])) { + throw new Error(SIOPErrors.VERIFICATION_X509_SAN_DNS_ALT_NAMES_ERROR); + } + if (!subjectAltNames || !subjectAltNames['altNames'].some((name: any) => name.value === clientId)) { + throw new Error(SIOPErrors.VERIFICATION_X509_SAN_DNS_SCHEME_DNS_NAME_MATCH); + } + } + public async containsResponseType(singleType: ResponseType | string): Promise { const responseType: string = await this.getMergedProperty('response_type'); return responseType?.includes(singleType) === true; diff --git a/src/helpers/SIOPSpecVersion.ts b/src/helpers/SIOPSpecVersion.ts index 2ae0908..c8f3e2d 100644 --- a/src/helpers/SIOPSpecVersion.ts +++ b/src/helpers/SIOPSpecVersion.ts @@ -1,5 +1,5 @@ import { AuthorizationRequestPayloadVD11Schema, AuthorizationRequestPayloadVID1Schema } from '../schemas'; -import { AuthorizationRequestPayloadVD12OID4VPD18Schema } from '../schemas/validation/schemaValidation'; +import { AuthorizationRequestPayloadVD13OID4VPD20Schema } from '../schemas/validation/schemaValidation'; import { AuthorizationRequestPayload, ResponseMode, SupportedVersion } from '../types'; import errors from '../types/Errors'; @@ -34,15 +34,15 @@ export const authorizationRequestVersionDiscovery = (authorizationRequest: Autho const versions = []; const authorizationRequestCopy: AuthorizationRequestPayload = JSON.parse(JSON.stringify(authorizationRequest)); // todo: We could use v11 validation for v12 for now, as we do not differentiate in the schema at this point\ - const vd12Validation = AuthorizationRequestPayloadVD12OID4VPD18Schema(authorizationRequestCopy); - if (vd12Validation) { + const vd13Validation = AuthorizationRequestPayloadVD13OID4VPD20Schema(authorizationRequestCopy); + if (vd13Validation) { if ( !authorizationRequestCopy.registration_uri && !authorizationRequestCopy.registration && !(authorizationRequestCopy.claims && 'vp_token' in authorizationRequestCopy.claims) && authorizationRequestCopy.response_mode !== ResponseMode.POST // Post has been replaced by direct post ) { - versions.push(SupportedVersion.SIOPv2_D12_OID4VP_D18); + versions.push(SupportedVersion.SIOPv2_D13_OID4VP_D20); } } const vd11Validation = AuthorizationRequestPayloadVD11Schema(authorizationRequestCopy); diff --git a/src/id-token/Payload.ts b/src/id-token/Payload.ts index 1d2fcfe..628d2b3 100644 --- a/src/id-token/Payload.ts +++ b/src/id-token/Payload.ts @@ -35,7 +35,7 @@ export const createIDTokenPayload = async ( const rpSupportedVersions = authorizationRequestVersionDiscovery(payload); const maxRPVersion = rpSupportedVersions.reduce( (previous, current) => (current.valueOf() > previous.valueOf() ? current : previous), - SupportedVersion.SIOPv2_D12_OID4VP_D18, + SupportedVersion.SIOPv2_D13_OID4VP_D20, ); if (responseOpts.version && rpSupportedVersions.length > 0 && !rpSupportedVersions.includes(responseOpts.version)) { throw Error(`RP does not support spec version ${responseOpts.version}, supported versions: ${rpSupportedVersions.toString()}`); diff --git a/src/op/OP.ts b/src/op/OP.ts index c5a67dd..fc48012 100644 --- a/src/op/OP.ts +++ b/src/op/OP.ts @@ -245,7 +245,7 @@ export class OP { registration: { ...this._createResponseOptions?.registration, issuer }, responseURI, responseURIType: - this._createResponseOptions.responseURIType ?? (version < SupportedVersion.SIOPv2_D12_OID4VP_D18 && responseURI ? 'redirect_uri' : undefined), + this._createResponseOptions.responseURIType ?? (version < SupportedVersion.SIOPv2_D13_OID4VP_D20 && responseURI ? 'redirect_uri' : undefined), }; } diff --git a/src/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts b/src/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts deleted file mode 100644 index 8a415a9..0000000 --- a/src/schemas/AuthorizationRequestPayloadVD12OID4VPD18.schema.ts +++ /dev/null @@ -1,1059 +0,0 @@ -export const AuthorizationRequestPayloadVD12OID4VPD18SchemaObj = { - "$id": "AuthorizationRequestPayloadVD12OID4VPD18Schema", - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/AuthorizationRequestPayloadVD12OID4VPD18", - "definitions": { - "AuthorizationRequestPayloadVD12OID4VPD18": { - "type": "object", - "properties": { - "id_token_type": { - "type": "string" - }, - "client_metadata": { - "$ref": "#/definitions/RPRegistrationMetadataPayload" - }, - "client_metadata_uri": { - "type": "string" - }, - "iss": { - "type": "string" - }, - "sub": { - "type": "string" - }, - "aud": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "iat": { - "type": "number" - }, - "nbf": { - "type": "number" - }, - "type": { - "type": "string" - }, - "exp": { - "type": "number" - }, - "rexp": { - "type": "number" - }, - "jti": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "response_type": { - "anyOf": [ - { - "$ref": "#/definitions/ResponseType" - }, - { - "type": "string" - } - ] - }, - "client_id": { - "type": "string" - }, - "redirect_uri": { - "type": "string" - }, - "id_token_hint": { - "type": "string" - }, - "nonce": { - "type": "string" - }, - "state": { - "type": "string" - }, - "response_mode": { - "$ref": "#/definitions/ResponseMode" - }, - "request": { - "type": "string" - }, - "request_uri": { - "type": "string" - }, - "claims": { - "$ref": "#/definitions/ClaimPayloadCommon" - }, - "presentation_definition": { - "anyOf": [ - { - "$ref": "#/definitions/PresentationDefinitionV1" - }, - { - "$ref": "#/definitions/PresentationDefinitionV2" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/PresentationDefinitionV1" - } - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/PresentationDefinitionV2" - } - } - ] - }, - "presentation_definition_uri": { - "type": "string" - }, - "client_id_scheme": { - "$ref": "#/definitions/ClientIdScheme" - }, - "response_uri": { - "type": "string" - } - } - }, - "RPRegistrationMetadataPayload": { - "type": "object", - "properties": { - "client_id": { - "anyOf": [ - { - "type": "string" - }, - {} - ] - }, - "id_token_signing_alg_values_supported": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/SigningAlgo" - } - }, - { - "$ref": "#/definitions/SigningAlgo" - } - ] - }, - "request_object_signing_alg_values_supported": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/SigningAlgo" - } - }, - { - "$ref": "#/definitions/SigningAlgo" - } - ] - }, - "response_types_supported": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/ResponseType" - } - }, - { - "$ref": "#/definitions/ResponseType" - } - ] - }, - "scopes_supported": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/Scope" - } - }, - { - "$ref": "#/definitions/Scope" - } - ] - }, - "subject_types_supported": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/SubjectType" - } - }, - { - "$ref": "#/definitions/SubjectType" - } - ] - }, - "subject_syntax_types_supported": { - "type": "array", - "items": { - "type": "string" - } - }, - "vp_formats": { - "anyOf": [ - { - "$ref": "#/definitions/Format" - }, - {} - ] - }, - "client_name": { - "anyOf": [ - { - "type": "string" - }, - {} - ] - }, - "logo_uri": { - "anyOf": [ - {}, - { - "type": "string" - } - ] - }, - "client_purpose": { - "anyOf": [ - {}, - { - "type": "string" - } - ] - } - } - }, - "SigningAlgo": { - "type": "string", - "enum": [ - "EdDSA", - "RS256", - "PS256", - "ES256", - "ES256K" - ] - }, - "ResponseType": { - "type": "string", - "enum": [ - "id_token", - "vp_token" - ] - }, - "Scope": { - "type": "string", - "enum": [ - "openid", - "openid did_authn", - "profile", - "email", - "address", - "phone" - ] - }, - "SubjectType": { - "type": "string", - "enum": [ - "public", - "pairwise" - ] - }, - "Format": { - "type": "object", - "properties": { - "jwt": { - "$ref": "#/definitions/JwtObject" - }, - "jwt_vc": { - "$ref": "#/definitions/JwtObject" - }, - "jwt_vc_json": { - "$ref": "#/definitions/JwtObject" - }, - "jwt_vp": { - "$ref": "#/definitions/JwtObject" - }, - "jwt_vp_json": { - "$ref": "#/definitions/JwtObject" - }, - "ldp": { - "$ref": "#/definitions/LdpObject" - }, - "ldp_vc": { - "$ref": "#/definitions/LdpObject" - }, - "ldp_vp": { - "$ref": "#/definitions/LdpObject" - }, - "di": { - "$ref": "#/definitions/DiObject" - }, - "di_vc": { - "$ref": "#/definitions/DiObject" - }, - "di_vp": { - "$ref": "#/definitions/DiObject" - }, - "vc+sd-jwt": { - "$ref": "#/definitions/SdJwtObject" - } - }, - "additionalProperties": false - }, - "JwtObject": { - "type": "object", - "properties": { - "alg": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "alg" - ], - "additionalProperties": false - }, - "LdpObject": { - "type": "object", - "properties": { - "proof_type": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "proof_type" - ], - "additionalProperties": false - }, - "DiObject": { - "type": "object", - "properties": { - "proof_type": { - "type": "array", - "items": { - "type": "string" - } - }, - "cryptosuite": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "proof_type", - "cryptosuite" - ], - "additionalProperties": false - }, - "SdJwtObject": { - "type": "object", - "properties": { - "sd-jwt_alg_values": { - "type": "array", - "items": { - "type": "string" - } - }, - "kb-jwt_alg_values": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "ResponseMode": { - "type": "string", - "enum": [ - "fragment", - "form_post", - "post", - "direct_post", - "query" - ] - }, - "ClaimPayloadCommon": { - "type": "object" - }, - "PresentationDefinitionV1": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "purpose": { - "type": "string" - }, - "format": { - "$ref": "#/definitions/Format" - }, - "submission_requirements": { - "type": "array", - "items": { - "$ref": "#/definitions/SubmissionRequirement" - } - }, - "input_descriptors": { - "type": "array", - "items": { - "$ref": "#/definitions/InputDescriptorV1" - } - } - }, - "required": [ - "id", - "input_descriptors" - ], - "additionalProperties": false - }, - "SubmissionRequirement": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "purpose": { - "type": "string" - }, - "rule": { - "$ref": "#/definitions/Rules" - }, - "count": { - "type": "number" - }, - "min": { - "type": "number" - }, - "max": { - "type": "number" - }, - "from": { - "type": "string" - }, - "from_nested": { - "type": "array", - "items": { - "$ref": "#/definitions/SubmissionRequirement" - } - } - }, - "required": [ - "rule" - ], - "additionalProperties": false - }, - "Rules": { - "type": "string", - "enum": [ - "all", - "pick" - ] - }, - "InputDescriptorV1": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "purpose": { - "type": "string" - }, - "group": { - "type": "array", - "items": { - "type": "string" - } - }, - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/Schema" - } - }, - "issuance": { - "type": "array", - "items": { - "$ref": "#/definitions/Issuance" - } - }, - "constraints": { - "$ref": "#/definitions/ConstraintsV1" - } - }, - "required": [ - "id", - "schema" - ], - "additionalProperties": false - }, - "Schema": { - "type": "object", - "properties": { - "uri": { - "type": "string" - }, - "required": { - "type": "boolean" - } - }, - "required": [ - "uri" - ], - "additionalProperties": false - }, - "Issuance": { - "type": "object", - "properties": { - "manifest": { - "type": "string" - } - }, - "additionalProperties": {} - }, - "ConstraintsV1": { - "type": "object", - "properties": { - "limit_disclosure": { - "$ref": "#/definitions/Optionality" - }, - "statuses": { - "$ref": "#/definitions/Statuses" - }, - "fields": { - "type": "array", - "items": { - "$ref": "#/definitions/FieldV1" - } - }, - "subject_is_issuer": { - "$ref": "#/definitions/Optionality" - }, - "is_holder": { - "type": "array", - "items": { - "$ref": "#/definitions/HolderSubject" - } - }, - "same_subject": { - "type": "array", - "items": { - "$ref": "#/definitions/HolderSubject" - } - } - }, - "additionalProperties": false - }, - "Optionality": { - "type": "string", - "enum": [ - "required", - "preferred" - ] - }, - "Statuses": { - "type": "object", - "properties": { - "active": { - "$ref": "#/definitions/PdStatus" - }, - "suspended": { - "$ref": "#/definitions/PdStatus" - }, - "revoked": { - "$ref": "#/definitions/PdStatus" - } - }, - "additionalProperties": false - }, - "PdStatus": { - "type": "object", - "properties": { - "directive": { - "$ref": "#/definitions/Directives" - } - }, - "additionalProperties": false - }, - "Directives": { - "type": "string", - "enum": [ - "required", - "allowed", - "disallowed" - ] - }, - "FieldV1": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "path": { - "type": "array", - "items": { - "type": "string" - } - }, - "purpose": { - "type": "string" - }, - "filter": { - "$ref": "#/definitions/FilterV1" - }, - "predicate": { - "$ref": "#/definitions/Optionality" - } - }, - "required": [ - "path" - ], - "additionalProperties": false - }, - "FilterV1": { - "type": "object", - "properties": { - "const": { - "$ref": "#/definitions/OneOfNumberString" - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/definitions/OneOfNumberString" - } - }, - "exclusiveMinimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "exclusiveMaximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "format": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - }, - "minimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "maximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "not": { - "type": "object" - }, - "pattern": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "required": [ - "type" - ], - "additionalProperties": false - }, - "OneOfNumberString": { - "type": [ - "number", - "string" - ] - }, - "HolderSubject": { - "type": "object", - "properties": { - "field_id": { - "type": "array", - "items": { - "type": "string" - } - }, - "directive": { - "$ref": "#/definitions/Optionality" - } - }, - "required": [ - "field_id", - "directive" - ], - "additionalProperties": false - }, - "PresentationDefinitionV2": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "purpose": { - "type": "string" - }, - "format": { - "$ref": "#/definitions/Format" - }, - "submission_requirements": { - "type": "array", - "items": { - "$ref": "#/definitions/SubmissionRequirement" - } - }, - "input_descriptors": { - "type": "array", - "items": { - "$ref": "#/definitions/InputDescriptorV2" - } - }, - "frame": { - "type": "object" - } - }, - "required": [ - "id", - "input_descriptors" - ], - "additionalProperties": false - }, - "InputDescriptorV2": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "purpose": { - "type": "string" - }, - "format": { - "$ref": "#/definitions/Format" - }, - "group": { - "type": "array", - "items": { - "type": "string" - } - }, - "issuance": { - "type": "array", - "items": { - "$ref": "#/definitions/Issuance" - } - }, - "constraints": { - "$ref": "#/definitions/ConstraintsV2" - } - }, - "required": [ - "id", - "constraints" - ], - "additionalProperties": false - }, - "ConstraintsV2": { - "type": "object", - "properties": { - "limit_disclosure": { - "$ref": "#/definitions/Optionality" - }, - "statuses": { - "$ref": "#/definitions/Statuses" - }, - "fields": { - "type": "array", - "items": { - "$ref": "#/definitions/FieldV2" - } - }, - "subject_is_issuer": { - "$ref": "#/definitions/Optionality" - }, - "is_holder": { - "type": "array", - "items": { - "$ref": "#/definitions/HolderSubject" - } - }, - "same_subject": { - "type": "array", - "items": { - "$ref": "#/definitions/HolderSubject" - } - } - }, - "additionalProperties": false - }, - "FieldV2": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "path": { - "type": "array", - "items": { - "type": "string" - } - }, - "purpose": { - "type": "string" - }, - "filter": { - "$ref": "#/definitions/FilterV2" - }, - "predicate": { - "$ref": "#/definitions/Optionality" - }, - "name": { - "type": "string" - } - }, - "required": [ - "path" - ], - "additionalProperties": false - }, - "FilterV2": { - "type": "object", - "properties": { - "const": { - "$ref": "#/definitions/OneOfNumberString" - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/definitions/OneOfNumberString" - } - }, - "exclusiveMinimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "exclusiveMaximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "format": { - "type": "string" - }, - "formatMaximum": { - "type": "string" - }, - "formatMinimum": { - "type": "string" - }, - "formatExclusiveMaximum": { - "type": "string" - }, - "formatExclusiveMinimum": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - }, - "minimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "maximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "not": { - "type": "object" - }, - "pattern": { - "type": "string" - }, - "type": { - "type": "string" - }, - "contains": { - "$ref": "#/definitions/FilterV2Base" - }, - "items": { - "$ref": "#/definitions/FilterV2BaseItems" - } - }, - "required": [ - "type" - ], - "additionalProperties": false - }, - "FilterV2Base": { - "type": "object", - "properties": { - "const": { - "$ref": "#/definitions/OneOfNumberString" - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/definitions/OneOfNumberString" - } - }, - "exclusiveMinimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "exclusiveMaximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "format": { - "type": "string" - }, - "formatMaximum": { - "type": "string" - }, - "formatMinimum": { - "type": "string" - }, - "formatExclusiveMaximum": { - "type": "string" - }, - "formatExclusiveMinimum": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - }, - "minimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "maximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "not": { - "type": "object" - }, - "pattern": { - "type": "string" - }, - "type": { - "type": "string" - }, - "contains": { - "$ref": "#/definitions/FilterV2Base" - }, - "items": { - "$ref": "#/definitions/FilterV2BaseItems" - } - }, - "additionalProperties": false - }, - "FilterV2BaseItems": { - "type": "object", - "properties": { - "const": { - "$ref": "#/definitions/OneOfNumberString" - }, - "enum": { - "type": "array", - "items": { - "$ref": "#/definitions/OneOfNumberString" - } - }, - "exclusiveMinimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "exclusiveMaximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "format": { - "type": "string" - }, - "formatMaximum": { - "type": "string" - }, - "formatMinimum": { - "type": "string" - }, - "formatExclusiveMaximum": { - "type": "string" - }, - "formatExclusiveMinimum": { - "type": "string" - }, - "minLength": { - "type": "number" - }, - "maxLength": { - "type": "number" - }, - "minimum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "maximum": { - "$ref": "#/definitions/OneOfNumberString" - }, - "not": { - "type": "object" - }, - "pattern": { - "type": "string" - }, - "type": { - "type": "string" - }, - "contains": { - "$ref": "#/definitions/FilterV2Base" - }, - "items": { - "$ref": "#/definitions/FilterV2BaseItems" - } - }, - "required": [ - "type" - ], - "additionalProperties": false - }, - "ClientIdScheme": { - "type": "string", - "enum": [ - "pre-registered", - "redirect_uri", - "entity_id", - "did" - ] - } - } -}; \ No newline at end of file diff --git a/src/types/Errors.ts b/src/types/Errors.ts index b9f23bc..7f5a295 100644 --- a/src/types/Errors.ts +++ b/src/types/Errors.ts @@ -66,6 +66,12 @@ enum SIOPErrors { NO_VERIFIABLE_PRESENTATION_NO_CREDENTIALS = 'Either no verifiable presentation or no credentials found in the verifiable presentation', VERIFICATION_METHOD_NOT_SUPPORTED = 'Verification method not supported', VERIFICATION_METHOD_NO_MATCH = "The verification method from the RP's DID Document does NOT match the kid of the SIOP Request", + VERIFICATION_VERIFIER_ATTESTATION_SCHEME_ERROR = 'Verification failed. verifier_attestation scheme error: Invalid payload data', + VERIFICATION_X509_SAN_DNS_SCHEME_DNS_NAME_MATCH = 'Verification of x509_san_dns scheme error: DNS name does not match', + VERIFICATION_X509_SAN_DNS_ALT_NAMES_ERROR = 'Verification of x509_san_dns scheme error: No SAN found or incorrect SAN format.', + VERIFICATION_X509_SAN_DNS_SCHEME_ERROR = 'Verification failed. x509_san_dns scheme error: Invalid x509 data', + VERIFICATION_X509_SAN_DNS_SCHEME_NO_CERTIFICATE_ERROR = 'Verification failed. x509_san_dns scheme error: No certificate found', + VERIFICATION_X509_SAN_URI_SCHEME_NOT_IMPLEMENTED_ERROR = 'Verification failed. x509_san_uri not implemented.', 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', diff --git a/src/types/JWT.types.ts b/src/types/JWT.types.ts index cd8a9e2..e9d788b 100644 --- a/src/types/JWT.types.ts +++ b/src/types/JWT.types.ts @@ -30,6 +30,7 @@ export interface VerifiedJWT { issuer: string; //The issuer (did) of the JWT signer?: VerificationMethod; // The matching verification method from the DID that was used to sign jwt: string; // The JWT + jwk?: string; } /** diff --git a/src/types/SIOP.types.ts b/src/types/SIOP.types.ts index 7acd702..ec26e25 100644 --- a/src/types/SIOP.types.ts +++ b/src/types/SIOP.types.ts @@ -77,7 +77,7 @@ export interface AuthorizationRequestPayloadVD11 presentation_definition_uri?: string; } -export interface AuthorizationRequestPayloadVD12OID4VPD18 +export interface AuthorizationRequestPayloadVD13OID4VPD20 extends AuthorizationRequestCommonPayload, RequestClientMetadataPayloadProperties, RequestIdTokenPayloadProperties { @@ -88,13 +88,13 @@ export interface AuthorizationRequestPayloadVD12OID4VPD18 response_uri?: string; // New since OID4VP18 OPTIONAL. The Response URI to which the Wallet MUST send the Authorization Response using an HTTPS POST request as defined by the Response Mode direct_post. The Response URI receives all Authorization Response parameters as defined by the respective Response Type. When the response_uri parameter is present, the redirect_uri Authorization Request parameter MUST NOT be present. If the redirect_uri Authorization Request parameter is present when the Response Mode is direct_post, the Wallet MUST return an invalid_request Authorization Response error. } -export type ClientIdScheme = 'pre-registered' | 'redirect_uri' | 'entity_id' | 'did'; +export type ClientIdScheme = 'pre-registered' | 'redirect_uri' | 'entity_id' | 'did' | 'verifier_attestation' | 'x509_san_dns' | 'x509_san_uri'; // https://openid.bitbucket.io/connect/openid-connect-self-issued-v2-1_0.html#section-10 export type AuthorizationRequestPayload = | AuthorizationRequestPayloadVID1 | AuthorizationRequestPayloadVD11 - | AuthorizationRequestPayloadVD12OID4VPD18; + | AuthorizationRequestPayloadVD13OID4VPD20; export type JWTVcPresentationProfileAuthenticationRequestPayload = RequestIdTokenPayloadProperties; @@ -772,7 +772,7 @@ export interface RevocationOpts { export enum SupportedVersion { SIOPv2_ID1 = 70, SIOPv2_D11 = 110, - SIOPv2_D12_OID4VP_D18 = 180, + SIOPv2_D13_OID4VP_D20 = 180, JWT_VC_PRESENTATION_PROFILE_v1 = 71, } diff --git a/test/e2e/mattr.launchpad.spec.ts b/test/e2e/mattr.launchpad.spec.ts index ded7ab6..348aa0e 100644 --- a/test/e2e/mattr.launchpad.spec.ts +++ b/test/e2e/mattr.launchpad.spec.ts @@ -227,7 +227,7 @@ describe('Mattr OID4VP v18 credential offer', () => { console.log(JSON.stringify(verification)); expect(verification).toBeDefined(); - expect(verification.versions).toEqual([SupportedVersion.SIOPv2_D12_OID4VP_D18]); + expect(verification.versions).toEqual([SupportedVersion.SIOPv2_D13_OID4VP_D20]); /** * pd value: {"id":"dae5d9b6-8145-4297-99b2-b8fcc5abb5ad","input_descriptors":[{"id":"OpenBadgeCredential","format":{"jwt_vc_json":{"alg":["EdDSA"]},"jwt_vc":{"alg":["EdDSA"]}},"constraints":{"fields":[{"path":["$.vc.type"],"filter":{"type":"array","items":{"type":"string"},"contains":{"const":"OpenBadgeCredential"}}}]}}]} diff --git a/test/interop/EBSI/EBSI.spec.ts b/test/interop/EBSI/EBSI.spec.ts index 890e001..37621dd 100644 --- a/test/interop/EBSI/EBSI.spec.ts +++ b/test/interop/EBSI/EBSI.spec.ts @@ -51,7 +51,7 @@ describe('EBSI', () => { wellknownDIDVerifyCallback: async (_args: IVerifyCallbackArgs): Promise => ({ verified: true }), }, correlationId: '1234', - supportedVersions: [SupportedVersion.SIOPv2_D12_OID4VP_D18], + supportedVersions: [SupportedVersion.SIOPv2_D13_OID4VP_D20], }; it( 'succeed from request opts when all params are set', diff --git a/yarn.lock b/yarn.lock index e449ccf..c27d02d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3113,6 +3113,13 @@ resolved "https://registry.yarnpkg.com/@types/language-tags/-/language-tags-1.0.4.tgz#c622209605b919c41cbf5a78c2fb58dbc3d6f029" integrity sha512-20PQbifv3v/djCT+KlXybv0KqO5ofoR1qD1wkinN59kfggTPVTWGmPFgL/1yWuDyRcsQP/POvkqK+fnl5nOwTg== +"@types/node-forge@^1.3.11": + version "1.3.11" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + "@types/node@*": version "20.11.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.0.tgz#8e0b99e70c0c1ade1a86c4a282f7b7ef87c9552f" @@ -6530,6 +6537,11 @@ node-fetch@^3.2.10: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + node-gyp-build@^4.2.0: version "4.8.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd"