@lacspace/crypto
v1.1.2
Published
Safe, boring cryptography over Web Crypto — authenticated AES-256-GCM, PBKDF2 key derivation, SHA-256, HMAC, secure random and constant-time compare. Isomorphic (Node, edge, browser, RN).
Maintainers
Readme
@lacspace/crypto
Safe, boring cryptography — authenticated AES-256-GCM, key derivation, hashing.
A thin, correct layer over the Web Crypto API — no hand-rolled crypto. Authenticated AES-256-GCM, PBKDF2 key derivation, SHA-256/384/512, HMAC, secure random and constant-time compare. Same code on Node 18+, edge, browsers and React Native. Encrypt database fields, S3 payloads, cookies and tokens.
- 🔐
encrypt/decrypt— AES-256-GCM (authenticated: tampering is rejected) - 🔑
encryptWithPassword/decryptWithPassword— PBKDF2-derived key, self-contained - #️⃣
sha256/digest/hmac/hmacVerify - 🎲
randomBytes·generateKey·constantTimeEqual - 🧰 hex / base64url helpers
- ⚡ Zero dependencies · 🌍 isomorphic · 📦 ESM + CJS · fully typed
Install
npm install @lacspace/crypto # or pnpm add / yarn add / bun addEncrypt with a key
import { generateKey, encrypt, decrypt } from "@lacspace/crypto";
const key = generateKey(); // 256-bit base64url key — store securely
const blob = await encrypt("card: 4242…", key);
// "v1:<iv>:<ciphertext+tag>" — safe to store in Mongo / S3
const plain = await decrypt(blob, key); // "card: 4242…"Encrypt a field before saving to MongoDB, or an object before putting it on S3:
await s3.putObject({ Bucket, Key, Body: await encrypt(JSON.stringify(doc), key) });Encrypt with a passphrase
import { encryptWithPassword, decryptWithPassword } from "@lacspace/crypto";
const sealed = await encryptWithPassword("secret", userPassphrase);
// "v1p:<iterations>:<salt>:<iv>:<ciphertext>" — fully self-describing
const opened = await decryptWithPassword(sealed, userPassphrase);Hashing, HMAC & helpers
import { sha256, hmac, hmacVerify, constantTimeEqual, randomBytes } from "@lacspace/crypto";
await sha256("hello"); // hex digest
const sig = await hmac(secret, "payload"); // Uint8Array
await hmacVerify(secret, "payload", sig); // true (constant-time)
constantTimeEqual(a, b); // timing-safe compare
randomBytes(16); // CSPRNG bytesAPI
| Export | Description |
| --- | --- |
| encrypt / decrypt | AES-256-GCM with a 32-byte key |
| encryptWithPassword / decryptWithPassword | passphrase (PBKDF2 + AES-GCM) |
| generateKey | random 256-bit key (base64url) |
| sha256 / digest / hmac / hmacVerify | hashing & MAC |
| deriveBits | PBKDF2 key derivation |
| randomBytes / constantTimeEqual | primitives |
| toHex / fromHex / toBase64url / fromBase64url | encoding |
The Lacspace Security Kit
| Package | For |
| --- | --- |
| @lacspace/crypto | AES encryption & hashing (this package) |
| @lacspace/password | Password hashing |
| @lacspace/jwt | JWTs & tokens |
| @lacspace/apikey | API keys |
| @lacspace/otp | TOTP/HOTP 2FA |
| @lacspace/webauthn | Passkeys / biometric |
| @lacspace/mfa | 2FA/3FA orchestration |
| @lacspace/lock | Account lockout |
| @lacspace/headers | Secure headers / CSP |
| @lacspace/redact | Log redaction |
New in 1.1 — AAD, HKDF & key rotation
import { encrypt, decrypt, hkdf, Keyring } from "@lacspace/crypto";
// Bind ciphertext to a context so it can't be relocated to another row
const blob = await encrypt(secret, key, { aad: `user:${id}` });
await decrypt(blob, key, { aad: `user:${id}` }); // must match
// Derive many purpose-bound sub-keys from one master key
const encKey = await hkdf(master, { info: "field-encryption", length: 32 });
// Zero-downtime key rotation — new writes use the primary, old blobs still decrypt
const ring = new Keyring([{ id: "2025", key: oldKey }, { id: "2026", key: newKey }]);
const fresh = await ring.encrypt("secret"); // v2:2026:…
const text = await ring.decrypt(oldBlob); // finds the key by id
const migrated = await ring.reEncrypt(oldBlob); // re-key to primaryAlso decryptBytes() for binary-safe payloads (files, protobufs).
Licensing
This package is free under the Lacspace Free Licence — MIT-equivalent freedoms. Use it in personal and commercial projects at no cost; just keep the notice.
Not every Lacspace package is free. We also offer Commercial (paid), Client-specific, and Private (proprietary) packages under separate terms. See the full Lacspace Licence Centre.
Part of the Lacspace ecosystem — 35 zero-dependency, isomorphic TypeScript packages.
