npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@workkit/crypto

v0.2.0

Published

Ergonomic WebCrypto wrappers for Cloudflare Workers — AES-256-GCM, HKDF, envelope encryption, hashing

Readme

@workkit/crypto

AES-256-GCM encryption, key derivation, hashing, and random utilities for Workers

npm bundle size

Install

bun add @workkit/crypto

Usage

Before (raw WebCrypto)

// 20+ lines just to encrypt a value
const iv = crypto.getRandomValues(new Uint8Array(12))
const keyMaterial = await crypto.subtle.importKey(
  "raw",
  new TextEncoder().encode(secret),
  "PBKDF2",
  false,
  ["deriveKey"],
)
const key = await crypto.subtle.deriveKey(
  { name: "PBKDF2", salt, iterations: 100000, hash: "SHA-256" },
  keyMaterial,
  { name: "AES-GCM", length: 256 },
  false,
  ["encrypt"],
)
const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, encoded)
// ... concatenate IV, base64 encode, etc.

After (workkit crypto)

import { deriveKey, encrypt, decrypt, hash, hmac, randomHex } from "@workkit/crypto"

// Encrypt/decrypt in one line
const key = await deriveKey(secret, salt)
const token = await encrypt(key, { userId: "123", role: "admin" })
const data = await decrypt(key, token) // { userId: "123", role: "admin" }

// Hashing
const digest = await hash("SHA-256", "hello world") // hex string
const signature = await hmac("SHA-256", secret, payload) // HMAC hex

// Random values
const hex = randomHex(32) // 64-char hex string
const id = randomUUID() // crypto.randomUUID()

API

Encryption

  • encrypt(key, data) — AES-256-GCM encrypt. Accepts strings or JSON-serializable values. Returns base64.
  • decrypt(key, ciphertext) — Decrypt. Auto-parses JSON if applicable.

Key Management

  • generateKey() — Generate a new AES-GCM CryptoKey
  • deriveKey(secret, salt) — Derive a key from a password using PBKDF2
  • exportKey(key) — Export a CryptoKey to base64
  • importKey(base64) — Import a CryptoKey from base64

Hashing

  • hash(algorithm, data) — SHA-256/384/512 hash, returns hex
  • hmac(algorithm, key, data) — HMAC signature, returns hex

Random

  • randomBytes(length) — Cryptographically random Uint8Array
  • randomHex(length) — Random hex string
  • randomUUID() — UUID v4

Digital Signatures

  • sign(privateKey, data) — Sign data with a private key. Returns a base64-encoded signature. Accepts strings or JSON-serializable values.
  • sign.verify(publicKey, data, signature) — Verify a signature. Returns boolean.
  • generateSigningKeyPair(algorithm?) — Generate a signing key pair. Ed25519 by default, ECDSA P-256 fallback.
  • exportSigningKey(key) — Export a signing key (public or private) to base64. Uses SPKI for public, PKCS8 for private.
  • importSigningKey(base64, type, algorithm?) — Import a signing key from base64. type is "public" or "private".
import { sign, generateSigningKeyPair, exportSigningKey, importSigningKey } from "@workkit/crypto"

const { publicKey, privateKey } = await generateSigningKeyPair() // Ed25519
const signature = await sign(privateKey, { userId: "123" })
const valid = await sign.verify(publicKey, { userId: "123" }, signature) // true

const exported = await exportSigningKey(publicKey)
const restored = await importSigningKey(exported, "public")

Key Rotation

  • envelope.rotate(oldMasterKey, newMasterKey, encryptedKey, encryptedData) — Re-encrypt the DEK with a new master key. Data stays encrypted with the same DEK — O(1) regardless of data size.
import { envelope, generateKey } from "@workkit/crypto"

const newMaster = await generateKey()
const rotated = await envelope.rotate(oldMaster, newMaster, sealed.encryptedKey, sealed.encryptedData)
const data = await envelope.open(newMaster, rotated.encryptedKey, rotated.encryptedData)

AAD Encryption

  • encryptWithAAD(key, data, aad) — AES-256-GCM encrypt with Additional Authenticated Data. The AAD is verified during decryption but never encrypted.
  • decryptWithAAD(key, ciphertext, aad) — Decrypt ciphertext with the same AAD used during encryption. Fails if AAD doesn't match.
import { encryptWithAAD, decryptWithAAD, deriveKey } from "@workkit/crypto"

const key = await deriveKey(secret, salt)
const token = await encryptWithAAD(key, { role: "admin" }, "user:123")
const data = await decryptWithAAD(key, token, "user:123") // { role: "admin" }

License

MIT