@sphereon/ssi-types
Version:
SSI Common Types
142 lines • 7.37 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sdJwtDecodedCredentialToUniformCredential = void 0;
exports.isWrappedSdJwtVerifiableCredential = isWrappedSdJwtVerifiableCredential;
exports.isWrappedSdJwtVerifiablePresentation = isWrappedSdJwtVerifiablePresentation;
exports.decodeSdJwtVc = decodeSdJwtVc;
exports.decodeSdJwtVcAsync = decodeSdJwtVcAsync;
const decode_1 = require("@sd-jwt/decode");
const did_1 = require("./did");
function isWrappedSdJwtVerifiableCredential(vc) {
return vc.format === 'vc+sd-jwt';
}
function isWrappedSdJwtVerifiablePresentation(vp) {
return vp.format === 'vc+sd-jwt';
}
/**
* Decode an SD-JWT vc from its compact format (string) to an object containing the disclosures,
* signed payload, decoded payload and the compact SD-JWT vc.
*
* Both the input and output interfaces of this method are defined in `@sphereon/ssi-types`, so
* this method hides the actual implementation of SD-JWT (which is currently based on @sd-jwt/core)
*/
function decodeSdJwtVc(compactSdJwtVc, hasher) {
const { jwt, disclosures, kbJwt } = (0, decode_1.decodeSdJwtSync)(compactSdJwtVc, hasher);
const signedPayload = jwt.payload;
const decodedPayload = (0, decode_1.getClaimsSync)(signedPayload, disclosures, hasher);
const compactKeyBindingJwt = kbJwt ? compactSdJwtVc.split('~').pop() : undefined;
return Object.assign({ compactSdJwtVc, decodedPayload: decodedPayload, disclosures: disclosures.map((d) => {
const decoded = d.key ? [d.salt, d.key, d.value] : [d.salt, d.value];
if (!d._digest)
throw new Error('Implementation error: digest not present in disclosure');
return {
decoded: decoded,
digest: d._digest,
encoded: d.encode(),
};
}), signedPayload: signedPayload }, (compactKeyBindingJwt &&
kbJwt && {
kbJwt: {
header: kbJwt.header,
compact: compactKeyBindingJwt,
payload: kbJwt.payload,
},
}));
}
/**
* Decode an SD-JWT vc from its compact format (string) to an object containing the disclosures,
* signed payload, decoded payload and the compact SD-JWT vc.
*
* Both the input and output interfaces of this method are defined in `@sphereon/ssi-types`, so
* this method hides the actual implementation of SD-JWT (which is currently based on @sd-jwt/core)
*/
function decodeSdJwtVcAsync(compactSdJwtVc, hasher) {
return __awaiter(this, void 0, void 0, function* () {
const { jwt, disclosures, kbJwt } = yield (0, decode_1.decodeSdJwt)(compactSdJwtVc, hasher);
const signedPayload = jwt.payload;
const decodedPayload = yield (0, decode_1.getClaims)(signedPayload, disclosures, hasher);
const compactKeyBindingJwt = kbJwt ? compactSdJwtVc.split('~').pop() : undefined;
return Object.assign({ compactSdJwtVc, decodedPayload: decodedPayload, disclosures: disclosures.map((d) => {
const decoded = d.key ? [d.salt, d.key, d.value] : [d.salt, d.value];
if (!d._digest)
throw new Error('Implementation error: digest not present in disclosure');
return {
decoded: decoded,
digest: d._digest,
encoded: d.encode(),
};
}), signedPayload: signedPayload }, (compactKeyBindingJwt &&
kbJwt && {
kbJwt: {
header: kbJwt.header,
payload: kbJwt.payload,
compact: compactKeyBindingJwt,
},
}));
});
}
const sdJwtDecodedCredentialToUniformCredential = (decoded, opts) => {
var _a, _b, _c;
const { decodedPayload } = decoded;
const { exp, nbf, iss, iat, vct, cnf, status, sub, jti } = decodedPayload;
const maxSkewInMS = (_a = opts === null || opts === void 0 ? void 0 : opts.maxTimeSkewInMS) !== null && _a !== void 0 ? _a : 1500;
const expirationDate = jwtDateToISOString({ jwtClaim: exp, claimName: 'exp' });
let issuanceDateStr = jwtDateToISOString({ jwtClaim: iat, claimName: 'iat' });
let nbfDateAsStr;
if (nbf) {
nbfDateAsStr = jwtDateToISOString({ jwtClaim: nbf, claimName: 'nbf' });
if (issuanceDateStr && nbfDateAsStr && issuanceDateStr !== nbfDateAsStr) {
const diff = Math.abs(new Date(nbfDateAsStr).getTime() - new Date(iss).getTime());
if (!maxSkewInMS || diff > maxSkewInMS) {
throw Error(`Inconsistent issuance dates between JWT claim (${nbfDateAsStr}) and VC value (${iss})`);
}
}
issuanceDateStr = nbfDateAsStr;
}
const issuanceDate = issuanceDateStr;
if (!issuanceDate) {
throw Error(`JWT issuance date is required but was not present`);
}
// Filter out the fields we don't want in credentialSubject
const excludedFields = new Set(['vct', 'cnf', 'iss', 'iat', 'exp', 'nbf', 'jti', 'sub']);
const credentialSubject = Object.entries(decodedPayload).reduce((acc, [key, value]) => {
if (!excludedFields.has(key) &&
value !== undefined &&
value !== '' &&
!(typeof value === 'object' && value !== null && Object.keys(value).length === 0)) {
acc[key] = value;
}
return acc;
}, {});
const credential = Object.assign(Object.assign(Object.assign({ type: [vct], '@context': [], credentialSubject: Object.assign(Object.assign({}, credentialSubject), { id: (_c = (_b = credentialSubject.id) !== null && _b !== void 0 ? _b : sub) !== null && _c !== void 0 ? _c : jti }), issuanceDate,
expirationDate, issuer: iss }, (cnf && { cnf })), (status && { status })), { proof: {
type: did_1.IProofType.SdJwtProof2024,
created: nbfDateAsStr !== null && nbfDateAsStr !== void 0 ? nbfDateAsStr : issuanceDate,
proofPurpose: did_1.IProofPurpose.authentication,
verificationMethod: iss,
jwt: decoded.compactSdJwtVc,
} });
return credential;
};
exports.sdJwtDecodedCredentialToUniformCredential = sdJwtDecodedCredentialToUniformCredential;
const jwtDateToISOString = ({ jwtClaim, claimName, isRequired = false, }) => {
if (jwtClaim) {
const claim = parseInt(jwtClaim.toString());
// change JWT seconds to millisecond for the date
return new Date(claim * (claim < 9999999999 ? 1000 : 1)).toISOString().replace(/\.000Z/, 'Z');
}
else if (isRequired) {
throw Error(`JWT claim ${claimName} is required but was not present`);
}
return undefined;
};
//# sourceMappingURL=sd-jwt-vc.js.map