UNPKG

@sphereon/ssi-sdk-ext.jwt-service

Version:

280 lines 12.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; 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.CompactJwtEncrypter = exports.generateContentEncryptionKey = void 0; exports.createJwe = createJwe; exports.jweMergeHeaders = jweMergeHeaders; exports.decryptJwe = decryptJwe; exports.toWebCryptoCiphertext = toWebCryptoCiphertext; const random_1 = require("@stablelib/random"); const utils_1 = require("@veramo/utils"); const jose = __importStar(require("jose")); const u8a = __importStar(require("uint8arrays")); const IJwtService_1 = require("../types/IJwtService"); const generateContentEncryptionKey = (_a) => __awaiter(void 0, [_a], void 0, function* ({ alg, randomSource = random_1.defaultRandomSource, }) { let length; switch (alg) { case 'A128GCM': length = 16; break; case 'A192GCM': length = 24; break; case 'A128CBC-HS256': case 'A256GCM': length = 32; break; case 'A192CBC-HS384': length = 48; break; case 'A256CBC-HS512': length = 64; break; default: length = 32; } return (0, random_1.randomBytes)(length, randomSource); }); exports.generateContentEncryptionKey = generateContentEncryptionKey; function jweAssertValid(jwe) { if (!(jwe.protected && jwe.iv && jwe.ciphertext && jwe.tag)) { throw Error('JWE is missing properties: protected, iv, ciphertext and/or tag'); } if (jwe.recipients) { jwe.recipients.map((recipient) => { if (!(recipient.header && recipient.encrypted_key)) { throw Error('Malformed JWE recipients; no header and encrypted key present'); } }); } } function jweEncode({ ciphertext, tag, iv, protectedHeader, recipients, aad, unprotected, }) { if (!recipients || recipients.length === 0) { throw Error(`No recipient found`); } return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (unprotected && { unprotected })), { protected: protectedHeader, iv: (0, utils_1.bytesToBase64url)(iv), ciphertext: (0, utils_1.bytesToBase64url)(ciphertext) }), (tag && { tag: (0, utils_1.bytesToBase64url)(tag) })), (aad && { aad: (0, utils_1.bytesToBase64url)(aad) })), { recipients }); } class CompactJwtEncrypter { constructor(args) { if (args === null || args === void 0 ? void 0 : args.alg) { this._alg = args.alg; } if (args === null || args === void 0 ? void 0 : args.enc) { this._enc = args.enc; } this._keyManagementParams = args.keyManagementParams; this.recipientKey = args.key; this.expirationTime = args.expirationTime; this.issuer = args.issuer; this.audience = args.audience; } get enc() { if (!this._enc) { throw Error(`enc not set`); } return this._enc; } set enc(value) { // @ts-ignore if (!IJwtService_1.JweEncs.includes(value)) { throw Error(`invalid JWE enc value ${value}`); } this._enc = value; } get alg() { if (!this._alg) { throw Error(`alg not set`); } return this._alg; } set alg(value) { // @ts-ignore if (!IJwtService_1.JweAlgs.includes(value)) { throw Error(`invalid JWE alg value ${value}`); } this._alg = value; } encryptCompactJWT(payload, jweProtectedHeader, aad) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; const protectedHeader = Object.assign(Object.assign({}, jweProtectedHeader), { alg: (_a = jweProtectedHeader.alg) !== null && _a !== void 0 ? _a : this._alg, enc: (_b = jweProtectedHeader.enc) !== null && _b !== void 0 ? _b : this._enc }); if (!protectedHeader.alg || !protectedHeader.enc) { return Promise.reject(Error(`no 'alg' or 'enc' value set for the protected JWE header!`)); } this.enc = protectedHeader.enc; this.alg = protectedHeader.alg; if (payload.exp) { this.expirationTime = payload.exp; } if (payload.iss) { this.issuer = payload.iss; } if (payload.aud) { this.audience = payload.aud; } const encrypt = new jose.EncryptJWT(payload).setProtectedHeader(Object.assign(Object.assign({}, protectedHeader), { alg: this.alg, enc: this.enc })); if (this._alg.startsWith('ECDH')) { if (!this._keyManagementParams) { return Promise.reject(Error(`ECDH requires key management params`)); } encrypt.setKeyManagementParameters(this._keyManagementParams); } if (this.expirationTime !== undefined) { encrypt.setExpirationTime(this.expirationTime); } if (this.issuer) { encrypt.setIssuer(this.issuer); } if (this.audience) { encrypt.setAudience(this.audience); } return yield encrypt.encrypt(this.recipientKey); }); } static decryptCompactJWT(jwt, key, options) { return __awaiter(this, void 0, void 0, function* () { return yield jose.jwtDecrypt(jwt, key, options); }); } encrypt(payload, jweProtectedHeader, aad) { return __awaiter(this, void 0, void 0, function* () { const jwt = yield this.encryptCompactJWT(JSON.parse(u8a.toString(payload)), jweProtectedHeader, aad); const [protectedHeader, encryptedKey, ivB64, payloadB64, tagB64] = jwt.split('.'); //[jwe.protected, jwe.encrypted_key, jwe.iv, jwe.ciphertext, jwe.tag].join('.'); console.log(`FIXME: TO EncryptionResult`); return { protectedHeader, tag: (0, utils_1.base64ToBytes)(tagB64), ciphertext: (0, utils_1.base64ToBytes)(payloadB64), iv: (0, utils_1.base64ToBytes)(ivB64), recipients: [ Object.assign({}, (encryptedKey && { encrypted_key: encryptedKey })), ], }; }); } } exports.CompactJwtEncrypter = CompactJwtEncrypter; function createJwe(cleartext, encrypters, protectedHeader, aad) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; if (encrypters.length === 0) { throw Error('JWE needs at least 1 encryptor'); } if (encrypters.find((enc) => enc.alg === 'dir' || enc.alg === 'ECDH-ES')) { if (encrypters.length !== 1) { throw Error(`JWE can only do "dir" or "ECDH-ES" encryption with one key. ${encrypters.length} supplied`); } const encryptionResult = yield encrypters[0].encrypt(cleartext, protectedHeader, aad); return jweEncode(Object.assign(Object.assign({}, encryptionResult), { aad })); } else { const tmpEnc = encrypters[0].enc; if (!encrypters.reduce((acc, encrypter) => acc && encrypter.enc === tmpEnc, true)) { throw new Error('invalid_argument: Incompatible encrypters passed'); } let cek = undefined; let jwe = undefined; for (const encrypter of encrypters) { if (!cek) { const encryptionResult = yield encrypter.encrypt(cleartext, protectedHeader, aad); cek = encryptionResult.cek; jwe = jweEncode(Object.assign(Object.assign({}, encryptionResult), { aad })); } else { const recipient = yield ((_a = encrypter.encryptCek) === null || _a === void 0 ? void 0 : _a.call(encrypter, cek)); if (recipient) { (_b = jwe === null || jwe === void 0 ? void 0 : jwe.recipients) === null || _b === void 0 ? void 0 : _b.push(recipient); } } } if (!jwe) { throw Error(`No JWE constructed`); } return jwe; } }); } /** * Merges all headers, so we get a unified header. * * @param protectedHeader * @param unprotectedHeader * @param recipientUnprotectedHeader */ function jweMergeHeaders({ protectedHeader, unprotectedHeader, recipientUnprotectedHeader, }) { // TODO: Check that all headers/params are disjoint! const header = Object.assign(Object.assign(Object.assign({}, protectedHeader), unprotectedHeader), recipientUnprotectedHeader); if (!header.alg || !header.enc) { throw Error(`Either 'alg' or 'enc' are missing from the headers`); } return header; } function decryptJwe(jwe, decrypter) { return __awaiter(this, void 0, void 0, function* () { jweAssertValid(jwe); const protectedHeader = JSON.parse((0, utils_1.decodeBase64url)(jwe.protected)); if ((protectedHeader === null || protectedHeader === void 0 ? void 0 : protectedHeader.enc) !== decrypter.enc) { return Promise.reject(Error(`Decrypter enc '${decrypter.enc}' does not support header enc '${protectedHeader.enc}'`)); } else if (!jwe.tag) { return Promise.reject(Error(`Decrypter enc '${decrypter.enc}' does not support header enc '${protectedHeader.enc}'`)); } const sealed = toWebCryptoCiphertext(jwe.ciphertext, jwe.tag); const aad = u8a.fromString(jwe.aad ? `${jwe.protected}.${jwe.aad}` : jwe.protected); let cleartext = null; if (protectedHeader.alg === 'dir' && decrypter.alg === 'dir') { cleartext = yield decrypter.decrypt(sealed, (0, utils_1.base64ToBytes)(jwe.iv), aad); } else if (!jwe.recipients || jwe.recipients.length === 0) { throw Error('missing recipients for JWE'); } else { for (let i = 0; !cleartext && i < jwe.recipients.length; i++) { const recipient = jwe.recipients[i]; recipient.header = Object.assign(Object.assign({}, recipient.header), protectedHeader); if (recipient.header.alg === decrypter.alg) { cleartext = yield decrypter.decrypt(sealed, (0, utils_1.base64ToBytes)(jwe.iv), aad, recipient); } } } if (cleartext === null) throw new Error('failure: Failed to decrypt'); return cleartext; }); } function toWebCryptoCiphertext(ciphertext, tag) { return u8a.concat([(0, utils_1.base64ToBytes)(ciphertext), (0, utils_1.base64ToBytes)(tag)]); } //# sourceMappingURL=JWE.js.map