@sphereon/ssi-sdk-ext.jwt-service
Version:
280 lines • 12.5 kB
JavaScript
;
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