UNPKG

@sphereon/ssi-sdk.vc-status-list

Version:

Sphereon SSI-SDK plugin for Status List management, like StatusList2021.

1,225 lines (1,212 loc) • 63.4 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { Status2021: () => Status2021, StatusOAuth: () => StatusOAuth, checkStatusForCredential: () => checkStatusForCredential, checkStatusIndexFromStatusListCredential: () => checkStatusIndexFromStatusListCredential, createCredentialStatusFromStatusList: () => createCredentialStatusFromStatusList, createNewStatusList: () => createNewStatusList, determineStatusListType: () => determineStatusListType, extractCredentialDetails: () => extractCredentialDetails, fetchStatusListCredential: () => fetchStatusListCredential, simpleCheckStatusFromStatusListUrl: () => simpleCheckStatusFromStatusListUrl, statusList2021ToVerifiableCredential: () => statusList2021ToVerifiableCredential, statusPluginStatusFunction: () => statusPluginStatusFunction, toStatusListDetails: () => toStatusListDetails, updateStatusIndexFromStatusListCredential: () => updateStatusIndexFromStatusListCredential, updateStatusListIndexFromEncodedList: () => updateStatusListIndexFromEncodedList, vcLibCheckStatusFunction: () => vcLibCheckStatusFunction }); module.exports = __toCommonJS(index_exports); // src/types/index.ts var StatusOAuth = /* @__PURE__ */ (function(StatusOAuth2) { StatusOAuth2[StatusOAuth2["Valid"] = 0] = "Valid"; StatusOAuth2[StatusOAuth2["Invalid"] = 1] = "Invalid"; StatusOAuth2[StatusOAuth2["Suspended"] = 2] = "Suspended"; return StatusOAuth2; })({}); var Status2021 = /* @__PURE__ */ (function(Status20212) { Status20212[Status20212["Valid"] = 0] = "Valid"; Status20212[Status20212["Invalid"] = 1] = "Invalid"; return Status20212; })({}); // src/functions.ts var import_ssi_types7 = require("@sphereon/ssi-types"); var import_vc_status_list2 = require("@sphereon/vc-status-list"); // src/utils.ts var import_ssi_types = require("@sphereon/ssi-types"); var import_jwt_decode = require("jwt-decode"); function getAssertedStatusListType(type) { const assertedType = type ?? import_ssi_types.StatusListType.StatusList2021; if (![ import_ssi_types.StatusListType.StatusList2021, import_ssi_types.StatusListType.OAuthStatusList, import_ssi_types.StatusListType.BitstringStatusList ].includes(assertedType)) { throw Error(`StatusList type ${assertedType} is not supported (yet)`); } return assertedType; } __name(getAssertedStatusListType, "getAssertedStatusListType"); function getAssertedValue(name, value) { if (value === void 0 || value === null) { throw Error(`Missing required ${name} value`); } return value; } __name(getAssertedValue, "getAssertedValue"); function getAssertedValues(args) { const type = getAssertedStatusListType(args?.type); const id = getAssertedValue("id", args.id); const issuer = getAssertedValue("issuer", args.issuer); return { id, issuer, type }; } __name(getAssertedValues, "getAssertedValues"); function getAssertedProperty(propertyName, obj) { if (!(propertyName in obj)) { throw Error(`The input object does not contain required property: ${propertyName}`); } return getAssertedValue(propertyName, obj[propertyName]); } __name(getAssertedProperty, "getAssertedProperty"); var ValidProofTypeMap = /* @__PURE__ */ new Map([ [ import_ssi_types.StatusListType.StatusList2021, [ "jwt", "lds" ] ], [ import_ssi_types.StatusListType.OAuthStatusList, [ "jwt", "cbor" ] ], [ import_ssi_types.StatusListType.BitstringStatusList, [ "lds", "vc+jwt" ] ] ]); function assertValidProofType(type, proofFormat) { const validProofTypes = ValidProofTypeMap.get(type); if (!validProofTypes?.includes(proofFormat)) { throw Error(`Invalid proof format '${proofFormat}' for status list type ${type}`); } } __name(assertValidProofType, "assertValidProofType"); function determineStatusListType(credential) { const proofFormat = determineProofFormat(credential); switch (proofFormat) { case "jwt": return determineJwtStatusListType(credential); case "lds": return determineLdsStatusListType(credential); case "cbor": return import_ssi_types.StatusListType.OAuthStatusList; default: throw new Error("Cannot determine status list type from credential payload"); } } __name(determineStatusListType, "determineStatusListType"); function determineJwtStatusListType(credential) { const payload = (0, import_jwt_decode.jwtDecode)(credential); if ("status_list" in payload) { return import_ssi_types.StatusListType.OAuthStatusList; } if ("credentialSubject" in payload) { return getStatusListTypeFromSubject(payload.credentialSubject); } if ("vc" in payload && "credentialSubject" in payload.vc) { return getStatusListTypeFromSubject(payload.vc.credentialSubject); } throw new Error("Invalid status list credential: credentialSubject not found"); } __name(determineJwtStatusListType, "determineJwtStatusListType"); function determineLdsStatusListType(credential) { const uniform = import_ssi_types.CredentialMapper.toUniformCredential(credential); const statusListType = uniform.type.find((type) => Object.values(import_ssi_types.StatusListType).some((statusType) => type.includes(statusType))); if (!statusListType) { throw new Error("Invalid status list credential type"); } return statusListType.replace("Credential", ""); } __name(determineLdsStatusListType, "determineLdsStatusListType"); function getStatusListTypeFromSubject(credentialSubject) { switch (credentialSubject.type) { case "StatusList2021": return import_ssi_types.StatusListType.StatusList2021; case "BitstringStatusList": return import_ssi_types.StatusListType.BitstringStatusList; default: throw new Error(`Unknown credential subject type: ${credentialSubject.type}`); } } __name(getStatusListTypeFromSubject, "getStatusListTypeFromSubject"); function determineProofFormat(credential) { const type = import_ssi_types.CredentialMapper.detectDocumentType(credential); switch (type) { case import_ssi_types.DocumentFormat.JWT: return "jwt"; case import_ssi_types.DocumentFormat.MSO_MDOC: return "cbor"; case import_ssi_types.DocumentFormat.JSONLD: return "lds"; default: throw Error("Cannot determine credential payload type"); } } __name(determineProofFormat, "determineProofFormat"); function ensureDate(value) { if (value === void 0 || value === null) { return void 0; } if (value instanceof Date) { return value; } if (typeof value === "string") { if (value.trim() === "") { return void 0; } const date = new Date(value); if (isNaN(date.getTime())) { return void 0; } return date; } return void 0; } __name(ensureDate, "ensureDate"); // src/impl/StatusList2021.ts var import_ssi_types2 = require("@sphereon/ssi-types"); var import_vc_status_list = require("@sphereon/vc-status-list"); var DEFAULT_LIST_LENGTH = 25e4; var DEFAULT_PROOF_FORMAT = "lds"; var StatusList2021Implementation = class { static { __name(this, "StatusList2021Implementation"); } async createNewStatusList(args, context) { const length = args?.length ?? DEFAULT_LIST_LENGTH; const proofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT; assertValidProofType(import_ssi_types2.StatusListType.StatusList2021, proofFormat); const veramoProofFormat = proofFormat; const { issuer, id } = args; const correlationId = getAssertedValue("correlationId", args.correlationId); const list = new import_vc_status_list.StatusList({ length }); const encodedList = await list.encode(); const statusPurpose = "revocation"; const statusListCredential = await this.createVerifiableCredential({ ...args, encodedList, proofFormat: veramoProofFormat }, context); return { encodedList, statusListCredential, statusList2021: { statusPurpose, indexingDirection: "rightToLeft", credentialIdMode: import_ssi_types2.StatusListCredentialIdMode.ISSUANCE }, length, type: import_ssi_types2.StatusListType.StatusList2021, proofFormat, id, correlationId, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } async updateStatusListIndex(args, context) { const credential = args.statusListCredential; const uniform = import_ssi_types2.CredentialMapper.toUniformCredential(credential); const { issuer, credentialSubject } = uniform; const id = getAssertedValue("id", uniform.id); const origEncodedList = getAssertedProperty("encodedList", credentialSubject); const index = typeof args.statusListIndex === "number" ? args.statusListIndex : parseInt(args.statusListIndex); const statusList = await import_vc_status_list.StatusList.decode({ encodedList: origEncodedList }); statusList.setStatus(index, args.value != 0); const encodedList = await statusList.encode(); const proofFormat = import_ssi_types2.CredentialMapper.detectDocumentType(credential) === import_ssi_types2.DocumentFormat.JWT ? "jwt" : "lds"; const updatedCredential = await this.createVerifiableCredential({ ...args, id, issuer, encodedList, proofFormat }, context); if (!("statusPurpose" in credentialSubject)) { return Promise.reject(Error("statusPurpose is required in credentialSubject for StatusList2021")); } return { statusListCredential: updatedCredential, encodedList, statusList2021: { statusPurpose: credentialSubject.statusPurpose, indexingDirection: "rightToLeft", credentialIdMode: import_ssi_types2.StatusListCredentialIdMode.ISSUANCE }, length: statusList.length - 1, type: import_ssi_types2.StatusListType.StatusList2021, proofFormat, id, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } async updateStatusListFromEncodedList(args, context) { if (!args.statusList2021) { throw new Error("statusList2021 options required for type StatusList2021"); } const proofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT; assertValidProofType(import_ssi_types2.StatusListType.StatusList2021, proofFormat); const veramoProofFormat = proofFormat; const { issuer, id } = getAssertedValues(args); const statusList = await import_vc_status_list.StatusList.decode({ encodedList: args.encodedList }); const index = typeof args.statusListIndex === "number" ? args.statusListIndex : parseInt(args.statusListIndex); statusList.setStatus(index, args.value !== 0); const newEncodedList = await statusList.encode(); const credential = await this.createVerifiableCredential({ id, issuer, encodedList: newEncodedList, proofFormat: veramoProofFormat, keyRef: args.keyRef }, context); return { type: import_ssi_types2.StatusListType.StatusList2021, statusListCredential: credential, encodedList: newEncodedList, statusList2021: { statusPurpose: args.statusList2021.statusPurpose, indexingDirection: "rightToLeft", credentialIdMode: import_ssi_types2.StatusListCredentialIdMode.ISSUANCE }, length: statusList.length, proofFormat: args.proofFormat ?? "lds", id, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } async checkStatusIndex(args) { const uniform = import_ssi_types2.CredentialMapper.toUniformCredential(args.statusListCredential); const { credentialSubject } = uniform; const encodedList = getAssertedProperty("encodedList", credentialSubject); const statusList = await import_vc_status_list.StatusList.decode({ encodedList }); const status = statusList.getStatus(typeof args.statusListIndex === "number" ? args.statusListIndex : parseInt(args.statusListIndex)); return status ? Status2021.Invalid : Status2021.Valid; } /** * Performs the initial parsing of a StatusListCredential. * This method handles expensive operations like JWT/CWT decoding once. * It extracts all details available from the credential payload itself. */ async extractCredentialDetails(credential) { const uniform = import_ssi_types2.CredentialMapper.toUniformCredential(credential); const { issuer, credentialSubject } = uniform; const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject; return { id: getAssertedValue("id", uniform.id), issuer, encodedList: getAssertedProperty("encodedList", subject) }; } async toStatusListDetails(args) { if ("statusListCredential" in args) { const { statusListCredential, correlationId, driverType } = args; const uniform = import_ssi_types2.CredentialMapper.toUniformCredential(statusListCredential); const { issuer, credentialSubject } = uniform; const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject; const id = getAssertedValue("id", uniform.id); const encodedList = getAssertedProperty("encodedList", subject); const statusPurpose = getAssertedProperty("statusPurpose", subject); const proofFormat = import_ssi_types2.CredentialMapper.detectDocumentType(statusListCredential) === import_ssi_types2.DocumentFormat.JWT ? "jwt" : "lds"; const list = await import_vc_status_list.StatusList.decode({ encodedList }); return { id, encodedList, issuer, type: import_ssi_types2.StatusListType.StatusList2021, proofFormat, length: list.length, statusListCredential, statuslistContentType: this.buildContentType(proofFormat), correlationId, driverType, indexingDirection: "rightToLeft", statusPurpose, statusList2021: { indexingDirection: "rightToLeft", statusPurpose, credentialIdMode: import_ssi_types2.StatusListCredentialIdMode.ISSUANCE } }; } else { const { extractedDetails, statusListEntity } = args; const statusList2021Entity = statusListEntity; const proofFormat = import_ssi_types2.CredentialMapper.detectDocumentType(statusListEntity.statusListCredential) === import_ssi_types2.DocumentFormat.JWT ? "jwt" : "lds"; const list = await import_vc_status_list.StatusList.decode({ encodedList: extractedDetails.encodedList }); return { id: extractedDetails.id, encodedList: extractedDetails.encodedList, issuer: extractedDetails.issuer, type: import_ssi_types2.StatusListType.StatusList2021, proofFormat, length: list.length, statusListCredential: statusListEntity.statusListCredential, statuslistContentType: this.buildContentType(proofFormat), correlationId: statusListEntity.correlationId, driverType: statusListEntity.driverType, indexingDirection: statusList2021Entity.indexingDirection, statusPurpose: statusList2021Entity.statusPurpose, statusList2021: { indexingDirection: statusList2021Entity.indexingDirection, statusPurpose: statusList2021Entity.statusPurpose, credentialIdMode: import_ssi_types2.StatusListCredentialIdMode.ISSUANCE } }; } } async createCredentialStatus(args) { const { statusList, statusListIndex } = args; const statusList2021 = statusList; return { id: `${statusList.id}#${statusListIndex}`, type: "StatusList2021Entry", statusPurpose: statusList2021.statusPurpose ?? "revocation", statusListIndex: "" + statusListIndex, statusListCredential: statusList.id }; } async createVerifiableCredential(args, context) { const identifier = await context.agent.identifierManagedGet({ identifier: typeof args.issuer === "string" ? args.issuer : args.issuer.id, vmRelationship: "assertionMethod", offlineWhenNoDIDRegistered: true }); const credential = { "@context": [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/vc/status-list/2021/v1" ], id: args.id, issuer: args.issuer, type: [ "VerifiableCredential", "StatusList2021Credential" ], credentialSubject: { id: args.id, type: "StatusList2021", statusPurpose: "revocation", encodedList: args.encodedList } }; const verifiableCredential = await context.agent.createVerifiableCredential({ credential, keyRef: args.keyRef ?? identifier.kmsKeyRef, proofFormat: args.proofFormat, fetchRemoteContexts: true }); return import_ssi_types2.CredentialMapper.toWrappedVerifiableCredential(verifiableCredential).original; } buildContentType(proofFormat) { switch (proofFormat) { case "jwt": return `application/statuslist+jwt`; case "cbor": return `application/statuslist+cwt`; case "lds": return "application/statuslist+ld+json"; default: throw Error(`Unsupported content type '${proofFormat}' for status lists`); } } }; // src/impl/OAuthStatusList.ts var import_ssi_types4 = require("@sphereon/ssi-types"); var import_jwt_status_list3 = require("@sd-jwt/jwt-status-list"); // src/impl/encoding/jwt.ts var import_ssi_types3 = require("@sphereon/ssi-types"); var import_jwt_status_list = require("@sd-jwt/jwt-status-list"); var import_base64url = __toESM(require("base64url"), 1); // src/impl/encoding/common.ts var resolveIdentifier = /* @__PURE__ */ __name(async (context, issuer, keyRef) => { return await context.agent.identifierManagedGet({ identifier: issuer, vmRelationship: "assertionMethod", offlineWhenNoDIDRegistered: true, ...keyRef && { kmsKeyRef: keyRef } }); }, "resolveIdentifier"); // src/impl/encoding/jwt.ts var import_ssi_sdk_ext = require("@sphereon/ssi-sdk-ext.identifier-resolution"); var STATUS_LIST_JWT_TYP = "statuslist+jwt"; var createSignedJwt = /* @__PURE__ */ __name(async (context, statusList, issuerString, id, expiresAt, keyRef) => { const identifier = await resolveIdentifier(context, issuerString, keyRef); const resolution = await (0, import_ssi_sdk_ext.ensureManagedIdentifierResult)(identifier, context); const payload = { iss: issuerString, sub: id, iat: Math.floor(Date.now() / 1e3), ...expiresAt && { exp: Math.floor(expiresAt.getTime() / 1e3) } }; const header = { alg: getSigningAlgo(resolution.key.type), typ: STATUS_LIST_JWT_TYP }; const values = (0, import_jwt_status_list.createHeaderAndPayload)(statusList, payload, header); const signedJwt = await context.agent.jwtCreateJwsCompactSignature({ issuer: { ...identifier, noIssPayloadUpdate: false }, protectedHeader: values.header, payload: values.payload }); return { statusListCredential: signedJwt.jwt, encodedList: values.payload.status_list.lst }; }, "createSignedJwt"); var decodeStatusListJWT = /* @__PURE__ */ __name((jwt) => { const [, payloadBase64] = jwt.split("."); const payload = JSON.parse(import_base64url.default.decode(payloadBase64)); if (!payload.iss || !payload.sub || !payload.status_list) { throw new Error("Missing required fields in JWT payload"); } const statusList = import_jwt_status_list.StatusList.decompressStatusList(payload.status_list.lst, payload.status_list.bits); return { issuer: payload.iss, id: payload.sub, statusList, exp: payload.exp, ttl: payload.ttl, iat: payload.iat }; }, "decodeStatusListJWT"); var getSigningAlgo = /* @__PURE__ */ __name((type) => { switch (type) { case "Ed25519": return import_ssi_types3.JoseSignatureAlgorithm.EdDSA; case "Secp256k1": return import_ssi_types3.JoseSignatureAlgorithm.ES256K; case "Secp256r1": return import_ssi_types3.JoseSignatureAlgorithm.ES256; case "RSA": return import_ssi_types3.JoseSignatureAlgorithm.RS256; default: throw Error("Key type not yet supported"); } }, "getSigningAlgo"); // src/impl/encoding/cbor.ts var import_jwt_status_list2 = require("@sd-jwt/jwt-status-list"); var import_pako = require("pako"); var import_kmp_mdoc_core = __toESM(require("@sphereon/kmp-mdoc-core"), 1); var import_base64url2 = __toESM(require("base64url"), 1); var { com, kotlin } = import_kmp_mdoc_core.default; var CborByteString = import_kmp_mdoc_core.default.com.sphereon.cbor.CborByteString; var CborUInt = import_kmp_mdoc_core.default.com.sphereon.cbor.CborUInt; var CborString = import_kmp_mdoc_core.default.com.sphereon.cbor.CborString; var decompressRawStatusList = import_jwt_status_list2.StatusList.decodeStatusList.bind(import_jwt_status_list2.StatusList); var CWT_CLAIMS = { SUBJECT: 2, ISSUER: 1, ISSUED_AT: 6, EXPIRATION: 4, TIME_TO_LIVE: 65534, STATUS_LIST: 65533 }; var createSignedCbor = /* @__PURE__ */ __name(async (context, statusList, issuerString, id, expiresAt, keyRef) => { const identifier = await resolveIdentifier(context, issuerString, keyRef); const encodeStatusList = statusList.encodeStatusList(); const compressedList = (0, import_pako.deflate)(encodeStatusList, { level: 9 }); const compressedBytes = new Int8Array(compressedList); const statusListMap = new com.sphereon.cbor.CborMap(kotlin.collections.KtMutableMap.fromJsMap(/* @__PURE__ */ new Map([ [ new com.sphereon.cbor.CborString("bits"), new com.sphereon.cbor.CborUInt(com.sphereon.kmp.LongKMP.fromNumber(statusList.getBitsPerStatus())) ], [ new com.sphereon.cbor.CborString("lst"), new com.sphereon.cbor.CborByteString(compressedBytes) ] ]))); const protectedHeader = new com.sphereon.cbor.CborMap(kotlin.collections.KtMutableMap.fromJsMap(/* @__PURE__ */ new Map([ [ new com.sphereon.cbor.CborUInt(com.sphereon.kmp.LongKMP.fromNumber(16)), new com.sphereon.cbor.CborString("statuslist+cwt") ] ]))); const protectedHeaderEncoded = com.sphereon.cbor.Cbor.encode(protectedHeader); const claimsMap = buildClaimsMap(id, issuerString, statusListMap, expiresAt); const claimsEncoded = com.sphereon.cbor.Cbor.encode(claimsMap); const signedCWT = await context.agent.keyManagerSign({ keyRef: identifier.kmsKeyRef, data: import_base64url2.default.encode(Buffer.from(claimsEncoded)), encoding: void 0 }); const protectedHeaderEncodedInt8 = new Int8Array(protectedHeaderEncoded); const claimsEncodedInt8 = new Int8Array(claimsEncoded); const signatureBytes = import_base64url2.default.decode(signedCWT); const signatureInt8 = new Int8Array(Buffer.from(signatureBytes)); const cwtArrayElements = [ new CborByteString(protectedHeaderEncodedInt8), new CborByteString(claimsEncodedInt8), new CborByteString(signatureInt8) ]; const cwtArray = new com.sphereon.cbor.CborArray(kotlin.collections.KtMutableList.fromJsArray(cwtArrayElements)); const cwtEncoded = com.sphereon.cbor.Cbor.encode(cwtArray); const cwtBuffer = Buffer.from(cwtEncoded); return { statusListCredential: import_base64url2.default.encode(cwtBuffer), encodedList: import_base64url2.default.encode(compressedList) }; }, "createSignedCbor"); function buildClaimsMap(id, issuerString, statusListMap, expiresAt) { const ttl = 65535; const claimsEntries = [ [ new CborUInt(com.sphereon.kmp.LongKMP.fromNumber(CWT_CLAIMS.SUBJECT)), new com.sphereon.cbor.CborString(id) ], [ new CborUInt(com.sphereon.kmp.LongKMP.fromNumber(CWT_CLAIMS.ISSUER)), new com.sphereon.cbor.CborString(issuerString) ], [ new CborUInt(com.sphereon.kmp.LongKMP.fromNumber(CWT_CLAIMS.ISSUED_AT)), new CborUInt(com.sphereon.kmp.LongKMP.fromNumber(Math.floor(Date.now() / 1e3))) ] ]; if (expiresAt) { claimsEntries.push([ new com.sphereon.cbor.CborUInt(com.sphereon.kmp.LongKMP.fromNumber(CWT_CLAIMS.EXPIRATION)), new com.sphereon.cbor.CborUInt(com.sphereon.kmp.LongKMP.fromNumber(Math.floor(expiresAt.getTime() / 1e3))) ]); } if (ttl) { claimsEntries.push([ new com.sphereon.cbor.CborUInt(com.sphereon.kmp.LongKMP.fromNumber(CWT_CLAIMS.TIME_TO_LIVE)), new com.sphereon.cbor.CborUInt(com.sphereon.kmp.LongKMP.fromNumber(ttl)) ]); } claimsEntries.push([ new com.sphereon.cbor.CborUInt(com.sphereon.kmp.LongKMP.fromNumber(CWT_CLAIMS.STATUS_LIST)), statusListMap ]); const claimsMap = new com.sphereon.cbor.CborMap(kotlin.collections.KtMutableMap.fromJsMap(new Map(claimsEntries))); return claimsMap; } __name(buildClaimsMap, "buildClaimsMap"); var getCborValueFromMap = /* @__PURE__ */ __name((map, key) => { const value = getCborOptionalValueFromMap(map, key); if (value === void 0) { throw new Error(`Required claim ${key} not found`); } return value; }, "getCborValueFromMap"); var getCborOptionalValueFromMap = /* @__PURE__ */ __name((map, key) => { const value = map.get(new CborUInt(com.sphereon.kmp.LongKMP.fromNumber(key))); if (!value) { return void 0; } return value.value; }, "getCborOptionalValueFromMap"); var decodeStatusListCWT = /* @__PURE__ */ __name((cwt) => { const encodedCbor = import_base64url2.default.toBuffer(cwt); const encodedCborArray = new Int8Array(encodedCbor); const decodedCbor = com.sphereon.cbor.Cbor.decode(encodedCborArray); if (!(decodedCbor instanceof com.sphereon.cbor.CborArray)) { throw new Error("Invalid CWT format: Expected a CBOR array"); } const [, payload] = decodedCbor.value.asJsArrayView(); if (!(payload instanceof com.sphereon.cbor.CborByteString)) { throw new Error("Invalid payload format: Expected a CBOR ByteString"); } const claims = com.sphereon.cbor.Cbor.decode(payload.value); if (!(claims instanceof com.sphereon.cbor.CborMap)) { throw new Error("Invalid claims format: Expected a CBOR map"); } const claimsMap = claims.value.asJsMapView(); const statusListMap = claimsMap.get(new CborUInt(com.sphereon.kmp.LongKMP.fromNumber(65533))).value.asJsMapView(); const bits = Number(statusListMap.get(new CborString("bits")).value); const decoded = new Uint8Array(statusListMap.get(new CborString("lst")).value); const uint8Array = (0, import_pako.inflate)(decoded); const rawStatusList = decompressRawStatusList(uint8Array, bits); const statusList = new import_jwt_status_list2.StatusList(rawStatusList, bits); return { issuer: getCborValueFromMap(claimsMap, CWT_CLAIMS.ISSUER), id: getCborValueFromMap(claimsMap, CWT_CLAIMS.SUBJECT), statusList, iat: Number(getCborValueFromMap(claimsMap, CWT_CLAIMS.ISSUED_AT)), exp: getCborOptionalValueFromMap(claimsMap, CWT_CLAIMS.EXPIRATION), ttl: getCborOptionalValueFromMap(claimsMap, CWT_CLAIMS.TIME_TO_LIVE) }; }, "decodeStatusListCWT"); // src/impl/OAuthStatusList.ts var DEFAULT_BITS_PER_STATUS = 1; var DEFAULT_LIST_LENGTH2 = 25e4; var DEFAULT_PROOF_FORMAT2 = "jwt"; var OAuthStatusListImplementation = class { static { __name(this, "OAuthStatusListImplementation"); } async createNewStatusList(args, context) { if (!args.oauthStatusList) { throw new Error("OAuthStatusList options are required for type OAuthStatusList"); } const proofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT2; const { issuer, id, oauthStatusList, keyRef } = args; const { bitsPerStatus } = oauthStatusList; const expiresAt = ensureDate(oauthStatusList.expiresAt); const length = args.length ?? DEFAULT_LIST_LENGTH2; const issuerString = typeof issuer === "string" ? issuer : issuer.id; const correlationId = getAssertedValue("correlationId", args.correlationId); const statusList = new import_jwt_status_list3.StatusList(new Array(length).fill(0), bitsPerStatus ?? DEFAULT_BITS_PER_STATUS); const encodedList = statusList.compressStatusList(); const { statusListCredential } = await this.createSignedStatusList(proofFormat, context, statusList, issuerString, id, expiresAt, keyRef); return { encodedList, statusListCredential, oauthStatusList: { bitsPerStatus }, length, type: import_ssi_types4.StatusListType.OAuthStatusList, proofFormat, id, correlationId, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } async updateStatusListIndex(args, context) { const { statusListCredential, value, keyRef } = args; const expiresAt = ensureDate(args.expiresAt); if (typeof statusListCredential !== "string") { return Promise.reject("statusListCredential in neither JWT nor CWT"); } const proofFormat = determineProofFormat(statusListCredential); const decoded = proofFormat === "jwt" ? decodeStatusListJWT(statusListCredential) : decodeStatusListCWT(statusListCredential); const { statusList, issuer, id } = decoded; const index = typeof args.statusListIndex === "number" ? args.statusListIndex : parseInt(args.statusListIndex); if (index < 0 || index >= statusList.statusList.length) { throw new Error("Status list index out of bounds"); } if (typeof value !== "number") { throw new Error("Status list values should be of type number"); } statusList.setStatus(index, value); const { statusListCredential: signedCredential, encodedList } = await this.createSignedStatusList(proofFormat, context, statusList, issuer, id, expiresAt, keyRef); return { statusListCredential: signedCredential, encodedList, oauthStatusList: { bitsPerStatus: statusList.getBitsPerStatus() }, length: statusList.statusList.length, type: import_ssi_types4.StatusListType.OAuthStatusList, proofFormat, id, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } // FIXME: This still assumes only two values (boolean), whilst this list supports 8 bits max async updateStatusListFromEncodedList(args, context) { if (!args.oauthStatusList) { throw new Error("OAuthStatusList options are required for type OAuthStatusList"); } const { proofFormat, oauthStatusList, keyRef } = args; const { bitsPerStatus } = oauthStatusList; const expiresAt = ensureDate(oauthStatusList.expiresAt); const { issuer, id } = getAssertedValues(args); const issuerString = typeof issuer === "string" ? issuer : issuer.id; const listToUpdate = import_jwt_status_list3.StatusList.decompressStatusList(args.encodedList, bitsPerStatus ?? DEFAULT_BITS_PER_STATUS); const index = typeof args.statusListIndex === "number" ? args.statusListIndex : parseInt(args.statusListIndex); listToUpdate.setStatus(index, args.value); const { statusListCredential, encodedList } = await this.createSignedStatusList(proofFormat ?? DEFAULT_PROOF_FORMAT2, context, listToUpdate, issuerString, id, expiresAt, keyRef); return { encodedList, statusListCredential, oauthStatusList: { bitsPerStatus, expiresAt }, length: listToUpdate.statusList.length, type: import_ssi_types4.StatusListType.OAuthStatusList, proofFormat: proofFormat ?? DEFAULT_PROOF_FORMAT2, id, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } async checkStatusIndex(args) { const { statusListCredential, statusListIndex } = args; if (typeof statusListCredential !== "string") { return Promise.reject("statusListCredential in neither JWT nor CWT"); } const proofFormat = determineProofFormat(statusListCredential); const { statusList } = proofFormat === "jwt" ? decodeStatusListJWT(statusListCredential) : decodeStatusListCWT(statusListCredential); const index = typeof statusListIndex === "number" ? statusListIndex : parseInt(statusListIndex); if (index < 0 || index >= statusList.statusList.length) { throw new Error(`Status list index out of bounds, has ${statusList.statusList.length} items, requested ${index}`); } return statusList.getStatus(index); } /** * Performs the initial parsing of a StatusListCredential. * This method handles expensive operations like JWT/CWT decoding once. * It extracts all details available from the credential payload itself. */ async extractCredentialDetails(credential) { if (typeof credential !== "string") { return Promise.reject("statusListCredential must be a JWT or CWT string"); } const proofFormat = determineProofFormat(credential); const decoded = proofFormat === "jwt" ? decodeStatusListJWT(credential) : decodeStatusListCWT(credential); return { id: decoded.id, issuer: decoded.issuer, encodedList: decoded.statusList.compressStatusList(), decodedPayload: decoded }; } async toStatusListDetails(args) { if ("statusListCredential" in args) { const { statusListCredential, bitsPerStatus, correlationId, driverType } = args; if (!bitsPerStatus || bitsPerStatus < 1) { return Promise.reject(Error("bitsPerStatus must be set for OAuth status lists and must be 1 or higher")); } const proofFormat = determineProofFormat(statusListCredential); const decoded = proofFormat === "jwt" ? decodeStatusListJWT(statusListCredential) : decodeStatusListCWT(statusListCredential); const { statusList, issuer, id, exp } = decoded; const expiresAt = exp ? new Date(exp * 1e3) : void 0; return { id, encodedList: statusList.compressStatusList(), issuer, type: import_ssi_types4.StatusListType.OAuthStatusList, proofFormat, length: statusList.statusList.length, statusListCredential, statuslistContentType: this.buildContentType(proofFormat), correlationId, driverType, bitsPerStatus, ...expiresAt && { expiresAt }, oauthStatusList: { bitsPerStatus, ...expiresAt && { expiresAt } } }; } else { const { extractedDetails, statusListEntity } = args; const oauthEntity = statusListEntity; const decoded = extractedDetails.decodedPayload; const proofFormat = determineProofFormat(statusListEntity.statusListCredential); const expiresAt = decoded.exp ? new Date(decoded.exp * 1e3) : void 0; return { id: extractedDetails.id, encodedList: extractedDetails.encodedList, issuer: extractedDetails.issuer, type: import_ssi_types4.StatusListType.OAuthStatusList, proofFormat, length: decoded.statusList.statusList.length, statusListCredential: statusListEntity.statusListCredential, statuslistContentType: this.buildContentType(proofFormat), correlationId: statusListEntity.correlationId, driverType: statusListEntity.driverType, bitsPerStatus: oauthEntity.bitsPerStatus, ...expiresAt && { expiresAt }, oauthStatusList: { bitsPerStatus: oauthEntity.bitsPerStatus, ...expiresAt && { expiresAt } } }; } } async createCredentialStatus(args) { const { statusList, statusListIndex } = args; const oauthStatusList = statusList; return { id: `${statusList.id}#${statusListIndex}`, type: "OAuthStatusListEntry", bitsPerStatus: oauthStatusList.bitsPerStatus, statusListIndex: "" + statusListIndex, statusListCredential: statusList.id, expiresAt: oauthStatusList.expiresAt }; } buildContentType(proofFormat) { return `application/statuslist+${proofFormat === "cbor" ? "cwt" : "jwt"}`; } async createSignedStatusList(proofFormat, context, statusList, issuerString, id, expiresAt, keyRef) { switch (proofFormat) { case "jwt": { return await createSignedJwt(context, statusList, issuerString, id, expiresAt, keyRef); } case "cbor": { return await createSignedCbor(context, statusList, issuerString, id, expiresAt, keyRef); } default: throw new Error(`Invalid proof format '${proofFormat}' for OAuthStatusList`); } } }; // src/impl/StatusListFactory.ts var import_ssi_types6 = require("@sphereon/ssi-types"); // src/impl/BitstringStatusListImplementation.ts var import_ssi_types5 = require("@sphereon/ssi-types"); var import_vc_bitstring_status_lists = require("@4sure-tech/vc-bitstring-status-lists"); var DEFAULT_LIST_LENGTH3 = 131072; var DEFAULT_PROOF_FORMAT3 = "vc+jwt"; var DEFAULT_STATUS_PURPOSE = "revocation"; var BitstringStatusListImplementation = class { static { __name(this, "BitstringStatusListImplementation"); } /** * Creates a new bitstring status list with the specified configuration * * @param args - Configuration for the new status list including issuer, purpose, and size * @param context - Veramo agent context for credential operations * @returns Promise resolving to the created status list details */ async createNewStatusList(args, context) { if (!args.bitstringStatusList) { throw new Error("BitstringStatusList options are required for type BitstringStatusList"); } const length = args?.length ?? DEFAULT_LIST_LENGTH3; const proofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT3; assertValidProofType(import_ssi_types5.StatusListType.BitstringStatusList, proofFormat); const { issuer, id } = args; const correlationId = getAssertedValue("correlationId", args.correlationId); const { statusPurpose, bitsPerStatus, validFrom, validUntil, ttl } = args.bitstringStatusList; const unsignedCredential = await (0, import_vc_bitstring_status_lists.createStatusListCredential)({ id, issuer, statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE, validFrom: ensureDate(validFrom), validUntil: ensureDate(validUntil), ttl }); const statusListCredential = await this.createVerifiableCredential({ unsignedCredential, id, issuer, proofFormat, keyRef: args.keyRef }, context); return { encodedList: unsignedCredential.credentialSubject.encodedList, statusListCredential, bitstringStatusList: { statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE, ...unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }, ...unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }, ttl, bitsPerStatus }, length, type: import_ssi_types5.StatusListType.BitstringStatusList, proofFormat, id, correlationId, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } /** * Updates the status of a specific credential in an existing status list * * @param args - Update parameters including the status list credential, index, and new value * @param context - Veramo agent context for credential operations * @returns Promise resolving to the updated status list details */ async updateStatusListIndex(args, context) { if (!args.bitsPerStatus || args.bitsPerStatus < 1) { return Promise.reject(Error("bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListIndex)")); } const credential = args.statusListCredential; const uniform = import_ssi_types5.CredentialMapper.toUniformCredential(credential); const { issuer, credentialSubject } = uniform; const id = getAssertedValue("id", uniform.id); const origEncodedList = getAssertedProperty("encodedList", credentialSubject); const index = typeof args.statusListIndex === "number" ? args.statusListIndex : parseInt(args.statusListIndex); const statusList = await import_vc_bitstring_status_lists.BitstreamStatusList.decode({ encodedList: origEncodedList, statusSize: args.bitsPerStatus }); const bitstringStatusId = args.value; statusList.setStatus(index, bitstringStatusId); const proofFormat = import_ssi_types5.CredentialMapper.detectDocumentType(credential) === import_ssi_types5.DocumentFormat.JWT ? "vc+jwt" : "lds"; const credSubject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject; const statusPurpose = getAssertedProperty("statusPurpose", credSubject); const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : void 0; const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : void 0; const ttl = credSubject.ttl; const unsignedCredential = await (0, import_vc_bitstring_status_lists.createStatusListCredential)({ id, issuer, statusList, statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE, validFrom: ensureDate(validFrom), validUntil: ensureDate(validUntil), ttl }); const updatedCredential = await this.createVerifiableCredential({ unsignedCredential, id, issuer, proofFormat, keyRef: args.keyRef }, context); return { statusListCredential: updatedCredential, encodedList: unsignedCredential.credentialSubject.encodedList, bitstringStatusList: { statusPurpose, ...unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }, ...unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }, bitsPerStatus: args.bitsPerStatus, ttl }, length: statusList.getLength(), type: import_ssi_types5.StatusListType.BitstringStatusList, proofFormat, id, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } /** * Updates a status list by decoding an encoded list, modifying it, and re-encoding * * @param args - Update parameters including encoded list, index, and new value * @param context - Veramo agent context for credential operations * @returns Promise resolving to the updated status list details */ async updateStatusListFromEncodedList(args, context) { if (!args.bitstringStatusList) { throw new Error("bitstringStatusList options required for type BitstringStatusList"); } if (args.bitstringStatusList.bitsPerStatus < 1) { return Promise.reject(Error("bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (updateStatusListFromEncodedList)")); } const { statusPurpose, bitsPerStatus, ttl, validFrom, validUntil } = args.bitstringStatusList; const proofFormat = args?.proofFormat ?? DEFAULT_PROOF_FORMAT3; assertValidProofType(import_ssi_types5.StatusListType.BitstringStatusList, proofFormat); const { issuer, id } = getAssertedValues(args); const statusList = await import_vc_bitstring_status_lists.BitstreamStatusList.decode({ encodedList: args.encodedList, statusSize: bitsPerStatus }); const index = typeof args.statusListIndex === "number" ? args.statusListIndex : parseInt(args.statusListIndex); statusList.setStatus(index, args.value); const unsignedCredential = await (0, import_vc_bitstring_status_lists.createStatusListCredential)({ id, issuer, statusList, statusPurpose: statusPurpose ?? DEFAULT_STATUS_PURPOSE, validFrom: ensureDate(validFrom), validUntil: ensureDate(validUntil), ttl }); const credential = await this.createVerifiableCredential({ unsignedCredential, id, issuer, proofFormat, keyRef: args.keyRef }, context); return { type: import_ssi_types5.StatusListType.BitstringStatusList, statusListCredential: credential, encodedList: unsignedCredential.credentialSubject.encodedList, bitstringStatusList: { statusPurpose, bitsPerStatus, ...unsignedCredential.validFrom && { validFrom: new Date(unsignedCredential.validFrom) }, ...unsignedCredential.validUntil && { validUntil: new Date(unsignedCredential.validUntil) }, ttl }, length: statusList.getLength(), proofFormat: args.proofFormat ?? "lds", id, issuer, statuslistContentType: this.buildContentType(proofFormat) }; } /** * Checks the status of a specific credential by its index in the status list * * @param args - Check parameters including the status list credential and index * @returns Promise resolving to the status value at the specified index */ async checkStatusIndex(args) { if (!args.bitsPerStatus || args.bitsPerStatus < 1) { return Promise.reject(Error("bitsPerStatus must be set for bitstring status lists and must be 1 or higher. (checkStatusIndex)")); } const uniform = import_ssi_types5.CredentialMapper.toUniformCredential(args.statusListCredential); const { credentialSubject } = uniform; const encodedList = getAssertedProperty("encodedList", credentialSubject); const statusList = await import_vc_bitstring_status_lists.BitstreamStatusList.decode({ encodedList, statusSize: args.bitsPerStatus }); const numIndex = typeof args.statusListIndex === "number" ? args.statusListIndex : parseInt(args.statusListIndex); if (statusList.getLength() <= numIndex) { throw new Error(`Status list index out of bounds, has ${statusList.getLength()} entries, requested ${numIndex}`); } return statusList.getStatus(numIndex); } /** * Performs the initial parsing of a StatusListCredential. * This method handles expensive operations like JWT/CWT decoding once. * It extracts all details available from the credential payload itself. */ async extractCredentialDetails(credential) { const uniform = import_ssi_types5.CredentialMapper.toUniformCredential(credential); const { issuer, credentialSubject } = uniform; const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject; return { id: getAssertedValue("id", uniform.id), issuer, encodedList: getAssertedProperty("encodedList", subject) }; } async toStatusListDetails(args) { if ("statusListCredential" in args) { const { statusListCredential, bitsPerStatus, correlationId, driverType } = args; if (!bitsPerStatus || bitsPerStatus < 1) { return Promise.reject(Error("bitsPerStatus must be set for bitstring status lists and must be 1 or higher")); } const uniform = import_ssi_types5.CredentialMapper.toUniformCredential(statusListCredential); const { issuer, credentialSubject } = uniform; const subject = Array.isArray(credentialSubject) ? credentialSubject[0] : credentialSubject; const id = getAssertedValue("id", uniform.id); const encodedList = getAssertedProperty("encodedList", subject); const statusPurpose = getAssertedProperty("statusPurpose", subject); const validFrom = uniform.validFrom ? new Date(uniform.validFrom) : void 0; const validUntil = uniform.validUntil ? new Date(uniform.validUntil) : void 0; const ttl = subject.ttl; const proofFormat = import_ssi_types5.CredentialMapper.detectDocumentType(statusListCredential) === import_ssi_types5.DocumentFormat.JWT ? "vc+jwt" : "lds"; const statuslistLength = import_vc_bitstring_status_lists.BitstreamStatusList.getStatusListLength(encodedList, bitsPerStatus); return { id, encodedList, issuer, type: import_ssi_types5.StatusListType.BitstringStatusList, proofFormat, length: statuslistLength, statusListCredential, statuslistContentType: this.buildContentType(proofFormat), correlationId, driverType, statusPurpose, bitsPerStatus, ...validFrom && { validFrom }, ...validUntil && { validUntil }, ...ttl && { ttl }, bitstringStatusList: { statusPurpose, bitsPerStatus, ...validFrom && { validFrom }, ...validUntil && { validUntil }, ...ttl && { ttl } } }; } else { const { extractedDeta