@sd-jwt/core
Version:
sd-jwt draft 7 implementation in typescript
1,176 lines (1,166 loc) • 39.6 kB
JavaScript
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 {
base64urlDecode,
base64urlEncode as base64urlEncode3,
SDJWTException as SDJWTException6,
uint8ArrayToBase64Url as uint8ArrayToBase64Url2
} from "@sd-jwt/utils";
// src/jwt.ts
import { base64urlEncode, SDJWTException } from "@sd-jwt/utils";
import { decodeJwt } from "@sd-jwt/decode";
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 SDJWTException("Serialize Error: Invalid JWT");
}
if (this.encoded) {
const parts = this.encoded.split(".");
if (parts.length !== 3) {
throw new SDJWTException(`Invalid JWT format: ${this.encoded}`);
}
const unsignedToken = parts.slice(0, 2).join(".");
return unsignedToken;
}
const header = base64urlEncode(JSON.stringify(this.header));
const payload = base64urlEncode(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 SDJWTException("Serialize Error: Invalid JWT");
}
const header = base64urlEncode(JSON.stringify(this.header));
const payload = base64urlEncode(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 SDJWTException("Verify Error: JWT is not yet valid");
}
if (((_c = this.payload) == null ? void 0 : _c.nbf) && this.payload.nbf - skew > currentDate) {
throw new SDJWTException("Verify Error: JWT is not yet valid");
}
if (((_d = this.payload) == null ? void 0 : _d.exp) && this.payload.exp + skew < currentDate) {
throw new SDJWTException("Verify Error: JWT is expired");
}
if (!this.signature) {
throw new SDJWTException("Verify Error: no signature in JWT");
}
const data = this.getUnsignedToken();
const verified = yield verifier(data, this.signature);
if (!verified) {
throw new SDJWTException("Verify Error: Invalid JWT Signature");
}
return { payload: this.payload, header: this.header };
});
}
};
// src/kbjwt.ts
import { SDJWTException as SDJWTException2 } from "@sd-jwt/utils";
import {
KB_JWT_TYP
} from "@sd-jwt/types";
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 SDJWTException2("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 SDJWTException2("Invalid Key Binding Jwt");
}
const data = this.getUnsignedToken();
const verified = yield values.verifier(
data,
this.signature,
values.payload
);
if (!verified) {
throw new SDJWTException2("Verify Error: Invalid JWT Signature");
}
if (this.payload.nonce !== values.nonce) {
throw new SDJWTException2("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/decoy.ts
import { uint8ArrayToBase64Url } from "@sd-jwt/utils";
var createDecoy = (hash, saltGenerator) => __async(void 0, null, function* () {
const { hasher, alg } = hash;
const salt = yield saltGenerator(16);
const decoy = yield hasher(salt, alg);
return uint8ArrayToBase64Url(decoy);
});
// src/sdjwt.ts
import { SDJWTException as SDJWTException3, Disclosure } from "@sd-jwt/utils";
import {
SD_DECOY,
SD_DIGEST,
SD_LIST_KEY,
SD_SEPARATOR
} from "@sd-jwt/types";
import { createHashMapping, getSDAlgAndPayload, unpack } from "@sd-jwt/decode";
import { transformPresentationFrame } from "@sd-jwt/present";
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_SEPARATOR);
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_SEPARATOR);
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 SDJWTException3("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 SDJWTException3("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_SEPARATOR);
data.push(encodeddisclosures);
}
data.push(this.kbJwt ? this.kbJwt.encodeJwt() : "");
return data.join(SD_SEPARATOR);
}
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 SDJWTException3("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 SDJWTException3("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(void 0, 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);
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-ignore
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
import {
KB_JWT_TYP as KB_JWT_TYP2,
IANA_HASH_ALGORITHMS
} from "@sd-jwt/types";
import { getSDAlgAndPayload as getSDAlgAndPayload2 } from "@sd-jwt/decode";
// src/flattenJSON.ts
import { SDJWTException as SDJWTException4 } from "@sd-jwt/utils";
import { splitSdJwt } from "@sd-jwt/decode";
import { SD_SEPARATOR as SD_SEPARATOR2 } from "@sd-jwt/types";
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 SDJWTException4("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_SEPARATOR2);
data.push(disclosures);
}
const kb_jwt = (_a = this.kb_jwt) != null ? _a : "";
data.push(kb_jwt);
return data.join(SD_SEPARATOR2);
}
};
// src/generalJSON.ts
import { base64urlEncode as base64urlEncode2, SDJWTException as SDJWTException5 } from "@sd-jwt/utils";
import { splitSdJwt as splitSdJwt2 } from "@sd-jwt/decode";
import { SD_SEPARATOR as SD_SEPARATOR3 } from "@sd-jwt/types";
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 SDJWTException5("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 SDJWTException5("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 SDJWTException5("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_SEPARATOR3);
data.push(disclosures);
}
const kb = (_a = this.kb_jwt) != null ? _a : "";
data.push(kb);
return data.join(SD_SEPARATOR3);
}
addSignature(protectedHeader, signer, kid) {
return __async(this, null, function* () {
const header = base64urlEncode2(JSON.stringify(protectedHeader));
const signature = yield signer(`${header}.${this.payload}`);
this.signatures.push({
protected: header,
signature,
kid
});
});
}
};
// 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
};