UNPKG

@inngest/middleware-encryption

Version:
86 lines (85 loc) 4.37 kB
"use strict"; 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LibSodiumEncryptionService = void 0; const libsodium_wrappers_1 = __importDefault(require("libsodium-wrappers")); /** * The default encryption service used by the encryption middleware. * * This service uses LibSodium to encrypt and decrypt data. It supports multiple * keys, so that you can rotate keys without breaking existing encrypted data. * * An option is also provided to encrypt with a previous methodology, allowing * you to transition all services to using this new strategy before removing the * flag. */ class LibSodiumEncryptionService { constructor(key) { this.identifier = "inngest/libsodium"; if (!key) { throw new Error("Missing encryption key(s) in encryption middleware"); } const keys = (Array.isArray(key) ? key : [key]) .map((s) => s.trim()) .filter(Boolean); if (!keys.length) { throw new Error("Missing encryption key(s) in encryption middleware"); } /** * Ensure we pre-hash the keys to the correct length before using them, and * also always wait for sodium to be ready. Accessing keys in other * functions always requires awaiting this value, so we can never skip this * readiness check. */ this.keys = libsodium_wrappers_1.default.ready.then(() => { return keys.map((k) => { return libsodium_wrappers_1.default.crypto_generichash(libsodium_wrappers_1.default.crypto_secretbox_KEYBYTES, k); }); }); } encrypt(value) { return __awaiter(this, void 0, void 0, function* () { const keys = yield this.keys; const nonce = libsodium_wrappers_1.default.randombytes_buf(libsodium_wrappers_1.default.crypto_secretbox_NONCEBYTES); const message = libsodium_wrappers_1.default.from_string(JSON.stringify(value)); const ciphertext = libsodium_wrappers_1.default.crypto_secretbox_easy(message, nonce, keys[0]); const combined = new Uint8Array(nonce.length + ciphertext.length); combined.set(nonce); combined.set(ciphertext, nonce.length); return libsodium_wrappers_1.default.to_base64(combined, libsodium_wrappers_1.default.base64_variants.ORIGINAL); }); } decrypt(value) { return __awaiter(this, void 0, void 0, function* () { const keys = yield this.keys; let err; for (const key of keys) { try { const combined = libsodium_wrappers_1.default.from_base64(value, libsodium_wrappers_1.default.base64_variants.ORIGINAL); const nonce = combined.slice(0, libsodium_wrappers_1.default.crypto_secretbox_NONCEBYTES); const ciphertext = combined.slice(libsodium_wrappers_1.default.crypto_secretbox_NONCEBYTES); const decrypted = libsodium_wrappers_1.default.crypto_secretbox_open_easy(ciphertext, nonce, key); const decoder = new TextDecoder("utf8"); return JSON.parse(decoder.decode(decrypted)); } catch (decryptionError) { err = decryptionError; } } throw (err || new Error("Unable to decrypt value; no keys were able to decrypt it")); }); } } exports.LibSodiumEncryptionService = LibSodiumEncryptionService;