@worker-tools/encrypted-cookie-store
Version:
A partial implementation of the Cookie Store API that transparently encrypts and decrypts cookies via AES-GCM.
131 lines • 7.27 kB
JavaScript
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;
export * from 'cookie-store-interface';
import { bufferSourceToUint8Array, concatBufferSources, splitBufferSource } from "typed-array-utils";
import { Base64Decoder, Base64Encoder } from "base64-encoding";
import { AggregateError } from "./aggregate-error.js";
const EXT = '.enc';
const IV_LENGTH = 16; // bytes
const secretToUint8Array = (secret) => typeof secret === 'string'
? new TextEncoder().encode(secret)
: 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.
*/
export 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 Base64Decoder().decode(cookie.value);
const [iv, cipher] = 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 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
? bufferSourceToUint8Array(opts.salt)
: new 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 Base64Encoder({ url: true }).encode(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);
}
}
_EncryptedCookieStore_store = new WeakMap(), _EncryptedCookieStore_keyring = new WeakMap(), _EncryptedCookieStore_key = new WeakMap(), _EncryptedCookieStore_decrypt = new WeakMap();
//# sourceMappingURL=index.js.map