UNPKG

@sd-jwt/core

Version:

sd-jwt draft 7 implementation in typescript

1,176 lines (1,166 loc) 39.6 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/index.ts import { getSDAlgAndPayload as getSDAlgAndPayload2 } from "@sd-jwt/decode"; import { IANA_HASH_ALGORITHMS, KB_JWT_TYP as KB_JWT_TYP2 } from "@sd-jwt/types"; import { base64urlDecode, base64urlEncode as base64urlEncode3, SDJWTException as SDJWTException6, uint8ArrayToBase64Url as uint8ArrayToBase64Url2 } from "@sd-jwt/utils"; // src/flattenJSON.ts import { splitSdJwt } from "@sd-jwt/decode"; import { SD_SEPARATOR } from "@sd-jwt/types"; import { SDJWTException } from "@sd-jwt/utils"; var FlattenJSON = class _FlattenJSON { constructor(data) { this.disclosures = data.disclosures; this.kb_jwt = data.kb_jwt; this.payload = data.jwtData.payload; this.signature = data.jwtData.signature; this.protected = data.jwtData.protected; } static fromEncode(encodedSdJwt) { const { jwt, disclosures, kbJwt } = splitSdJwt(encodedSdJwt); const { 0: protectedHeader, 1: payload, 2: signature } = jwt.split("."); if (!protectedHeader || !payload || !signature) { throw new SDJWTException("Invalid JWT"); } return new _FlattenJSON({ jwtData: { protected: protectedHeader, payload, signature }, disclosures, kb_jwt: kbJwt }); } static fromSerialized(json) { return new _FlattenJSON({ jwtData: { protected: json.protected, payload: json.payload, signature: json.signature }, disclosures: json.header.disclosures, kb_jwt: json.header.kb_jwt }); } toJson() { return { payload: this.payload, signature: this.signature, protected: this.protected, header: { disclosures: this.disclosures, kb_jwt: this.kb_jwt } }; } toEncoded() { var _a; const data = []; const jwt = `${this.protected}.${this.payload}.${this.signature}`; data.push(jwt); if (this.disclosures && this.disclosures.length > 0) { const disclosures = this.disclosures.join(SD_SEPARATOR); data.push(disclosures); } const kb_jwt = (_a = this.kb_jwt) != null ? _a : ""; data.push(kb_jwt); return data.join(SD_SEPARATOR); } }; // src/generalJSON.ts import { splitSdJwt as splitSdJwt2 } from "@sd-jwt/decode"; import { SD_SEPARATOR as SD_SEPARATOR2 } from "@sd-jwt/types"; import { base64urlEncode, SDJWTException as SDJWTException2 } from "@sd-jwt/utils"; var GeneralJSON = class _GeneralJSON { constructor(data) { this.payload = data.payload; this.disclosures = data.disclosures; this.kb_jwt = data.kb_jwt; this.signatures = data.signatures; } static fromEncode(encodedSdJwt) { const { jwt, disclosures, kbJwt } = splitSdJwt2(encodedSdJwt); const { 0: protectedHeader, 1: payload, 2: signature } = jwt.split("."); if (!protectedHeader || !payload || !signature) { throw new SDJWTException2("Invalid JWT"); } return new _GeneralJSON({ payload, disclosures, kb_jwt: kbJwt, signatures: [ { protected: protectedHeader, signature } ] }); } static fromSerialized(json) { var _a, _b, _c; if (!json.signatures[0]) { throw new SDJWTException2("Invalid JSON"); } const disclosures = (_b = (_a = json.signatures[0].header) == null ? void 0 : _a.disclosures) != null ? _b : []; const kb_jwt = (_c = json.signatures[0].header) == null ? void 0 : _c.kb_jwt; return new _GeneralJSON({ payload: json.payload, disclosures, kb_jwt, signatures: json.signatures.map((s) => { var _a2; return { protected: s.protected, signature: s.signature, kid: (_a2 = s.header) == null ? void 0 : _a2.kid }; }) }); } toJson() { return { payload: this.payload, signatures: this.signatures.map((s, i) => { if (i !== 0) { return { header: { kid: s.kid }, protected: s.protected, signature: s.signature }; } return { header: { disclosures: this.disclosures, kid: s.kid, kb_jwt: this.kb_jwt }, protected: s.protected, signature: s.signature }; }) }; } toEncoded(index) { var _a; if (index < 0 || index >= this.signatures.length) { throw new SDJWTException2("Index out of bounds"); } const data = []; const { protected: protectedHeader, signature } = this.signatures[index]; const jwt = `${protectedHeader}.${this.payload}.${signature}`; data.push(jwt); if (this.disclosures && this.disclosures.length > 0) { const disclosures = this.disclosures.join(SD_SEPARATOR2); data.push(disclosures); } const kb = (_a = this.kb_jwt) != null ? _a : ""; data.push(kb); return data.join(SD_SEPARATOR2); } addSignature(protectedHeader, signer, kid) { return __async(this, null, function* () { const header = base64urlEncode(JSON.stringify(protectedHeader)); const signature = yield signer(`${header}.${this.payload}`); this.signatures.push({ protected: header, signature, kid }); }); } }; // src/jwt.ts import { decodeJwt } from "@sd-jwt/decode"; import { base64urlEncode as base64urlEncode2, SDJWTException as SDJWTException3 } from "@sd-jwt/utils"; var Jwt = class _Jwt { constructor(data) { this.header = data == null ? void 0 : data.header; this.payload = data == null ? void 0 : data.payload; this.signature = data == null ? void 0 : data.signature; this.encoded = data == null ? void 0 : data.encoded; } static decodeJWT(jwt) { return decodeJwt(jwt); } static fromEncode(encodedJwt) { const { header, payload, signature } = _Jwt.decodeJWT( encodedJwt ); const jwt = new _Jwt({ header, payload, signature, encoded: encodedJwt }); return jwt; } setHeader(header) { this.header = header; this.encoded = void 0; return this; } setPayload(payload) { this.payload = payload; this.encoded = void 0; return this; } getUnsignedToken() { if (!this.header || !this.payload) { throw new SDJWTException3("Serialize Error: Invalid JWT"); } if (this.encoded) { const parts = this.encoded.split("."); if (parts.length !== 3) { throw new SDJWTException3(`Invalid JWT format: ${this.encoded}`); } const unsignedToken = parts.slice(0, 2).join("."); return unsignedToken; } const header = base64urlEncode2(JSON.stringify(this.header)); const payload = base64urlEncode2(JSON.stringify(this.payload)); return `${header}.${payload}`; } sign(signer) { return __async(this, null, function* () { const data = this.getUnsignedToken(); this.signature = yield signer(data); return this.encodeJwt(); }); } encodeJwt() { if (this.encoded) { return this.encoded; } if (!this.header || !this.payload || !this.signature) { throw new SDJWTException3("Serialize Error: Invalid JWT"); } const header = base64urlEncode2(JSON.stringify(this.header)); const payload = base64urlEncode2(JSON.stringify(this.payload)); const signature = this.signature; const compact = `${header}.${payload}.${signature}`; this.encoded = compact; return compact; } /** * Verify the JWT using the provided verifier function. * It checks the signature and validates the iat, nbf, and exp claims if they are present. * @param verifier * @param options - Options for verification, such as current date and skew seconds * @returns */ verify(verifier, options) { return __async(this, null, function* () { var _a, _b, _c, _d; const skew = (options == null ? void 0 : options.skewSeconds) ? options.skewSeconds : 0; const currentDate = (_a = options == null ? void 0 : options.currentDate) != null ? _a : Math.floor(Date.now() / 1e3); if (((_b = this.payload) == null ? void 0 : _b.iat) && this.payload.iat - skew > currentDate) { throw new SDJWTException3("Verify Error: JWT is not yet valid"); } if (((_c = this.payload) == null ? void 0 : _c.nbf) && this.payload.nbf - skew > currentDate) { throw new SDJWTException3("Verify Error: JWT is not yet valid"); } if (((_d = this.payload) == null ? void 0 : _d.exp) && this.payload.exp + skew < currentDate) { throw new SDJWTException3("Verify Error: JWT is expired"); } if (!this.signature) { throw new SDJWTException3("Verify Error: no signature in JWT"); } const data = this.getUnsignedToken(); const verified = yield verifier(data, this.signature); if (!verified) { throw new SDJWTException3("Verify Error: Invalid JWT Signature"); } return { payload: this.payload, header: this.header }; }); } }; // src/kbjwt.ts import { KB_JWT_TYP } from "@sd-jwt/types"; import { SDJWTException as SDJWTException4 } from "@sd-jwt/utils"; var KBJwt = class _KBJwt extends Jwt { // Checking the validity of the key binding jwt // the type unknown is not good, but we don't know at this point how to get the public key of the signer, this is defined in the kbVerifier verifyKB(values) { return __async(this, null, function* () { var _a; if (!this.header || !this.payload || !this.signature) { throw new SDJWTException4("Verify Error: Invalid JWT"); } if (!this.header.alg || this.header.alg === "none" || !this.header.typ || this.header.typ !== KB_JWT_TYP || !this.payload.iat || !this.payload.aud || !this.payload.nonce || // this is for backward compatibility with version 06 !(this.payload.sd_hash || ((_a = this.payload) == null ? void 0 : _a._sd_hash))) { throw new SDJWTException4("Invalid Key Binding Jwt"); } const data = this.getUnsignedToken(); const verified = yield values.verifier( data, this.signature, values.payload ); if (!verified) { throw new SDJWTException4("Verify Error: Invalid JWT Signature"); } if (this.payload.nonce !== values.nonce) { throw new SDJWTException4("Verify Error: Invalid Nonce"); } return { payload: this.payload, header: this.header }; }); } // This function is for creating KBJwt object for verify properly static fromKBEncode(encodedJwt) { const { header, payload, signature } = Jwt.decodeJWT( encodedJwt ); const jwt = new _KBJwt({ header, payload, signature, encoded: encodedJwt }); return jwt; } }; // src/sdjwt.ts import { createHashMapping, getSDAlgAndPayload, unpack } from "@sd-jwt/decode"; import { transformPresentationFrame } from "@sd-jwt/present"; import { SD_DECOY, SD_DIGEST, SD_LIST_KEY, SD_SEPARATOR as SD_SEPARATOR3 } from "@sd-jwt/types"; import { Disclosure, SDJWTException as SDJWTException5 } from "@sd-jwt/utils"; // src/decoy.ts import { uint8ArrayToBase64Url } from "@sd-jwt/utils"; var createDecoy = (hash, saltGenerator) => __async(null, null, function* () { const { hasher, alg } = hash; const salt = yield saltGenerator(16); const decoy = yield hasher(salt, alg); return uint8ArrayToBase64Url(decoy); }); // src/sdjwt.ts var SDJwt = class _SDJwt { constructor(data) { this.jwt = data == null ? void 0 : data.jwt; this.disclosures = data == null ? void 0 : data.disclosures; this.kbJwt = data == null ? void 0 : data.kbJwt; } static decodeSDJwt(sdjwt, hasher) { return __async(this, null, function* () { const [encodedJwt, ...encodedDisclosures] = sdjwt.split(SD_SEPARATOR3); const jwt = Jwt.fromEncode(encodedJwt); if (!jwt.payload) { throw new Error("Payload is undefined on the JWT. Invalid state reached"); } if (encodedDisclosures.length === 0) { return { jwt, disclosures: [] }; } const encodedKeyBindingJwt = encodedDisclosures.pop(); const kbJwt = encodedKeyBindingJwt ? KBJwt.fromKBEncode(encodedKeyBindingJwt) : void 0; const { _sd_alg } = getSDAlgAndPayload(jwt.payload); const disclosures = yield Promise.all( encodedDisclosures.map( (ed) => Disclosure.fromEncode(ed, { alg: _sd_alg, hasher }) ) ); return { jwt, disclosures, kbJwt }; }); } static extractJwt(encodedSdJwt) { return __async(this, null, function* () { const [encodedJwt, ..._encodedDisclosures] = encodedSdJwt.split(SD_SEPARATOR3); return Jwt.fromEncode(encodedJwt); }); } static fromEncode(encodedSdJwt, hasher) { return __async(this, null, function* () { const { jwt, disclosures, kbJwt } = yield _SDJwt.decodeSDJwt(encodedSdJwt, hasher); return new _SDJwt({ jwt, disclosures, kbJwt }); }); } present(presentFrame, hasher) { return __async(this, null, function* () { const disclosures = yield this.getPresentDisclosures(presentFrame, hasher); const presentSDJwt = new _SDJwt({ jwt: this.jwt, disclosures, kbJwt: this.kbJwt }); return presentSDJwt.encodeSDJwt(); }); } getPresentDisclosures(presentFrame, hasher) { return __async(this, null, function* () { var _a; if (!((_a = this.jwt) == null ? void 0 : _a.payload) || !this.disclosures) { throw new SDJWTException5("Invalid sd-jwt: jwt or disclosures is missing"); } const { _sd_alg: alg } = getSDAlgAndPayload(this.jwt.payload); const hash = { alg, hasher }; const hashmap = yield createHashMapping(this.disclosures, hash); const { disclosureKeymap } = yield unpack( this.jwt.payload, this.disclosures, hasher ); const keys = presentFrame ? transformPresentationFrame(presentFrame) : yield this.presentableKeys(hasher); const disclosures = keys.map((k) => hashmap[disclosureKeymap[k]]).filter((d) => d !== void 0); return disclosures; }); } encodeSDJwt() { const data = []; if (!this.jwt) { throw new SDJWTException5("Invalid sd-jwt: jwt is missing"); } const encodedJwt = this.jwt.encodeJwt(); data.push(encodedJwt); if (this.disclosures && this.disclosures.length > 0) { const encodeddisclosures = this.disclosures.map((dc) => dc.encode()).join(SD_SEPARATOR3); data.push(encodeddisclosures); } data.push(this.kbJwt ? this.kbJwt.encodeJwt() : ""); return data.join(SD_SEPARATOR3); } keys(hasher) { return __async(this, null, function* () { return listKeys(yield this.getClaims(hasher)).sort(); }); } presentableKeys(hasher) { return __async(this, null, function* () { var _a, _b; if (!((_a = this.jwt) == null ? void 0 : _a.payload) || !this.disclosures) { throw new SDJWTException5("Invalid sd-jwt: jwt or disclosures is missing"); } const { disclosureKeymap } = yield unpack( (_b = this.jwt) == null ? void 0 : _b.payload, this.disclosures, hasher ); return Object.keys(disclosureKeymap).sort(); }); } getClaims(hasher) { return __async(this, null, function* () { var _a; if (!((_a = this.jwt) == null ? void 0 : _a.payload) || !this.disclosures) { throw new SDJWTException5("Invalid sd-jwt: jwt or disclosures is missing"); } const { unpackedObj } = yield unpack( this.jwt.payload, this.disclosures, hasher ); return unpackedObj; }); } }; var listKeys = (obj, prefix = "") => { const keys = []; for (const key in obj) { if (obj[key] === void 0) continue; const newKey = prefix ? `${prefix}.${key}` : key; keys.push(newKey); if (obj[key] && typeof obj[key] === "object" && obj[key] !== null) { keys.push(...listKeys(obj[key], newKey)); } } return keys; }; var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(null, null, function* () { var _a, _b; if (!disclosureFrame) { return { packedClaims: claims, disclosures: [] }; } const sd = (_a = disclosureFrame[SD_DIGEST]) != null ? _a : []; const decoyCount = (_b = disclosureFrame[SD_DECOY]) != null ? _b : 0; if (Array.isArray(claims)) { const packedClaims2 = []; const disclosures2 = []; const recursivePackedClaims2 = {}; for (const key in disclosureFrame) { if (key !== SD_DIGEST) { const idx = Number.parseInt(key, 10); const packed = yield pack( claims[idx], disclosureFrame[idx], hash, saltGenerator ); recursivePackedClaims2[idx] = packed.packedClaims; disclosures2.push(...packed.disclosures); } } for (let i = 0; i < claims.length; i++) { const claim = recursivePackedClaims2[i] ? recursivePackedClaims2[i] : claims[i]; if (sd.includes(i)) { const salt = yield saltGenerator(16); const disclosure = new Disclosure([salt, claim]); const digest = yield disclosure.digest(hash); packedClaims2.push({ [SD_LIST_KEY]: digest }); disclosures2.push(disclosure); } else { packedClaims2.push(claim); } } for (let j = 0; j < decoyCount; j++) { const decoyDigest = yield createDecoy(hash, saltGenerator); packedClaims2.push({ [SD_LIST_KEY]: decoyDigest }); } return { packedClaims: packedClaims2, disclosures: disclosures2 }; } const packedClaims = {}; const disclosures = []; const recursivePackedClaims = {}; for (const key in disclosureFrame) { if (key !== SD_DIGEST) { const packed = yield pack( // @ts-expect-error claims[key], disclosureFrame[key], hash, saltGenerator ); recursivePackedClaims[key] = packed.packedClaims; disclosures.push(...packed.disclosures); } } const _sd = []; for (const key in claims) { const claim = recursivePackedClaims[key] ? recursivePackedClaims[key] : claims[key]; if (sd.includes(key)) { const salt = yield saltGenerator(16); const disclosure = new Disclosure([salt, key, claim]); const digest = yield disclosure.digest(hash); _sd.push(digest); disclosures.push(disclosure); } else { packedClaims[key] = claim; } } for (let j = 0; j < decoyCount; j++) { const decoyDigest = yield createDecoy(hash, saltGenerator); _sd.push(decoyDigest); } if (_sd.length > 0) { packedClaims[SD_DIGEST] = _sd.sort(); } return { packedClaims, disclosures }; }); // src/index.ts var _SDJwtInstance = class _SDJwtInstance { constructor(userConfig) { this.userConfig = {}; if (userConfig) { if (userConfig.hashAlg && !IANA_HASH_ALGORITHMS.includes(userConfig.hashAlg)) { throw new SDJWTException6( `Invalid hash algorithm: ${userConfig.hashAlg}` ); } this.userConfig = userConfig; } } createKBJwt(options, sdHash) { return __async(this, null, function* () { if (!this.userConfig.kbSigner) { throw new SDJWTException6("Key Binding Signer not found"); } if (!this.userConfig.kbSignAlg) { throw new SDJWTException6("Key Binding sign algorithm not specified"); } const { payload } = options; const kbJwt = new KBJwt({ header: { typ: KB_JWT_TYP2, alg: this.userConfig.kbSignAlg }, payload: __spreadProps(__spreadValues({}, payload), { sd_hash: sdHash }) }); yield kbJwt.sign(this.userConfig.kbSigner); return kbJwt; }); } SignJwt(jwt) { return __async(this, null, function* () { if (!this.userConfig.signer) { throw new SDJWTException6("Signer not found"); } yield jwt.sign(this.userConfig.signer); return jwt; }); } VerifyJwt(jwt, options) { return __async(this, null, function* () { if (!this.userConfig.verifier) { throw new SDJWTException6("Verifier not found"); } return jwt.verify(this.userConfig.verifier, options); }); } issue(payload, disclosureFrame, options) { return __async(this, null, function* () { var _a, _b; if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } if (!this.userConfig.saltGenerator) { throw new SDJWTException6("SaltGenerator not found"); } if (!this.userConfig.signAlg) { throw new SDJWTException6("sign alogrithm not specified"); } if (disclosureFrame) { this.validateReservedFields(disclosureFrame); } const hasher = this.userConfig.hasher; const hashAlg = (_a = this.userConfig.hashAlg) != null ? _a : _SDJwtInstance.DEFAULT_hashAlg; const { packedClaims, disclosures } = yield pack( payload, disclosureFrame, { hasher, alg: hashAlg }, this.userConfig.saltGenerator ); const alg = this.userConfig.signAlg; const OptionHeader = (_b = options == null ? void 0 : options.header) != null ? _b : {}; const CustomHeader = this.userConfig.omitTyp ? OptionHeader : __spreadValues({ typ: this.type }, OptionHeader); const header = __spreadProps(__spreadValues({}, CustomHeader), { alg }); const jwt = new Jwt({ header, payload: __spreadProps(__spreadValues({}, packedClaims), { _sd_alg: disclosureFrame ? hashAlg : void 0 }) }); yield this.SignJwt(jwt); const sdJwt = new SDJwt({ jwt, disclosures }); return sdJwt.encodeSDJwt(); }); } /** * Validates if the disclosureFrame contains any reserved fields. If so it will throw an error. * @param disclosureFrame * @returns */ validateReservedFields(_disclosureFrame) { return; } present(encodedSDJwt, presentationFrame, options) { return __async(this, null, function* () { var _a; if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const hasher = this.userConfig.hasher; const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher); if (!((_a = sdjwt.jwt) == null ? void 0 : _a.payload)) throw new SDJWTException6("Payload not found"); const presentSdJwtWithoutKb = yield sdjwt.present( presentationFrame, hasher ); if (!(options == null ? void 0 : options.kb)) { return presentSdJwtWithoutKb; } const sdHashStr = yield this.calculateSDHash( presentSdJwtWithoutKb, sdjwt, hasher ); sdjwt.kbJwt = yield this.createKBJwt(options.kb, sdHashStr); return sdjwt.present(presentationFrame, hasher); }); } // This function is for verifying the SD JWT // If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT // If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it verify(encodedSDJwt, options) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const hasher = this.userConfig.hasher; const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher); if (!sdjwt.jwt || !sdjwt.jwt.payload) { throw new SDJWTException6("Invalid SD JWT"); } const { payload, header } = yield this.validate(encodedSDJwt, options); if (options == null ? void 0 : options.requiredClaimKeys) { const keys = yield sdjwt.keys(hasher); const missingKeys = options.requiredClaimKeys.filter( (k) => !keys.includes(k) ); if (missingKeys.length > 0) { throw new SDJWTException6( `Missing required claim keys: ${missingKeys.join(", ")}` ); } } if (!(options == null ? void 0 : options.keyBindingNonce)) { return { payload, header }; } if (!sdjwt.kbJwt) { throw new SDJWTException6("Key Binding JWT not exist"); } if (!this.userConfig.kbVerifier) { throw new SDJWTException6("Key Binding Verifier not found"); } const kb = yield sdjwt.kbJwt.verifyKB({ verifier: this.userConfig.kbVerifier, payload, nonce: options.keyBindingNonce }); if (!kb) { throw new Error("signature is not valid"); } const sdHashfromKb = kb.payload.sd_hash; const sdjwtWithoutKb = new SDJwt({ jwt: sdjwt.jwt, disclosures: sdjwt.disclosures }); const presentSdJwtWithoutKb = sdjwtWithoutKb.encodeSDJwt(); const sdHashStr = yield this.calculateSDHash( presentSdJwtWithoutKb, sdjwt, hasher ); if (sdHashStr !== sdHashfromKb) { throw new SDJWTException6("Invalid sd_hash in Key Binding JWT"); } return { payload, header, kb }; }); } calculateSDHash(presentSdJwtWithoutKb, sdjwt, hasher) { return __async(this, null, function* () { if (!sdjwt.jwt || !sdjwt.jwt.payload) { throw new SDJWTException6("Invalid SD JWT"); } const { _sd_alg } = getSDAlgAndPayload2(sdjwt.jwt.payload); const sdHash = yield hasher(presentSdJwtWithoutKb, _sd_alg); const sdHashStr = uint8ArrayToBase64Url2(sdHash); return sdHashStr; }); } /** * This function is for validating the SD JWT * Checking signature, if provided the iat and exp when provided and return its the claims * @param encodedSDJwt * @param options * @returns */ validate(encodedSDJwt, options) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const hasher = this.userConfig.hasher; const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher); if (!sdjwt.jwt) { throw new SDJWTException6("Invalid SD JWT"); } const verifiedPayloads = yield this.VerifyJwt(sdjwt.jwt, options); const claims = yield sdjwt.getClaims(hasher); return { payload: claims, header: verifiedPayloads.header }; }); } config(newConfig) { this.userConfig = __spreadValues(__spreadValues({}, this.userConfig), newConfig); } encode(sdJwt) { return sdJwt.encodeSDJwt(); } decode(endcodedSDJwt) { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } return SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher); } keys(endcodedSDJwt) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const sdjwt = yield SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher); return sdjwt.keys(this.userConfig.hasher); }); } presentableKeys(endcodedSDJwt) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const sdjwt = yield SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher); return sdjwt.presentableKeys(this.userConfig.hasher); }); } getClaims(endcodedSDJwt) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const sdjwt = yield SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher); return sdjwt.getClaims(this.userConfig.hasher); }); } toFlattenJSON(endcodedSDJwt) { return FlattenJSON.fromEncode(endcodedSDJwt); } toGeneralJSON(endcodedSDJwt) { return GeneralJSON.fromEncode(endcodedSDJwt); } }; _SDJwtInstance.DEFAULT_hashAlg = "sha-256"; var SDJwtInstance = _SDJwtInstance; var SDJwtGeneralJSONInstance = class { constructor(userConfig) { this.userConfig = {}; if (userConfig) { if (userConfig.hashAlg && !IANA_HASH_ALGORITHMS.includes(userConfig.hashAlg)) { throw new SDJWTException6( `Invalid hash algorithm: ${userConfig.hashAlg}` ); } this.userConfig = userConfig; } } createKBJwt(options, sdHash) { return __async(this, null, function* () { if (!this.userConfig.kbSigner) { throw new SDJWTException6("Key Binding Signer not found"); } if (!this.userConfig.kbSignAlg) { throw new SDJWTException6("Key Binding sign algorithm not specified"); } const { payload } = options; const kbJwt = new KBJwt({ header: { typ: KB_JWT_TYP2, alg: this.userConfig.kbSignAlg }, payload: __spreadProps(__spreadValues({}, payload), { sd_hash: sdHash }) }); yield kbJwt.sign(this.userConfig.kbSigner); return kbJwt; }); } encodeObj(obj) { return base64urlEncode3(JSON.stringify(obj)); } issue(payload, disclosureFrame, options) { return __async(this, null, function* () { var _a; if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } if (!this.userConfig.saltGenerator) { throw new SDJWTException6("SaltGenerator not found"); } if (disclosureFrame) { this.validateReservedFields(disclosureFrame); } const hasher = this.userConfig.hasher; const hashAlg = (_a = this.userConfig.hashAlg) != null ? _a : SDJwtInstance.DEFAULT_hashAlg; const { packedClaims, disclosures } = yield pack( payload, disclosureFrame, { hasher, alg: hashAlg }, this.userConfig.saltGenerator ); const encodedDisclosures = disclosures.map((d) => d.encode()); const encodedSDJwtPayload = this.encodeObj(__spreadProps(__spreadValues({}, packedClaims), { _sd_alg: disclosureFrame ? hashAlg : void 0 })); const signatures = yield Promise.all( options.sigs.map((s) => __async(this, null, function* () { const { signer, alg, kid, header } = s; const protectedHeader = __spreadValues({ typ: this.type, alg, kid }, header); const encodedProtectedHeader = this.encodeObj(protectedHeader); const signature = yield signer( `${encodedProtectedHeader}.${encodedSDJwtPayload}` ); return { protected: encodedProtectedHeader, kid, signature }; })) ); const generalJson = new GeneralJSON({ payload: encodedSDJwtPayload, disclosures: encodedDisclosures, signatures }); return generalJson; }); } /** * Validates if the disclosureFrame contains any reserved fields. If so it will throw an error. * @param disclosureFrame * @returns */ validateReservedFields(_disclosureFrame) { return; } present(generalJSON, presentationFrame, options) { return __async(this, null, function* () { var _a; if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const hasher = this.userConfig.hasher; const encodedSDJwt = generalJSON.toEncoded(0); const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher); if (!((_a = sdjwt.jwt) == null ? void 0 : _a.payload)) throw new SDJWTException6("Payload not found"); const disclosures = yield sdjwt.getPresentDisclosures( presentationFrame, hasher ); const encodedDisclosures = disclosures.map((d) => d.encode()); const presentedGeneralJSON = new GeneralJSON({ payload: generalJSON.payload, disclosures: encodedDisclosures, signatures: generalJSON.signatures }); if (!(options == null ? void 0 : options.kb)) { return presentedGeneralJSON; } const presentSdJwtWithoutKb = yield sdjwt.present( presentationFrame, hasher ); const sdHashStr = yield this.calculateSDHash( presentSdJwtWithoutKb, sdjwt, hasher ); const kbJwt = yield this.createKBJwt(options.kb, sdHashStr); const encodedKbJwt = kbJwt.encodeJwt(); presentedGeneralJSON.kb_jwt = encodedKbJwt; return presentedGeneralJSON; }); } // This function is for verifying the SD JWT // If requiredClaimKeys is provided, it will check if the required claim keys are presentation in the SD JWT // If requireKeyBindings is true, it will check if the key binding JWT is presentation and verify it verify(generalJSON, options) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const hasher = this.userConfig.hasher; const { payload, headers } = yield this.validate(generalJSON); const encodedSDJwt = generalJSON.toEncoded(0); const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher); if (!sdjwt.jwt || !sdjwt.jwt.payload) { throw new SDJWTException6("Invalid SD JWT"); } if (options == null ? void 0 : options.requiredClaimKeys) { const keys = yield sdjwt.keys(hasher); const missingKeys = options == null ? void 0 : options.requiredClaimKeys.filter( (k) => !keys.includes(k) ); if (missingKeys.length > 0) { throw new SDJWTException6( `Missing required claim keys: ${missingKeys.join(", ")}` ); } } if (!(options == null ? void 0 : options.keyBindingNonce)) { return { payload, headers }; } if (!sdjwt.kbJwt) { throw new SDJWTException6("Key Binding JWT not exist"); } if (!this.userConfig.kbVerifier) { throw new SDJWTException6("Key Binding Verifier not found"); } const kb = yield sdjwt.kbJwt.verifyKB({ verifier: this.userConfig.kbVerifier, payload, nonce: options.keyBindingNonce }); if (!kb) { throw new Error("signature is not valid"); } const sdHashfromKb = kb.payload.sd_hash; const sdjwtWithoutKb = new SDJwt({ jwt: sdjwt.jwt, disclosures: sdjwt.disclosures }); const presentSdJwtWithoutKb = sdjwtWithoutKb.encodeSDJwt(); const sdHashStr = yield this.calculateSDHash( presentSdJwtWithoutKb, sdjwt, hasher ); if (sdHashStr !== sdHashfromKb) { throw new SDJWTException6("Invalid sd_hash in Key Binding JWT"); } return { payload, headers, kb }; }); } calculateSDHash(presentSdJwtWithoutKb, sdjwt, hasher) { return __async(this, null, function* () { if (!sdjwt.jwt || !sdjwt.jwt.payload) { throw new SDJWTException6("Invalid SD JWT"); } const { _sd_alg } = getSDAlgAndPayload2(sdjwt.jwt.payload); const sdHash = yield hasher(presentSdJwtWithoutKb, _sd_alg); const sdHashStr = uint8ArrayToBase64Url2(sdHash); return sdHashStr; }); } // This function is for validating the SD JWT // Just checking signature and return its the claims validate(generalJSON) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } if (!this.userConfig.verifier) { throw new SDJWTException6("Verifier not found"); } const hasher = this.userConfig.hasher; const verifier = this.userConfig.verifier; const { payload, signatures } = generalJSON; const results = yield Promise.all( signatures.map((s) => __async(this, null, function* () { const { protected: encodedHeader, signature } = s; const verified2 = yield verifier( `${encodedHeader}.${payload}`, signature ); const header = JSON.parse(base64urlDecode(encodedHeader)); return { verified: verified2, header }; })) ); const verified = results.every((r) => r.verified); if (!verified) { throw new SDJWTException6("Signature is not valid"); } const encodedSDJwt = generalJSON.toEncoded(0); const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher); if (!sdjwt.jwt) { throw new SDJWTException6("Invalid SD JWT"); } const claims = yield sdjwt.getClaims(hasher); return { payload: claims, headers: results.map((r) => r.header) }; }); } config(newConfig) { this.userConfig = __spreadValues(__spreadValues({}, this.userConfig), newConfig); } encode(sdJwt, index) { return sdJwt.toEncoded(index); } decode(endcodedSDJwt) { return GeneralJSON.fromEncode(endcodedSDJwt); } keys(generalSdjwt) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const endcodedSDJwt = generalSdjwt.toEncoded(0); const sdjwt = yield SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher); return sdjwt.keys(this.userConfig.hasher); }); } presentableKeys(generalSdjwt) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const endcodedSDJwt = generalSdjwt.toEncoded(0); const sdjwt = yield SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher); return sdjwt.presentableKeys(this.userConfig.hasher); }); } getClaims(generalSdjwt) { return __async(this, null, function* () { if (!this.userConfig.hasher) { throw new SDJWTException6("Hasher not found"); } const endcodedSDJwt = generalSdjwt.toEncoded(0); const sdjwt = yield SDJwt.fromEncode(endcodedSDJwt, this.userConfig.hasher); return sdjwt.getClaims(this.userConfig.hasher); }); } }; SDJwtGeneralJSONInstance.DEFAULT_hashAlg = "sha-256"; export { FlattenJSON, GeneralJSON, Jwt, KBJwt, SDJwt, SDJwtGeneralJSONInstance, SDJwtInstance, createDecoy, listKeys, pack };