@animo-id/pex
Version:
A Typescript implementation of the v1 and v2 DIF Presentation Exchange specification
177 lines • 19.6 kB
JavaScript
import { JSONPath as jp } from '@astronautlabs/jsonpath';
import { OriginalType } from '@sphereon/ssi-types';
import { nanoid } from 'nanoid';
import { Status } from '../../ConstraintUtils';
import { PEVersion } from '../../types';
import PexMessages from '../../types/Messages';
import { PexCredentialMapper } from '../../types/PexCredentialMapper';
import { AbstractEvaluationHandler } from './abstractEvaluationHandler';
export class UriEvaluationHandler extends AbstractEvaluationHandler {
constructor(client) {
super(client);
}
getName() {
return 'UriEvaluation';
}
static HASHLINK_URL_ENCODED_REGEX = /hl:[a-zA-Z0-9]+:[a-zA-Z0-9]+/g;
static HASHLINK_QUERY_URL_REGEX = /https*?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)(hl=[a-zA-Z0-9]+)/g;
handle(definition, wrappedVcs) {
// This filter is removed in V2
definition.input_descriptors.forEach((inDesc, descriptorIdx) => {
const uris = definition.getVersion() !== PEVersion.v2 ? inDesc.schema.map((so) => so.uri) : [];
wrappedVcs.forEach((wvc, wrappedVCIdx) => {
const vcUris = UriEvaluationHandler.buildVcContextAndSchemaUris(wvc.credential, definition.getVersion());
this.evaluateUris(wvc, vcUris, uris, descriptorIdx, wrappedVCIdx, definition.getVersion());
});
});
const definitionAllowsDataIntegrity = definition.format?.di || definition.format?.di_vc || definition.format?.di_vp;
const descriptorMap = this.getResults()
.filter((result) => result.status === Status.INFO)
.map((result) => {
let format = result.payload?.format;
// This checks if the new data integrity format should be used.
// That may be the case if the input descriptor points to credentials that are in ldp_vc or ldp format,
// and the presentation definition allows data integrity.
if (definitionAllowsDataIntegrity && (format === 'ldp_vc' || format === 'ldp')) {
const wvcs = jp.nodes(wrappedVcs, result.verifiable_credential_path).map((node) => node.value);
// check if all vc's have a data integrity proof
const vcDataIntegrityProofs = wvcs.map((vc) => {
if (vc.type !== OriginalType.JSONLD || !vc.credential.proof)
return [];
const proofs = Array.isArray(vc.credential.proof) ? vc.credential.proof : [vc.credential.proof];
const dataIntegrityProofs = proofs.filter((proof) => proof.type === 'DataIntegrityProof' && proof.cryptosuite !== undefined);
return dataIntegrityProofs;
});
// determine the common cryptosuites of all vc's
const commonCryptosuites = vcDataIntegrityProofs.reduce((a, b) => a.filter((c) => b.includes(c)));
// the input descriptor should also allow data integrity
const inputDescriptor = jp.nodes(definition, result.input_descriptor_path)[0].value;
const inputDescriptorAllowsDataIntegrity = !inputDescriptor['format'] || inputDescriptor?.format?.di || inputDescriptor?.format?.di_vc || inputDescriptor?.format?.di_vp;
if (commonCryptosuites.length > 0 && inputDescriptorAllowsDataIntegrity) {
format = 'di_vc';
}
}
const inputDescriptor = jp.nodes(definition, result.input_descriptor_path)[0].value;
return {
id: inputDescriptor.id,
format,
path: result.verifiable_credential_path,
};
});
// The presentation submission is being created in this handler, then updated in subsequent handler.
// TODO: This approach needs to be refactored for a new Major version.
// Also there is no apparent need for the indirection and state in this class.
// Simply do the first loops and amend the presentation submission in every loop.
if (this.client.generatePresentationSubmission && (!this.presentationSubmission || Object.keys(this.presentationSubmission).length === 0)) {
this.presentationSubmission = {
id: nanoid(),
definition_id: definition.id,
descriptor_map: descriptorMap,
};
}
}
evaluateUris(wvc, verifiableCredentialUris, inputDescriptorsUris, idIdx, vcIdx, pdVersion) {
let hasAnyMatch = false;
if (pdVersion === PEVersion.v1) {
for (let i = 0; i < inputDescriptorsUris.length; i++) {
if (UriEvaluationHandler.containsHashlink(inputDescriptorsUris[i])) {
this.getResults().push(this.createWarnResultObject(idIdx, vcIdx));
}
}
for (let i = 0; i < verifiableCredentialUris.length; i++) {
if (inputDescriptorsUris.find((el) => el === verifiableCredentialUris[i]) != undefined) {
hasAnyMatch = true;
}
}
}
else {
hasAnyMatch = true;
}
if (hasAnyMatch) {
this.getResults().push(this.createSuccessResultObject(wvc, inputDescriptorsUris, idIdx, vcIdx));
}
else {
this.getResults().push(this.createErrorResultObject(wvc, inputDescriptorsUris, idIdx, vcIdx));
}
}
static buildVcContextAndSchemaUris(credential, version) {
const uris = [];
// W3C credential
if (PexCredentialMapper.isW3cCredential(credential)) {
if (Array.isArray(credential['@context'])) {
credential['@context'].forEach((value) => uris.push(value));
}
else {
uris.push(credential['@context']);
}
if (Array.isArray(credential.credentialSchema) && credential.credentialSchema.length > 0) {
credential.credentialSchema.forEach((element) => uris.push(element.id));
}
else if (credential.credentialSchema) {
uris.push(credential.credentialSchema.id);
}
if (version === PEVersion.v1) {
// JWT VC Profile and MS Entry Verified ID do use the schema from V1 to match against types in the VC
Array.isArray(credential.type)
? credential.type.forEach((type) => uris.push(type))
: credential.type
? uris.push(credential.type)
: undefined;
}
}
// NOTE: we add the `vct` field of an SD-JWT to the list of uris, to allow SD-JWT
// to work with PEX v1 in the same way that JWT vcs can work with pex v1. If we don't
// add this, then SD-JWTs can only be used with PEX v2.
if (PexCredentialMapper.isSdJwtDecodedCredential(credential)) {
if (version === PEVersion.v1) {
uris.push(credential.decodedPayload.vct);
}
}
return uris;
}
createSuccessResultObject(wvc, inputDescriptorsUris, idIdx, vcIdx) {
const result = this.createResult(idIdx, vcIdx);
result.status = Status.INFO;
result.message = PexMessages.URI_EVALUATION_PASSED;
result.payload = {
format: wvc.format,
vcContext: PexCredentialMapper.isW3cCredential(wvc.credential) ? wvc.credential['@context'] : undefined,
vcCredentialSchema: PexCredentialMapper.isW3cCredential(wvc.credential) ? wvc.credential.credentialSchema : undefined,
inputDescriptorsUris,
};
return result;
}
createErrorResultObject(wvc, inputDescriptorsUris, idIdx, vcIdx) {
const result = this.createResult(idIdx, vcIdx);
result.status = Status.ERROR;
result.message = PexMessages.URI_EVALUATION_DIDNT_PASS;
result.payload = {
format: wvc.format,
vcContext: PexCredentialMapper.isW3cCredential(wvc.credential) ? wvc.credential['@context'] : undefined,
vcCredentialSchema: PexCredentialMapper.isW3cCredential(wvc.credential) ? wvc.credential.credentialSchema : undefined,
inputDescriptorsUris,
};
return result;
}
createWarnResultObject(idIdx, vcIdx) {
const result = this.createResult(idIdx, vcIdx);
result.status = Status.WARN;
result.message = PexMessages.URI_EVALUATION_DIDNT_PASS;
result.payload = PexMessages.INPUT_DESCRIPTOR_CONTEXT_CONTAINS_HASHLINK_VERIFICATION_NOT_SUPPORTED;
return result;
}
createResult(idIdx, vcIdx) {
return {
input_descriptor_path: `$.input_descriptors[${idIdx}]`,
verifiable_credential_path: `$[${vcIdx}]`,
evaluator: this.getName(),
status: Status.INFO,
message: undefined,
};
}
static containsHashlink(url) {
return !(url.matchAll(UriEvaluationHandler.HASHLINK_QUERY_URL_REGEX).next().done &&
url.matchAll(UriEvaluationHandler.HASHLINK_URL_ENCODED_REGEX).next().done);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXJpRXZhbHVhdGlvbkhhbmRsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9saWIvZXZhbHVhdGlvbi9oYW5kbGVycy91cmlFdmFsdWF0aW9uSGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsUUFBUSxJQUFJLEVBQUUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXpELE9BQU8sRUFBa0MsWUFBWSxFQUFvQyxNQUFNLHFCQUFxQixDQUFDO0FBQ3JILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFaEMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9DLE9BQU8sRUFBcUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzNHLE9BQU8sV0FBVyxNQUFNLHNCQUFzQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxtQkFBbUIsRUFBK0IsTUFBTSxpQ0FBaUMsQ0FBQztBQUluRyxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUV4RSxNQUFNLE9BQU8sb0JBQXFCLFNBQVEseUJBQXlCO0lBQ2pFLFlBQVksTUFBd0I7UUFDbEMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2hCLENBQUM7SUFFTSxPQUFPO1FBQ1osT0FBTyxlQUFlLENBQUM7SUFDekIsQ0FBQztJQUVPLE1BQU0sQ0FBQywwQkFBMEIsR0FBRywrQkFBK0IsQ0FBQztJQUNwRSxNQUFNLENBQUMsd0JBQXdCLEdBQ3JDLHVIQUF1SCxDQUFDO0lBRW5ILE1BQU0sQ0FBQyxVQUEyQyxFQUFFLFVBQXlDO1FBQ2xHLCtCQUErQjtRQUNJLFVBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsQ0FBQyxNQUF5QixFQUFFLGFBQXFCLEVBQUUsRUFBRTtZQUM1SCxNQUFNLElBQUksR0FBYSxVQUFVLENBQUMsVUFBVSxFQUFFLEtBQUssU0FBUyxDQUFDLEVBQUUs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