Node.js crypto encryption for Keyv
Encrypt and decrypt values stored in Keyv using the Node.js crypto module. Supports the authenticated ciphers AES-GCM (default), AES-CCM, and ChaCha20-Poly1305, so a value changed in the store fails to decrypt instead of decrypting to altered data.
Table of Contents
Install
npm install --save keyv @keyv/encrypt-node
Usage
import Keyv from 'keyv';
import KeyvEncryptNode from '@keyv/encrypt-node';
// A random secret, such as the output of `openssl rand -base64 32` (see options.key)
const encryption = new KeyvEncryptNode({ key: process.env.KEYV_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 KeyvEncryptNode(options)
options.key
Type: string | Buffer
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. Buffer 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: NodeAlgorithm
Default: 'aes-256-gcm'
The cipher algorithm to use. Supported values, all authenticated (AEAD):
aes-256-gcm,aes-192-gcm,aes-128-gcmaes-256-ccm,aes-192-ccm,aes-128-ccmchacha20-poly1305
Any other algorithm throws when the adapter is created. AES-CCM can't encrypt a serialized value of 16 MiB or more; writing one fails.
options.encoding
Type: NodeEncoding
Default: 'base64'
The encoding used for the encrypted output string: 'base64', 'base64url', or 'hex'. Other encodings can't hold arbitrary bytes, so they throw 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 { pbkdf2Sync } from 'node:crypto';
import KeyvEncryptNode from '@keyv/encrypt-node';
// Derive the key once at startup: 600,000 iterations of PBKDF2-HMAC-SHA256 take on the order of 100 ms.
// 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 key = pbkdf2Sync(process.env.KEYV_PASSPHRASE, process.env.KEYV_SALT, 600_000, 32, 'sha256');
const encryption = new KeyvEncryptNode({ key });
The key length must match the algorithm: 32 bytes for aes-256-gcm, aes-256-ccm, and chacha20-poly1305, 24 for the aes-192 algorithms, and 16 for the aes-128 ones.