UNPKG

@worker-tools/encrypted-cookie-store

Version:

A partial implementation of the Cookie Store API that transparently encrypts and decrypts cookies via AES-GCM.

149 lines 8.26 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 __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _EncryptedCookieStore_store, _EncryptedCookieStore_keyring, _EncryptedCookieStore_key, _EncryptedCookieStore_decrypt; Object.defineProperty(exports, "__esModule", { value: true }); exports.EncryptedCookieStore = void 0; __exportStar(require("cookie-store-interface"), exports); const typed_array_utils_1 = require("typed-array-utils"); const base64_encoding_1 = require("base64-encoding"); const aggregate_error_js_1 = require("./aggregate-error.js"); const EXT = '.enc'; const IV_LENGTH = 16; // bytes const secretToUint8Array = (secret) => typeof secret === 'string' ? new TextEncoder().encode(secret) : (0, typed_array_utils_1.bufferSourceToUint8Array)(secret); /** * # Encrypted Cookie Store * A partial implementation of the [Cookie Store API](https://wicg.github.io/cookie-store) * that transparently encrypts and decrypts cookies via AES-GCM. * * This is likely only useful in server-side implementations, * but written in a platform-agnostic way. */ class EncryptedCookieStore { constructor(store, key, opts = {}) { var _a; _EncryptedCookieStore_store.set(this, void 0); _EncryptedCookieStore_keyring.set(this, void 0); _EncryptedCookieStore_key.set(this, void 0); _EncryptedCookieStore_decrypt.set(this, async (cookie) => { const errors = []; for (const key of __classPrivateFieldGet(this, _EncryptedCookieStore_keyring, "f")) { try { const buffer = new base64_encoding_1.Base64Decoder().decode(cookie.value); const [iv, cipher] = (0, typed_array_utils_1.splitBufferSource)(buffer, IV_LENGTH); const clearBuffer = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, cipher); const clearText = new TextDecoder().decode(clearBuffer); cookie.name = cookie.name.substring(0, cookie.name.length - EXT.length); cookie.value = clearText; return cookie; } catch (err) { errors.push(err); } } throw new aggregate_error_js_1.AggregateError(errors, 'None of the provided keys was able to decrypt the cookie.'); }); __classPrivateFieldSet(this, _EncryptedCookieStore_store, store, "f"); __classPrivateFieldSet(this, _EncryptedCookieStore_key, key, "f"); __classPrivateFieldSet(this, _EncryptedCookieStore_keyring, [key, ...(_a = opts.keyring) !== null && _a !== void 0 ? _a : []], "f"); } /** A helper function to derive a crypto key from a passphrase */ static async deriveCryptoKey(opts) { var _a, _b, _c, _d; if (!opts.secret) throw Error('Secret missing'); const passphraseKey = await (opts.format === 'jwk' ? crypto.subtle.importKey('jwk', opts.secret, 'PBKDF2', false, ['deriveKey']) : crypto.subtle.importKey((_a = opts.format) !== null && _a !== void 0 ? _a : 'raw', secretToUint8Array(opts.secret), 'PBKDF2', false, ['deriveKey', 'deriveBits'])); const key = await crypto.subtle.deriveKey({ name: 'PBKDF2', iterations: (_b = opts.iterations) !== null && _b !== void 0 ? _b : 999, hash: (_c = opts.hash) !== null && _c !== void 0 ? _c : 'SHA-256', salt: opts.salt ? (0, typed_array_utils_1.bufferSourceToUint8Array)(opts.salt) : new base64_encoding_1.Base64Decoder().decode('Gfw5ic5qS062JvoubvO+DA==') }, passphraseKey, { name: 'AES-GCM', length: (_d = opts.length) !== null && _d !== void 0 ? _d : 256, }, false, ['encrypt', 'decrypt']); return key; } async get(name) { if (typeof name !== 'string') throw Error('Overload not implemented.'); const cookie = await __classPrivateFieldGet(this, _EncryptedCookieStore_store, "f").get(`${name}${EXT}`); if (!cookie) return cookie; // FIXME: empty values! return __classPrivateFieldGet(this, _EncryptedCookieStore_decrypt, "f").call(this, cookie); } async getAll(options) { if (options != null) throw Error('Overload not implemented.'); const list = []; for (const cookie of await __classPrivateFieldGet(this, _EncryptedCookieStore_store, "f").getAll(options)) { if (cookie.name.endsWith(EXT)) { list.push(await __classPrivateFieldGet(this, _EncryptedCookieStore_decrypt, "f").call(this, cookie)); } } return list; } async set(options, value) { var _a; const [name, val] = typeof options === 'string' ? [options, value !== null && value !== void 0 ? value : ''] : [options.name, (_a = options.value) !== null && _a !== void 0 ? _a : '']; // FIXME: empty string! const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH)); const message = new TextEncoder().encode(val); const cipher = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, __classPrivateFieldGet(this, _EncryptedCookieStore_key, "f"), message); const cipherB64 = new base64_encoding_1.Base64Encoder({ url: true }).encode((0, typed_array_utils_1.concatBufferSources)(iv, cipher)); return __classPrivateFieldGet(this, _EncryptedCookieStore_store, "f").set({ ...typeof options === 'string' ? {} : options, name: `${name}${EXT}`, value: cipherB64, }); } delete(options) { if (typeof options !== 'string') throw Error('Overload not implemented.'); return __classPrivateFieldGet(this, _EncryptedCookieStore_store, "f").delete(`${options}${EXT}`); } addEventListener(...args) { return __classPrivateFieldGet(this, _EncryptedCookieStore_store, "f").addEventListener(...args); } dispatchEvent(event) { return __classPrivateFieldGet(this, _EncryptedCookieStore_store, "f").dispatchEvent(event); } removeEventListener(...args) { return __classPrivateFieldGet(this, _EncryptedCookieStore_store, "f").removeEventListener(...args); } } exports.EncryptedCookieStore = EncryptedCookieStore; _EncryptedCookieStore_store = new WeakMap(), _EncryptedCookieStore_keyring = new WeakMap(), _EncryptedCookieStore_key = new WeakMap(), _EncryptedCookieStore_decrypt = new WeakMap(); //# sourceMappingURL=index.js.map