@eecc/vc-verifier-rules
Version:
GS1 US Rules Verification Library for validating GS1 based verifiable credentials.
893 lines (874 loc) • 42.1 kB
JavaScript
import Ajv from 'ajv/dist/2020.js';
import addFormats from 'ajv-formats';
import ajvErrors from 'ajv-errors';
// src/lib/rules-schema/genericCredentialSchema.ts
var genericCredentialSchema = {
"$id": "Generic-Schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"version": "1.0.0",
"description": "Generic Credential",
"type": "object",
"title": "genericCredentialSchema",
"properties": void 0
};
// src/lib/rules-schema/gs1-chain-rules.ts
var gs1CredentialChainRules = {
genericCredentialSchema: {
title: "genericCredentialSchema",
extendsCredentialType: { type: [], rule: "" },
childCredential: void 0
},
GS1PrefixLicenseCredential: {
title: "GS1PrefixLicenseCredential",
extendsCredentialType: void 0,
childCredential: {
"type": ["GS1CompanyPrefixLicenseCredential", "GS1IdentificationKeyLicenseCredential"]
}
},
GS1CompanyPrefixLicenseCredential: {
title: "GS1CompanyPrefixLicenseCredential",
extendsCredentialType: { type: ["GS1PrefixLicenseCredential"], rule: "GS1PrefixLicenseCredential" },
childCredential: {
"type": ["KeyCredential"]
}
},
KeyCredential: {
title: "KeyCredential",
extendsCredentialType: { type: ["GS1CompanyPrefixLicenseCredential", "GS1IdentificationKeyLicenseCredential"], rule: "GS1CompanyPrefixLicenseCredential" },
childCredential: {
"type": ["OrganizationDataCredential", "ProductDataCredential"]
}
},
OrganizationDataCredential: {
title: "OrganizationDataCredential",
extendsCredentialType: { type: ["KeyCredential"], rule: "KeyCredential" },
childCredential: void 0
},
ProductDataCredential: {
title: "ProductDataCredential",
extendsCredentialType: { type: ["KeyCredential"], rule: "KeyDataCredential" },
childCredential: void 0
}
};
// src/lib/utility/text-util.ts
new TextEncoder();
var decoder = new TextDecoder();
// src/lib/schema/gs1-schema-extention-types.ts
var gs1GenericSchemaGS1 = {
"$id": "GS1-Generoc-Schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"version": "1.0.0",
"title": "GS1GenericSchema",
"description": "A Generic GS1 Schema used when a specific schema is not available",
"type": "object",
"properties": {
"credentialSubject": {}
}
};
var emptyJsonSchemaSubjectOnly = {
"credentialSubject": {}
};
var gs1AltLicenseValidationRules = {
"altLicenseValidation": true
};
var gs1DigitalLinkRules = {
"digitalLink": true
};
var gs1DigitalLinkSameAsRules = {
"digitalLinkSameAs": true
};
// src/lib/get-credential-type.ts
var GS1_PREFIX_LICENSE_CREDENTIAL = "GS1PrefixLicenseCredential";
var GS1_COMPANY_PREFIX_LICENSE_CREDENTIAL = "GS1CompanyPrefixLicenseCredential";
var KEY_CREDENTIAL = "KeyCredential";
var ORGANIZATION_DATA_CREDENTIAL = "OrganizationDataCredential";
var PRODUCT_DATA_CREDENTIAL = "ProductDataCredential";
var GS1_IDENTIFICATION_KEY_LICENSE_CREDENTIAL = "GS1IdentificationKeyLicenseCredential";
var GS1CredentialTypes = [
GS1_COMPANY_PREFIX_LICENSE_CREDENTIAL,
KEY_CREDENTIAL,
ORGANIZATION_DATA_CREDENTIAL,
PRODUCT_DATA_CREDENTIAL,
GS1_PREFIX_LICENSE_CREDENTIAL,
GS1_IDENTIFICATION_KEY_LICENSE_CREDENTIAL
];
var UNKNOWN_VALUE = "unknown";
var GS1CredentialSchema = [
{ name: GS1_PREFIX_LICENSE_CREDENTIAL, schemaId: "https://id.gs1.org/vc/schema/v1/prefix" },
{ name: GS1_COMPANY_PREFIX_LICENSE_CREDENTIAL, schemaId: "https://id.gs1.org/vc/schema/v1/companyprefix" },
{ name: KEY_CREDENTIAL, schemaId: "https://id.gs1.org/vc/schema/v1/key" },
{ name: ORGANIZATION_DATA_CREDENTIAL, schemaId: "https://id.gs1.org/vc/schema/v1/organizationdata" },
{ name: PRODUCT_DATA_CREDENTIAL, schemaId: "https://id.gs1.org/vc/schema/v1/productdata" },
{ name: GS1_IDENTIFICATION_KEY_LICENSE_CREDENTIAL, schemaId: "https://id.gs1.org/vc/schema/v1/identificationkey" }
];
var getCredentialType = (credentialTypesSource) => {
if (!credentialTypesSource)
return { name: UNKNOWN_VALUE, schemaId: "" };
const credentialTypes = credentialTypesSource.filter((credentialType) => GS1CredentialTypes.includes(credentialType));
if (credentialTypes.length === 1) {
const credentialType = GS1CredentialSchema.find((credential) => credential.name === credentialTypes[0]);
if (!credentialType) {
return { name: UNKNOWN_VALUE, schemaId: "" };
}
return credentialType;
} else if (credentialTypes.length > 1) {
return { name: UNKNOWN_VALUE, schemaId: "" };
}
return { name: UNKNOWN_VALUE, schemaId: "" };
};
var getCredentialRuleSchemaChain = function(credential) {
if (!credential) {
throw new Error("Credential is undefined");
}
const credentialType = getCredentialType(credential.type);
switch (credentialType.name) {
case GS1_PREFIX_LICENSE_CREDENTIAL:
return gs1CredentialChainRules.GS1PrefixLicenseCredential;
case GS1_COMPANY_PREFIX_LICENSE_CREDENTIAL:
return gs1CredentialChainRules.GS1CompanyPrefixLicenseCredential;
case KEY_CREDENTIAL:
return gs1CredentialChainRules.KeyCredential;
case ORGANIZATION_DATA_CREDENTIAL:
return gs1CredentialChainRules.OrganizationDataCredential;
case PRODUCT_DATA_CREDENTIAL:
return gs1CredentialChainRules.ProductDataCredential;
default:
return gs1CredentialChainRules.genericCredentialSchema;
}
};
var getGS1JsonSchema = function(fullJsonSchemaValidationOn, standardSchema, gs1RulesSchema) {
const schemaToValidate = { ...standardSchema };
if (!fullJsonSchemaValidationOn && gs1RulesSchema != null) {
schemaToValidate.properties = { ...emptyJsonSchemaSubjectOnly };
}
if (gs1RulesSchema != null) {
schemaToValidate.properties.credentialSubject = { ...schemaToValidate.properties?.credentialSubject, ...gs1RulesSchema };
}
return schemaToValidate;
};
var getGS1JsonSchemaForV1 = function(jsonSchema) {
const { ...v1Schema } = jsonSchema;
v1Schema["$id"] = jsonSchema["$id"] + "-V1";
v1Schema.required = [];
v1Schema.properties = {};
v1Schema.properties.credentialSubject = jsonSchema.properties.credentialSubject;
return v1Schema;
};
var callResolverToGetJsonSchema = function(schemaLoader, credentialType) {
if (schemaLoader) {
const credentialSchemaContent = schemaLoader(credentialType.schemaId);
if (credentialSchemaContent && credentialSchemaContent.length > 0) {
const schemaContent = decoder.decode(credentialSchemaContent);
const parsedSchemaContent = JSON.parse(schemaContent);
return parsedSchemaContent;
}
}
return gs1GenericSchemaGS1;
};
var getCredentialRuleSchema = function(schemaLoader, credential, fullJsonSchemaValidationOn = true) {
if (!credential) {
throw new Error("Credential can not be undefined.");
}
if (credential.type === void 0) {
throw new Error("Credential type can not be undefined.");
}
const credentialType = getCredentialType(credential.type);
const credentialSchema = callResolverToGetJsonSchema(schemaLoader, credentialType);
const starterSchema = credential.proof !== void 0 ? getGS1JsonSchemaForV1(credentialSchema) : credentialSchema;
switch (credentialType.name) {
case GS1_PREFIX_LICENSE_CREDENTIAL:
return getGS1JsonSchema(fullJsonSchemaValidationOn, starterSchema, gs1AltLicenseValidationRules);
case GS1_COMPANY_PREFIX_LICENSE_CREDENTIAL:
return getGS1JsonSchema(fullJsonSchemaValidationOn, starterSchema, gs1AltLicenseValidationRules);
case KEY_CREDENTIAL:
return getGS1JsonSchema(fullJsonSchemaValidationOn, starterSchema, gs1DigitalLinkRules);
case ORGANIZATION_DATA_CREDENTIAL:
return getGS1JsonSchema(fullJsonSchemaValidationOn, starterSchema, gs1DigitalLinkSameAsRules);
case PRODUCT_DATA_CREDENTIAL:
return getGS1JsonSchema(fullJsonSchemaValidationOn, starterSchema, gs1DigitalLinkSameAsRules);
default:
return genericCredentialSchema;
}
};
// src/lib/engine/gs1-credential-errors.ts
var invalidGS1CredentialTypes = { code: "GS1-100", rule: "The type of this license credential is not in the list of valid license credential types." };
var invalidExtendedCredentialMissing = { code: "GS1-120", rule: "Required extended license credential is missing to validate GS1 Extended Credentials Chain." };
var invalidRootCredentialType = { code: "GS1-130", rule: "The root credential type muts be a GS1PrefixLicenseCredential." };
var invalidIssueForPrefixLicense = { code: "GS1-140", rule: "The issuer of prefix license credential does not match the expected value." };
var invalidIssuer = { code: "GS1-150", rule: "The issuer of this license credential does not match the expected value." };
var invalidLicenseValueStartPrefix = { code: "GS1-201", rule: "License value does not start with the correct prefix value." };
var invalidLicenseValueFormat = { code: "GS1-202", rule: "The license value format is not valid." };
var invalidAlternativeLicenseValue = { code: "GS1-203", rule: "The alternative license value has not been specified." };
var invalidAlternativeLicenseNotCompatible = { code: "GS1-204", rule: "The alternative license value is not compatible with the license value." };
var invalidAlternativeLicenseNotSupported = { code: "GS1-205", rule: "An alternative license value is not supported." };
var invalidIssueSubject = { code: "GS1EX-212", rule: "License value does not start with the correct prefix value." };
var invalidGS1DigitalLink = { code: "GS1-213", rule: "Credential Subject Id must be a GS1 Digital Link." };
var invalidGS1DigitalLink_sameAs = { code: "GS1-214", rule: "Credential Subject sameAs must be a GS1 Digital Link." };
var dataMissingToValidateCredentialChain = { code: "GS1-300", rule: "One or more subject fields are missing or invalid. Can not validate credential chain." };
var dataMismatchBetweenDataKeyCredential = { code: "GS1-320", rule: "The data credential GS1 Digital Link does not match the Id in the Key Credential." };
var unsupportedCredentialChain = { code: "GS1-330", rule: "The credential chain is not supported." };
var verificationErrorCode = "VC-100";
var errorResolveCredentialCode = "GS1-010";
// src/lib/rules-definition/subject/check-credential-license.ts
async function checkCredentialLicenseValue(credentialSubject, licenseLength = { miniumLength: 3, maximumLength: 14 }) {
if (!credentialSubject?.licenseValue) {
return { verified: false, rule: invalidLicenseValueFormat };
}
const value = credentialSubject.licenseValue;
if (isNaN(+value)) {
return { verified: false, rule: invalidLicenseValueFormat };
}
if (value.length < licenseLength.miniumLength || value.length > licenseLength.maximumLength) {
return { verified: false, rule: invalidLicenseValueFormat };
}
return { verified: true };
}
async function checkPrefixCredentialLicenseValue(credentialSubject) {
return checkCredentialLicenseValue(credentialSubject, { miniumLength: 2, maximumLength: 4 });
}
// src/lib/rules-definition/subject/check-credential-subject-Id-digital-link.ts
function checkForGS1DigitalLink(value, validationRule, ignoreNull) {
if (!value) {
if (ignoreNull) {
return { verified: true };
}
return { verified: false, rule: validationRule };
}
const gs1DigitalLinkResult = parseGS1DigitalLink(value);
return gs1DigitalLinkResult.isValid ? { verified: true } : { verified: false, rule: validationRule };
}
function checkCredentialSameAsDigitalLink(credentialSubject) {
return checkForGS1DigitalLink(credentialSubject?.sameAs, invalidGS1DigitalLink_sameAs, true);
}
function checkCredentialSubjectIdDigitalLink(credentialSubject) {
return checkForGS1DigitalLink(credentialSubject?.id, invalidGS1DigitalLink, false);
}
var GS1_DIGITAL_LINK_GTIN = "01";
var GS1_DIGITAL_LINK_GLN = "254";
var GS1_DIGITAL_LINK_PARTYGLN = "417";
function gs1DigitalLinkType(typeValue) {
if (typeValue === GS1_DIGITAL_LINK_GTIN) {
return "GTIN";
}
if (typeValue === GS1_DIGITAL_LINK_GLN || typeValue === GS1_DIGITAL_LINK_PARTYGLN) {
return "GLN";
}
return "Unknown";
}
function parseGS1DigitalLink(value) {
const ulrValue = value instanceof URL ? value.toString() : value ? value : "";
if (value != null) {
const subjectIdNoProtocol = ulrValue.replace("https://", "");
const subjectIdParsed = subjectIdNoProtocol.split("/");
if (subjectIdParsed.length >= 3) {
return {
isValid: true,
type: gs1DigitalLinkType(subjectIdParsed[1]),
originalValue: ulrValue,
parsedValue: subjectIdParsed[2],
otherUriElements: subjectIdParsed.slice(3)
};
}
}
return {
isValid: false,
originalValue: ulrValue,
type: "Unknown"
};
}
// src/lib/utility/jwt-utils.ts
var getDecodedPresentation = function getDecodedJwt(token) {
const jwt = atob(token.split(".")[1]);
const jwtPresentation = JSON.parse(jwt);
const decodedPresentation = { ...jwtPresentation };
decodedPresentation.verifiableCredential = [];
jwtPresentation.verifiableCredential.forEach((vc) => {
if (typeof vc === "object" && vc.id && typeof vc.id === "string" && !("credentialSubject" in vc && vc.id.split(";")[0] === "data:application/vc+jwt")) {
const parseOutToken = vc.id.split(";")[1];
const jwt2 = atob(parseOutToken.split(".")[1]);
const jsonJwt = JSON.parse(jwt2);
decodedPresentation.verifiableCredential.push(jsonJwt);
} else {
decodedPresentation.verifiableCredential.push(vc);
}
});
return decodedPresentation;
};
var getDecodedCredential = function getDecodedJwt2(token) {
const jwt = atob(token.split(".")[1]);
const jwtCredential = JSON.parse(jwt);
return jwtCredential;
};
var normalizeCredential = function(credential) {
if (typeof credential === "string") {
return getDecodedCredential(credential);
}
if (typeof credential === "object" && credential.id && typeof credential.id === "string" && credential.id.startsWith("data:application/vc+jwt")) {
const jwtPart = credential.id.split(";")[1];
if (jwtPart) {
return getDecodedCredential(jwtPart);
}
}
return credential;
};
var normalizePresentation = function(presentation) {
if (typeof presentation === "string") {
return getDecodedPresentation(presentation);
}
return presentation;
};
// src/lib/rules-definition/chain/shared-extended.ts
function getCredentialIssuer(credential) {
if (!credential) {
return "";
}
return typeof credential.issuer === "string" ? credential.issuer : credential.issuer.id;
}
async function checkIssuerToSubjectId(credential, extendedCredentialSubject) {
const credentialIssuer = getCredentialIssuer(credential);
if (credentialIssuer !== extendedCredentialSubject?.id) {
return { verified: false, rule: invalidIssueSubject };
}
return { verified: true };
}
function checkCredentialChainIssuers(credentialToCheck) {
if (!credentialToCheck) {
throw new Error("Credential Chain Issuers are not defined.");
}
if (!credentialToCheck.dataCredential || !credentialToCheck.keyCredential || !credentialToCheck.companyPrefix) {
return false;
}
const organizationCredentialIssuer = getCredentialIssuer(normalizeCredential(credentialToCheck.dataCredential));
const keyCredentialIssuer = getCredentialIssuer(normalizeCredential(credentialToCheck.keyCredential));
const companyPrefixCredentialIssuer = getCredentialIssuer(normalizeCredential(credentialToCheck.companyPrefix));
const companyPrefixSubjectID = normalizeCredential(credentialToCheck.companyPrefix)?.credentialSubject.id;
if (!organizationCredentialIssuer || !keyCredentialIssuer || !companyPrefixCredentialIssuer || !companyPrefixSubjectID) {
return false;
}
if (organizationCredentialIssuer === keyCredentialIssuer) {
if (keyCredentialIssuer !== companyPrefixCredentialIssuer) {
if (keyCredentialIssuer !== companyPrefixSubjectID) {
return false;
}
}
} else {
return false;
}
return true;
}
function checkCredentialIssuers(credential, credentialToCompare) {
const credentialIssuer = getCredentialIssuer(credential);
const credentialToCompareIssuer = getCredentialIssuer(credentialToCompare);
if (credentialIssuer !== credentialToCompareIssuer) {
return false;
}
return true;
}
function compareLicenseValue(licenseValue, prefixValue) {
const prefixPosition = licenseValue.indexOf(prefixValue);
return licenseValue?.startsWith(prefixValue, prefixPosition);
}
// src/lib/rules-definition/chain/validate-extended-company-prefix.ts
async function validateExtendedCompanyPrefixCredential(credentialType, credentialChain) {
const gs1CredentialCheck = { credentialId: normalizeCredential(credentialChain.credential).id, credentialName: credentialType, verified: true, errors: [] };
const credential = normalizeCredential(credentialChain.credential);
const credentialSubject = credential.credentialSubject;
const extendedCredential = credentialChain.extendedCredentialChain?.credential ? normalizeCredential(credentialChain.extendedCredentialChain?.credential) : void 0;
if (!extendedCredential) {
gs1CredentialCheck.verified = false;
gs1CredentialCheck.errors.push(invalidExtendedCredentialMissing);
return gs1CredentialCheck;
}
const extendedCredentialSubject = extendedCredential?.credentialSubject;
const issuerResult = await checkIssuerToSubjectId(credential, extendedCredentialSubject);
if (!issuerResult.verified) {
if (!checkCredentialIssuers(credential, extendedCredential)) {
gs1CredentialCheck.verified = false;
gs1CredentialCheck.errors.push(invalidIssuer);
}
}
const keyValue = parseGS1DigitalLink(credentialSubject.id);
const companyPrefixLicenseValue = extendedCredentialSubject.licenseValue;
if (!keyValue.parsedValue || !compareLicenseValue(keyValue.parsedValue, companyPrefixLicenseValue)) {
gs1CredentialCheck.verified = false;
gs1CredentialCheck.errors.push(invalidLicenseValueFormat);
}
return gs1CredentialCheck;
}
// src/lib/rules-definition/chain/validate-extended-license-prefix.ts
var GS1_GLOBAL_DID = "did:web:id.gs1.org";
async function compareLicenseLengthsToExtended(credentialSubject, extendedCredentialSubject) {
const licenseValue = credentialSubject?.licenseValue;
const extendedLicenseValue = extendedCredentialSubject?.licenseValue;
if (!licenseValue || !extendedLicenseValue) {
return { verified: false, rule: invalidLicenseValueFormat };
}
if (compareLicenseValue(licenseValue, extendedLicenseValue)) {
if (licenseValue.length <= extendedLicenseValue.length) {
return { verified: false, rule: invalidLicenseValueStartPrefix };
}
} else {
return { verified: false, rule: invalidLicenseValueFormat };
}
return { verified: true };
}
async function validateExtendedLicensePrefix(credentialType, credentialChain) {
const gs1CredentialCheck = { credentialId: normalizeCredential(credentialChain.credential).id, credentialName: credentialType, verified: true, errors: [] };
const credential = normalizeCredential(credentialChain.credential);
const credentialSubject = credential.credentialSubject;
const extendedCredential = credentialChain.extendedCredentialChain?.credential ? normalizeCredential(credentialChain.extendedCredentialChain?.credential) : void 0;
const extendedCredentialSubject = extendedCredential?.credentialSubject;
if (!extendedCredential) {
gs1CredentialCheck.verified = false;
gs1CredentialCheck.errors.push(invalidExtendedCredentialMissing);
return gs1CredentialCheck;
}
const extendedCredentialIssuer = getCredentialIssuer(extendedCredential);
if (extendedCredentialIssuer !== GS1_GLOBAL_DID) {
gs1CredentialCheck.verified = false;
gs1CredentialCheck.errors.push(invalidIssueForPrefixLicense);
return gs1CredentialCheck;
}
const issuerResult = await checkIssuerToSubjectId(credential, extendedCredentialSubject);
if (!issuerResult.verified && issuerResult.rule) {
gs1CredentialCheck.errors.push(issuerResult.rule);
}
const compareLicenseLResult = await compareLicenseLengthsToExtended(credentialSubject, extendedCredentialSubject);
if (!compareLicenseLResult.verified && compareLicenseLResult.rule) {
gs1CredentialCheck.errors.push(compareLicenseLResult.rule);
}
if (gs1CredentialCheck.errors.length > 0) {
gs1CredentialCheck.verified = false;
}
return gs1CredentialCheck;
}
// src/lib/rules-definition/chain/validate-extended-data-key.ts
function setupDataCredentialChain(credentialChain) {
const dataCredential = normalizeCredential(credentialChain.credential);
const dataCredentialChain = {
isValid: true,
dataCredential,
dataCredentialSubject: dataCredential.credentialSubject,
KeyCredential: credentialChain.extendedCredentialChain?.credential ? { credential: normalizeCredential(credentialChain.extendedCredentialChain?.credential), extendedCredentialChain: credentialChain.extendedCredentialChain?.extendedCredentialChain } : void 0,
keyCredentialSubject: credentialChain.extendedCredentialChain?.credential ? normalizeCredential(credentialChain.extendedCredentialChain?.credential)?.credentialSubject : void 0
};
dataCredentialChain.companyPrefixCredential = dataCredentialChain.KeyCredential?.extendedCredentialChain;
dataCredentialChain.companyPrefixCredentialSubject = dataCredentialChain.companyPrefixCredential?.credential ? normalizeCredential(dataCredentialChain.companyPrefixCredential?.credential)?.credentialSubject : void 0;
if (!dataCredentialChain.dataCredentialSubject || !dataCredentialChain.keyCredentialSubject || !dataCredentialChain.companyPrefixCredentialSubject) {
dataCredentialChain.isValid = false;
}
return dataCredentialChain;
}
function validateDataToKeyCredential(keyCredentialSubject, dataCredentialSubject, valueToCheck = "") {
if (keyCredentialSubject === void 0) {
throw new Error("Key Credential Subject is not defined.");
}
if (dataCredentialSubject === void 0) {
throw new Error("Data Credential Subject is not defined.");
}
const keyDigitalLink = parseGS1DigitalLink(keyCredentialSubject.id);
const dataCredentialId = dataCredentialSubject.sameAs ? dataCredentialSubject.sameAs : dataCredentialSubject.id;
const dataCredentialIdDigitalLink = parseGS1DigitalLink(dataCredentialId);
if (keyDigitalLink.isValid && dataCredentialIdDigitalLink.isValid) {
if (keyDigitalLink.parsedValue !== dataCredentialIdDigitalLink.parsedValue) {
return false;
}
} else {
return false;
}
if (valueToCheck !== "") {
return keyDigitalLink.parsedValue === valueToCheck;
}
return true;
}
function checkDataCredentialIssuerChain(dataCredentialChain) {
if (dataCredentialChain === void 0)
throw new Error("Data Credential Chain is not defined.");
if (dataCredentialChain.dataCredential === void 0)
throw new Error("dataCredential is required to validate GS1 Credential Chain.");
if (dataCredentialChain.KeyCredential === void 0)
throw new Error("KeyCredential is required to validate GS1 Credential Chain.");
if (dataCredentialChain.companyPrefixCredential === void 0)
throw new Error("companyPrefixCredential is required to validate GS1 Credential Chain.");
return checkCredentialChainIssuers({
dataCredential: dataCredentialChain.dataCredential,
keyCredential: dataCredentialChain.KeyCredential.credential,
companyPrefix: dataCredentialChain.companyPrefixCredential.credential
});
}
async function validateExtendedKeyDataCredential(credentialType, credentialChain) {
const gs1CredentialCheck = { credentialId: normalizeCredential(credentialChain.credential).id, credentialName: credentialType, verified: false, errors: [] };
const dataCredentialChain = setupDataCredentialChain(credentialChain);
if (!dataCredentialChain.isValid) {
gs1CredentialCheck.errors.push(dataMissingToValidateCredentialChain);
return gs1CredentialCheck;
}
const checkDataToKey = validateDataToKeyCredential(dataCredentialChain.keyCredentialSubject, dataCredentialChain.dataCredentialSubject);
if (!checkDataToKey) {
gs1CredentialCheck.errors.push(dataMismatchBetweenDataKeyCredential);
}
if (!checkDataCredentialIssuerChain(dataCredentialChain)) {
gs1CredentialCheck.errors.push(invalidIssuer);
}
gs1CredentialCheck.verified = gs1CredentialCheck.errors.length === 0;
return gs1CredentialCheck;
}
async function validateExtendedKeyCredential(credentialType, credentialChain) {
const gs1CredentialCheck = { credentialId: normalizeCredential(credentialChain.credential).id, credentialName: credentialType, verified: false, errors: [] };
const dataCredentialChain = setupDataCredentialChain(credentialChain);
if (!dataCredentialChain.isValid) {
gs1CredentialCheck.errors.push(dataMissingToValidateCredentialChain);
return gs1CredentialCheck;
}
const checkDataToKey = validateDataToKeyCredential(
dataCredentialChain.keyCredentialSubject,
dataCredentialChain.dataCredentialSubject
);
if (!checkDataToKey) {
gs1CredentialCheck.errors.push(dataMismatchBetweenDataKeyCredential);
}
if (!checkDataCredentialIssuerChain(dataCredentialChain)) {
gs1CredentialCheck.errors.push(invalidIssuer);
}
gs1CredentialCheck.verified = gs1CredentialCheck.errors.length === 0;
return gs1CredentialCheck;
}
// src/lib/rules-definition/rules-manager.ts
var rulesEngineManager = {};
rulesEngineManager.prefixLicense = checkPrefixCredentialLicenseValue;
rulesEngineManager.GS1PrefixLicenseCredential = validateExtendedLicensePrefix;
rulesEngineManager.GS1CompanyPrefixLicenseCredential = validateExtendedCompanyPrefixCredential;
rulesEngineManager.KeyCredential = validateExtendedKeyCredential;
rulesEngineManager.KeyDataCredential = validateExtendedKeyDataCredential;
async function resolveExternalCredential(externalCredentialLoader, verifiablePresentation, url) {
try {
if (verifiablePresentation) {
const presentationVerifiableCredential = verifiablePresentation.verifiableCredential;
if (Array.isArray(presentationVerifiableCredential)) {
const credential = presentationVerifiableCredential.find((q) => q.id === url);
if (credential !== void 0) {
return { credential, inPresentation: true };
}
} else {
if (presentationVerifiableCredential.id === url) {
return { credential: presentationVerifiableCredential, inPresentation: true };
}
}
}
if (!url) {
throw new Error(`External Credential "${url}" can not be resolved.`);
}
const externalResult = await externalCredentialLoader(url);
return { credential: externalResult, inPresentation: false };
} catch (e) {
return { credential: void 0, inPresentation: false, error: `External Credential "${url}" can not be resolved.` };
}
}
async function buildCredentialChain(externalCredentialLoader, verifiablePresentation, credential) {
const credentialSubject = normalizeCredential(credential)?.credentialSubject;
const credentialSchema = getCredentialRuleSchemaChain(normalizeCredential(credential));
const credentialSubjectSchemaRule = credentialSchema;
const credentialChain = {
credential,
inPresentation: true,
schema: credentialSchema,
credentialSubjectSchema: credentialSubjectSchemaRule,
extendedCredentialChain: void 0
};
if (credentialSubjectSchemaRule) {
const extendsCredentialMetaData = credentialSubjectSchemaRule.extendsCredentialType;
if (extendsCredentialMetaData) {
const extendedCredentialValue = credentialSubject.extendsCredential || credentialSubject.keyAuthorization;
const extendedCredentialResult = await resolveExternalCredential(externalCredentialLoader, verifiablePresentation, extendedCredentialValue);
if (extendedCredentialResult.credential) {
const extendedCredentialChain = await buildCredentialChain(externalCredentialLoader, verifiablePresentation, extendedCredentialResult.credential);
if (extendedCredentialChain.credential) {
extendedCredentialChain.inPresentation = extendedCredentialResult.inPresentation;
credentialChain.extendedCredentialChain = extendedCredentialChain;
} else {
credentialChain.error = extendedCredentialResult.error;
}
} else {
credentialChain.error = extendedCredentialResult.error;
}
}
}
return credentialChain;
}
async function validateCredentialChain(externalCredentialVerification, credentialChain, validateChain) {
const credential = credentialChain.credential;
const decodedCredential = normalizeCredential(credential);
const credentialSchema = credentialChain.schema;
const credentialSchemaSubject = credentialSchema.extendsCredentialType;
const extendedCredential = credentialChain.extendedCredentialChain?.credential;
const decodedExtendedCredential = extendedCredential ? normalizeCredential(extendedCredential) : void 0;
const gs1CredentialCheck = { credentialId: decodedCredential.id, credentialName: credentialSchema.title, verified: true, errors: [] };
if (!credentialChain.inPresentation) {
const checkCredentialResult = await externalCredentialVerification(credentialChain.credential);
if (!checkCredentialResult.verified) {
gs1CredentialCheck.verified = false;
gs1CredentialCheck.errors = gs1CredentialCheck.errors.concat(checkCredentialResult.errors);
}
}
if (!decodedExtendedCredential) {
return gs1CredentialCheck;
}
const extendedCredentialSchema = getCredentialRuleSchemaChain(decodedExtendedCredential);
const extendedCredentialType = extendedCredentialSchema.title;
const parentInvalid = credentialSchemaSubject?.type.includes(extendedCredentialType);
if (!parentInvalid) {
gs1CredentialCheck.errors.push(invalidGS1CredentialTypes);
}
const credentialType = getCredentialType(decodedCredential.type);
const childIsValid = extendedCredentialSchema?.childCredential?.type.includes(credentialType.name);
if (!childIsValid) {
gs1CredentialCheck.errors.push(invalidGS1CredentialTypes);
}
const extendedCredentialType_rule = credentialSchemaSubject?.rule;
if (!extendedCredentialType_rule || typeof rulesEngineManager[extendedCredentialType_rule] === "undefined") {
gs1CredentialCheck.verified = false;
gs1CredentialCheck.errors.push(unsupportedCredentialChain);
return gs1CredentialCheck;
}
if (validateChain) {
const extendedCredentialValidationResult = await rulesEngineManager[extendedCredentialType_rule](credentialType, credentialChain);
if (!extendedCredentialValidationResult.verified) {
gs1CredentialCheck.errors = gs1CredentialCheck.errors.concat(extendedCredentialValidationResult.errors);
}
if (gs1CredentialCheck.errors.length > 0) {
gs1CredentialCheck.verified = false;
}
}
if (credentialChain.extendedCredentialChain?.extendedCredentialChain) {
const validateExtendedCredentialResult = await validateCredentialChain(externalCredentialVerification, credentialChain.extendedCredentialChain, false);
if (!credentialChain.extendedCredentialChain?.extendedCredentialChain.inPresentation) {
const resolvedCredentialMetaData = credentialChain.extendedCredentialChain?.extendedCredentialChain;
gs1CredentialCheck.resolvedCredential = {
credentialId: normalizeCredential(resolvedCredentialMetaData?.credential)?.id,
credentialName: resolvedCredentialMetaData?.schema?.title,
verified: validateExtendedCredentialResult.verified,
errors: validateExtendedCredentialResult.errors
};
}
if (!validateExtendedCredentialResult.verified) {
gs1CredentialCheck.errors = gs1CredentialCheck.errors.concat(validateExtendedCredentialResult.errors);
}
} else {
if (extendedCredentialType !== GS1_PREFIX_LICENSE_CREDENTIAL) {
gs1CredentialCheck.errors.push(invalidRootCredentialType);
}
if (credentialChain.extendedCredentialChain && credentialChain.extendedCredentialChain.credentialSubjectSchema) {
const validateExtendedCredentialResult = await validateCredentialChain(externalCredentialVerification, credentialChain.extendedCredentialChain, false);
if (!credentialChain.extendedCredentialChain.inPresentation) {
const resolvedCredentialMetaData = credentialChain.extendedCredentialChain;
gs1CredentialCheck.resolvedCredential = {
credentialId: normalizeCredential(resolvedCredentialMetaData?.credential)?.id,
credentialName: resolvedCredentialMetaData?.schema?.title,
verified: validateExtendedCredentialResult.verified,
errors: validateExtendedCredentialResult.errors
};
}
if (!validateExtendedCredentialResult.verified) {
gs1CredentialCheck.verified = false;
gs1CredentialCheck.errors = gs1CredentialCheck.errors.concat(validateExtendedCredentialResult.errors);
}
}
}
return gs1CredentialCheck;
}
// src/lib/rules-definition/subject/check-credential-alternative-license.ts
function checkCredentialAlternativeLicenseValue(credentialSubject) {
const value = credentialSubject.licenseValue;
const altValue = credentialSubject.alternativeLicenseValue;
if (!value) {
return { verified: false, rule: invalidLicenseValueFormat };
}
if (value.startsWith("0")) {
if (!altValue) {
return { verified: false, rule: invalidAlternativeLicenseValue };
}
if (value !== "0" + altValue) {
return { verified: false, rule: invalidAlternativeLicenseNotCompatible };
}
} else {
if (altValue !== void 0) {
return { verified: false, rule: invalidAlternativeLicenseNotSupported };
}
}
return { verified: true };
}
// src/lib/schema/ajv-gs1-extension.ts
var ajv = new Ajv({
strict: false,
allErrors: true
});
addFormats(ajv);
ajvErrors(ajv);
var compileSchema = function(schemaToValidate) {
const maybeExistingSchema = ajv.getSchema(schemaToValidate.$id);
let compiledSchemaValidator = maybeExistingSchema;
if (compiledSchemaValidator === void 0) {
compiledSchemaValidator = ajv.compile(schemaToValidate);
}
return compiledSchemaValidator;
};
ajv.addKeyword({
keyword: "altLicenseValidation",
type: "object",
errors: true,
async: false,
validate: function customRuleValidation(schema, data, parentSchema, dataCxt) {
const gs1RuleCheckResult = checkCredentialAlternativeLicenseValue(data);
if (!gs1RuleCheckResult.verified) {
customRuleValidation.errors = createJsonSchemaError("altLicenseValidation", gs1RuleCheckResult, dataCxt);
}
return gs1RuleCheckResult.verified;
}
});
ajv.addKeyword({
keyword: "digitalLink",
type: "object",
errors: true,
async: false,
validate: function customRuleValidation2(schema, data, parentSchema, dataCxt) {
const gs1RuleCheckResult = checkCredentialSubjectIdDigitalLink(data);
if (!gs1RuleCheckResult.verified) {
customRuleValidation2.errors = createJsonSchemaError("digitalLink", gs1RuleCheckResult, dataCxt);
}
return gs1RuleCheckResult.verified;
}
});
ajv.addKeyword({
keyword: "digitalLinkSameAs",
type: "object",
errors: true,
async: false,
validate: function customRuleValidation3(schema, data, parentSchema, dataCxt) {
const gs1RuleCheckResult = checkCredentialSameAsDigitalLink(data);
if (!gs1RuleCheckResult.verified) {
customRuleValidation3.errors = createJsonSchemaError("digitalLink", gs1RuleCheckResult, dataCxt);
}
return gs1RuleCheckResult.verified;
}
});
var createJsonSchemaError = function(keyword, gs1RuleCheckResult, dataCxt) {
const errors = [];
errors.push({
instancePath: dataCxt?.instancePath,
schemaPath: "",
keyword,
params: {
keyword,
isGS1Error: true,
gs1Rule: gs1RuleCheckResult.rule
},
message: gs1RuleCheckResult.rule?.rule
});
return errors;
};
// src/lib/schema/validate-schema.ts
async function checkSchema(schemaToValidate, credential) {
const gs1CredentialCheck = { credentialId: credential.id, credentialName: schemaToValidate.title ? schemaToValidate.title : "unknown", verified: true, errors: [] };
const credentialToValidate = { ...credential, credentialSubject: { ...credential.credentialSubject } };
const ajvSchenma = compileSchema(schemaToValidate);
const valid = ajvSchenma(credentialToValidate);
if (!valid) {
gs1CredentialCheck.verified = false;
ajvSchenma.errors?.forEach((error) => {
const jsonSchemaError = error.params;
if (jsonSchemaError.isGS1Error && jsonSchemaError.gs1Rule !== void 0) {
gs1CredentialCheck.errors.push(jsonSchemaError.gs1Rule);
} else {
const jsonSchemaErrorRule = { code: errorResolveCredentialCode, rule: `${error.instancePath} ${error.message}` };
gs1CredentialCheck.errors.push(jsonSchemaErrorRule);
}
});
}
return gs1CredentialCheck;
}
// src/lib/gs1-verification-service.ts
async function checkGS1Credentials(validatorRequest, verifiablePresentation, credential) {
const decodedCredential = normalizeCredential(credential);
const decodedPresentation = normalizePresentation(verifiablePresentation);
const credentialSubject = decodedCredential?.credentialSubject;
if (!decodedCredential || !credentialSubject) {
throw new Error("No Credential in Presentation");
}
if (!validatorRequest || !validatorRequest.gs1DocumentResolver) {
throw new Error("Document Resolver Callback must be provided to validate GS1 Credentials");
}
const externalCredentialLoader = validatorRequest.gs1DocumentResolver.externalCredentialLoader;
const externalCredentialVerification = validatorRequest.gs1DocumentResolver.externalCredentialVerification;
const jsonSchemaLoader = validatorRequest.gs1DocumentResolver.externalJsonSchemaLoader;
const credentialSchema = getCredentialRuleSchema(jsonSchemaLoader, decodedCredential, validatorRequest.fullJsonSchemaValidationOn);
if (credentialSchema.$id === genericCredentialSchema.$id) {
return { credentialId: decodedCredential.id, credentialName: "unknown", verified: true, errors: [] };
}
const gs1CredentialCheck = { credentialId: decodedCredential.id, credentialName: credentialSchema.title ? credentialSchema.title : "unknown", verified: true, errors: [] };
const schemaCheckResult = await checkSchema(credentialSchema, decodedCredential);
if (!schemaCheckResult.verified) {
gs1CredentialCheck.errors = schemaCheckResult.errors;
}
const credentialChain = await buildCredentialChain(externalCredentialLoader, decodedPresentation, decodedCredential);
if (!credentialChain.error) {
const extendedCredentialResult = await validateCredentialChain(externalCredentialVerification, credentialChain, true);
gs1CredentialCheck.resolvedCredential = extendedCredentialResult.resolvedCredential;
if (!extendedCredentialResult.verified) {
gs1CredentialCheck.errors = gs1CredentialCheck.errors.concat(extendedCredentialResult.errors);
}
} else {
gs1CredentialCheck.errors.push({ code: errorResolveCredentialCode, rule: credentialChain.error });
}
if (gs1CredentialCheck.errors.length > 0) {
gs1CredentialCheck.verified = false;
}
return gs1CredentialCheck;
}
async function checkGS1CredentialWithoutPresentation(validatorRequest, verifiableCredential) {
const decodedCredential = normalizeCredential(verifiableCredential);
const verifiablePresentation = { verifiableCredential: decodedCredential };
return await checkGS1Credentials(validatorRequest, verifiablePresentation, verifiableCredential);
}
async function checkGS1CredentialPresentationValidation(validatorRequest, verifiablePresentation) {
const gs1CredentialCheck = { verified: true, result: [] };
const decodedPresentation = normalizePresentation(verifiablePresentation);
const presentationCredentials = decodedPresentation.verifiableCredential;
if (Array.isArray(presentationCredentials)) {
if (!validatorRequest.gs1DocumentResolver || !validatorRequest.gs1DocumentResolver.externalCredentialLoader) {
throw new Error("Validation Document Resolver Callback must be provided to validate GS1 Credentials");
}
const externalCredentialLoader = validatorRequest.gs1DocumentResolver.externalCredentialLoader;
const presentationToValidateVC = structuredClone(presentationCredentials);
for (const credential of presentationCredentials) {
const extendedCredentialValue = credential.credentialSubject.extendsCredential;
const extendedCredentialResult = await resolveExternalCredential(externalCredentialLoader, decodedPresentation, extendedCredentialValue);
if (extendedCredentialResult?.credential) {
if (!extendedCredentialResult.inPresentation) {
presentationToValidateVC.unshift(extendedCredentialResult.credential);
}
}
}
const presentationToValidate = structuredClone(decodedPresentation);
presentationToValidate.verifiableCredential = presentationToValidateVC;
for (const credential of presentationToValidate.verifiableCredential) {
const credentialResult = await checkGS1Credentials(validatorRequest, presentationToValidate, credential);
gs1CredentialCheck.result.push(credentialResult);
if (!credentialResult.verified) {
gs1CredentialCheck.verified = false;
}
}
} else {
const credentialResult = await checkGS1Credentials(validatorRequest, decodedPresentation, presentationCredentials);
gs1CredentialCheck.result.push(credentialResult);
if (!credentialResult.verified) {
gs1CredentialCheck.verified = false;
}
if (credentialResult.resolvedCredential) {
gs1CredentialCheck.result.push(credentialResult.resolvedCredential);
}
}
return gs1CredentialCheck;
}
export { GS1_COMPANY_PREFIX_LICENSE_CREDENTIAL, GS1_IDENTIFICATION_KEY_LICENSE_CREDENTIAL, GS1_PREFIX_LICENSE_CREDENTIAL, KEY_CREDENTIAL, ORGANIZATION_DATA_CREDENTIAL, PRODUCT_DATA_CREDENTIAL, UNKNOWN_VALUE, checkGS1CredentialPresentationValidation, checkGS1CredentialWithoutPresentation, getCredentialRuleSchema, getCredentialRuleSchemaChain, getCredentialType, verificationErrorCode };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=index.js.map