@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
JavaScript
"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