@sphereon/pex
Version:
A Typescript implementation of the v1 and v2 DIF Presentation Exchange specification
179 lines • 19.7 kB
JavaScript
import { JSONPath as jp } from '@astronautlabs/jsonpath';
import { CredentialMapper, OriginalType, } from '@sphereon/ssi-types';
import { nanoid } from 'nanoid';
import { Status } from '../../ConstraintUtils';
import { PEVersion } from '../../types';
import PexMessages from '../../types/Messages';
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 = [];
if (CredentialMapper.isMsoMdocDecodedCredential(credential)) {
return uris;
}
// W3C credential
if (CredentialMapper.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 (CredentialMapper.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: CredentialMapper.isW3cCredential(wvc.credential) ? wvc.credential['@context'] : undefined,
vcCredentialSchema: CredentialMapper.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: CredentialMapper.isW3cCredential(wvc.credential) ? wvc.credential['@context'] : undefined,
vcCredentialSchema: CredentialMapper.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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXJpRXZhbHVhdGlvbkhhbmRsZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9saWIvZXZhbHVhdGlvbi9oYW5kbGVycy91cmlFdmFsdWF0aW9uSGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxJQUFJLEVBQUUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXpELE9BQU8sRUFDTCxnQkFBZ0IsRUFJaEIsWUFBWSxHQUdiLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUVoQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDL0MsT0FBTyxFQUFxRSxTQUFTLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDM0csT0FBTyxXQUFXLE1BQU0sc0JBQXNCLENBQUM7QUFJL0MsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFeEUsTUFBTSxPQUFPLG9CQUFxQixTQUFRLHlCQUF5QjtJQUNqRSxZQUFZLE1BQXdCO1FBQ2xDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNoQixDQUFDO0lBRU0sT0FBTztRQUNaLE9BQU8sZUFBZSxDQUFDO0lBQ3pCLENBQUM7SUFFTyxNQUFNLENBQUMsMEJBQTBCLEdBQUcsK0JBQStCLENBQUM7SUFDcEUsTUFBTSxDQUFDLHdCQUF3QixHQUNyQyx1SEFBdUgsQ0FBQztJQUVuSCxNQUFNLENBQUMsVUFBMkMsRUFBRSxVQUF5QztRQUNsRywrQkFBK0I7UUFDSSxVQUFXLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBeUIsRUFBRSxhQUFxQixFQUFFLEVBQUU7WUFDNUgsTUFBTSxJQUFJLEdBQWEsVUFBVSxDQUFDLFVBQVUsRUFBRSxLQUFLLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMs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