@keyv/encrypt-web

Web Crypto API encryption for Keyv

build codecov npm npm

Encrypt and decrypt values stored in Keyv using the Web Crypto API (crypto.subtle). Works in browsers, Deno, Cloudflare Workers, and Node.js 18+. No Node.js-specific dependencies.

Table of Contents

Install

npm install --save keyv @keyv/encrypt-web

Usage

import Keyv from 'keyv';
import KeyvEncryptWeb from '@keyv/encrypt-web';

// Placeholder: use a random secret, such as the output of `openssl rand -base64 32` (see options.key),
// loaded from your runtime's secrets (an environment variable, a Workers secret) rather than written here.
const secret = 'replace-with-a-random-secret';
const encryption = new KeyvEncryptWeb({ key: secret });
const keyv = new Keyv({ encryption });

await keyv.set('foo', 'bar');
const value = await keyv.get('foo'); // 'bar' (decrypted automatically)

Encryption runs on the serialized value, so it needs serialization, which is on by default. With serialization: false, Keyv doesn't store values unencrypted: writes fail, Keyv emits error, and set() returns false when a listener is attached or rejects when none is.

API

new KeyvEncryptWeb(options)

options.key

Type: string | Uint8Array
Required

The encryption key. A string key is hashed once with SHA-256, with no salt or key stretching, and truncated to the length the algorithm needs. That's safe for a random secret but not for a password or passphrase: anyone who reads one stored value can test guesses offline at full speed. Use a random secret, such as the output of openssl rand -base64 32, and keep it out of source control. Uint8Array keys are used directly and must match the expected key length. To use a password or passphrase, derive the key with PBKDF2 first; see Using a password or passphrase.

options.algorithm

Type: WebAlgorithm
Default: 'aes-256-gcm'

The cipher algorithm to use. Supported values, all AES-GCM, which is authenticated (AEAD):

  • aes-256-gcm, aes-192-gcm, aes-128-gcm

Any other algorithm throws when the adapter is created.

Using a password or passphrase

If the key has to come from something a person chooses, derive the key bytes with PBKDF2 and pass those instead of the string. PBKDF2 repeats the hash many times, so every guess costs an attacker the same work:

import KeyvEncryptWeb from '@keyv/encrypt-web';

// Placeholders: load the passphrase from your runtime's secrets. The salt is a random value you
// generate once (`openssl rand -base64 16`) and keep with your configuration. It isn't secret, but
// reading values back needs the same passphrase and salt.
const passphrase = 'replace-with-your-passphrase';
const salt = 'replace-with-your-salt';

// Derive the key once at startup: 600,000 iterations of PBKDF2-HMAC-SHA256 take on the order of 100 ms.
const encoder = new TextEncoder();
const passphraseKey = await crypto.subtle.importKey('raw', encoder.encode(passphrase), 'PBKDF2', false, ['deriveBits']);
const bits = await crypto.subtle.deriveBits(
  { name: 'PBKDF2', hash: 'SHA-256', salt: encoder.encode(salt), iterations: 600_000 },
  passphraseKey,
  256,
);
const encryption = new KeyvEncryptWeb({ key: new Uint8Array(bits) });

Derive 256 bits for aes-256-gcm, 192 for aes-192-gcm, and 128 for aes-128-gcm.

License

MIT © Jared Wray

Edit this page