@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
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 __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
;