W WRNexusJS
Security

@wrnexus/encryption

Hashing, HMAC, authenticated encryption, and key derivation.

bun add @wrnexus/encryption@0.2.15
Dependency-free crypto helpers for WRNexusJS: authenticated symmetric encryption (AES-256-GCM), hashing, and HMAC signing.

Part of the WRNexusJS framework — an SSR-first, Bun-native full-stack web framework.

Overview

This package provides small, focused cryptographic primitives for server-side use: encrypting secrets/tokens/database fields at rest with AES-256-GCM, deriving keys from passwords via PBKDF2, computing SHA-256 digests, and signing/verifying payloads with HMAC-SHA256. It is built entirely on the standard Web Crypto API (crypto.subtle) plus btoa/atob and TextEncoder/TextDecoder — no third-party dependencies. Reach for it whenever you need to protect sensitive values or verify webhook signatures. All functions are async (Web Crypto is promise-based).

Installation

bun add @wrnexus/encryption
Private package — the machine must be authenticated to the wrnexus npm org
(a read token in ~/.npmrc). Requires Bun (Node is not supported).

API

All keys are exchanged as base64 strings and all digests/signatures as hex strings.

ExportSignatureDescription
generateKey() => Promise<string>Generate a random 256-bit AES key, base64-encoded. Store it as a secret.
deriveKey(password: string, salt: string) => Promise<string>Derive a base64 AES-256 key from a password + salt using PBKDF2 (100,000 iterations, SHA-256).
encrypt(plaintext: string, key: string) => Promise<string>AES-256-GCM encrypt a string. Returns base64 of iv(12 bytes) ‖ ciphertext+tag. A fresh random IV is used each call.
decrypt(payload: string, key: string) => Promise<string>Decrypt a value produced by encrypt. Throws if the key is wrong or the data was tampered with.
sha256(data: string) => Promise<string>SHA-256 hex digest of a string (e.g. content hashing, dedup keys).
hmacSign(data: string, secret: string) => Promise<string>HMAC-SHA256 hex signature of data with secret (e.g. signing webhooks).
hmacVerify(data: string, secret: string, signature: string) => Promise<boolean>Constant-time verify of an HMAC-SHA256 hex signature.

Notes:

  • generateKey produces a 32-byte (256-bit) key via crypto.getRandomValues.
  • encrypt/decrypt require a base64-encoded 256-bit key; anything else throws "Encryption key must be a base64 256-bit key".
  • decrypt throws "Invalid ciphertext" if the payload is shorter than the 12-byte IV, and the underlying Web Crypto call throws on any authentication (tag) mismatch.
  • hmacVerify compares in constant time (length check plus XOR accumulation) to avoid timing leaks.

Usage

Symmetric encryption of a secret at rest:

import { generateKey, encrypt, decrypt } from "@wrnexus/encryption";

const key = await generateKey(); // store this safely (env/secret manager)

const box = await encrypt("card #1234", key); // opaque base64 string, safe to persist
const plain = await decrypt(box, key); // "card #1234"

Deriving a key from a user password instead of a random key:

import { deriveKey, encrypt } from "@wrnexus/encryption";

const key = await deriveKey("correct horse battery staple", "per-user-salt");
const box = await encrypt("secret note", key);

Hashing and webhook signature verification:

import { sha256, hmacSign, hmacVerify } from "@wrnexus/encryption";

const digest = await sha256("some content"); // 64-char hex string

const signature = await hmacSign(rawBody, webhookSecret);
const ok = await hmacVerify(rawBody, webhookSecret, incomingSignatureHeader);
if (!ok) throw new Error("Invalid webhook signature");

Requirements / Notes

  • Bun-only. Relies on the Web Crypto API (crypto.subtle, crypto.getRandomValues) and the global btoa/atob, TextEncoder/TextDecoder — all available in Bun's runtime.
  • No dependencies. The package has an empty dependency set; nothing is bundled beyond standard runtime APIs.
  • Algorithms: AES-256-GCM (encryption), PBKDF2 with 100k SHA-256 iterations (key derivation), SHA-256 (digest), HMAC-SHA256 (signing).
  • Keep generated/derived keys and HMAC secrets out of source control; treat them as first-class secrets.

Complete TypeScript API

This declaration is generated from the exact published package and lists its exported functions, classes, interfaces, and types.

/**
 * @wrnexus/encryption — authenticated symmetric encryption (AES-256-GCM) via
 * WebCrypto, dependency-free. Use it to encrypt secrets, tokens, or database
 * fields at rest.
 *
 *   const key = await generateKey();                 // store this safely
 *   const box = await encrypt("card #1234", key);    // opaque base64 string
 *   const plain = await decrypt(box, key);           // "card #1234"
 *
 * A key derived from a password (PBKDF2) is also supported via `deriveKey`.
 */
/** SHA-256 hex digest of a string (e.g. content hashing, dedup keys). */
declare function sha256(data: string): Promise<string>;
/** HMAC-SHA256 hex signature of `data` with `secret` (e.g. signing webhooks). */
declare function hmacSign(data: string, secret: string): Promise<string>;
/** Constant-time verify of an HMAC-SHA256 signature. */
declare function hmacVerify(data: string, secret: string, signature: string): Promise<boolean>;
/** Generate a random 256-bit key, base64-encoded. Store it as a secret. */
declare function generateKey(): Promise<string>;
/**
 * Encrypt a string. Output is base64 of `iv(12) || ciphertext+tag`, safe to
 * store or transmit. Each call uses a fresh random IV.
 */
declare function encrypt(plaintext: string, key: string): Promise<string>;
/** Decrypt a value produced by `encrypt`. Throws if the key is wrong or data tampered. */
declare function decrypt(payload: string, key: string): Promise<string>;
/** Derive a base64 AES key from a password + salt (PBKDF2, 100k iterations). */
declare function deriveKey(password: string, salt: string): Promise<string>;

export { decrypt, deriveKey, encrypt, generateKey, hmacSign, hmacVerify, sha256 };

Examples

Copy-ready examples taken from this package's published documentation.

Example 1

bun add @wrnexus/encryption

Example 2

import { generateKey, encrypt, decrypt } from "@wrnexus/encryption";

const key = await generateKey(); // store this safely (env/secret manager)

const box = await encrypt("card #1234", key); // opaque base64 string, safe to persist
const plain = await decrypt(box, key); // "card #1234"

Example 3

import { deriveKey, encrypt } from "@wrnexus/encryption";

const key = await deriveKey("correct horse battery staple", "per-user-salt");
const box = await encrypt("secret note", key);

Example 4

import { sha256, hmacSign, hmacVerify } from "@wrnexus/encryption";

const digest = await sha256("some content"); // 64-char hex string

const signature = await hmacSign(rawBody, webhookSecret);
const ok = await hmacVerify(rawBody, webhookSecret, incomingSignatureHeader);
if (!ok) throw new Error("Invalid webhook signature");