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

@penumbra-tools/crypto

v1.0.1

Published

Client-side E2E encryption library using Web Crypto API. PBKDF2 key derivation, AES-GCM encryption, ECDH key exchange, and group key management.

Readme

penumbra-crypto

npm License: MIT

Client-side end-to-end encryption library using the Web Crypto API.

What This Protects

This library encrypts data client-side before it leaves the browser. The server never sees your plaintext data or encryption keys.

Protects against: Server compromise, database theft, man-in-the-middle attacks, and unauthorized data access by service operators.

Does NOT protect against: Compromised browser/device, malicious JavaScript served by the host, weak passphrases, or endpoint security failures. See SECURITY.md for the full threat model.

Features

  • PBKDF2 key derivation - Derive encryption keys from passphrases
  • AES-256-GCM encryption - Authenticated encryption for data
  • ECDH key exchange - Establish shared secrets between users
  • Group key management - Encrypt content for multiple recipients
  • Zero dependencies - Uses only the Web Crypto API
  • TypeScript - Full type definitions included

Installation

npm install @penumbra-tools/crypto

Quick Start

import { KeyManager, generateECDHKeyPair } from '@penumbra-tools/crypto';

// Initialize with user's passphrase
const keyManager = new KeyManager();
await keyManager.init(saltBase64, 'user-passphrase');

// Encrypt data
const encrypted = await keyManager.encrypt({ secret: 'data' });

// Decrypt data
const decrypted = await keyManager.decrypt(encrypted);

API Overview

Key Derivation

import { deriveKey } from '@penumbra-tools/crypto';

// Derive AES-256 key from passphrase
const salt = crypto.getRandomValues(new Uint8Array(16));
const key = await deriveKey('my-passphrase', salt);

Symmetric Encryption

import { encrypt, decrypt } from '@penumbra-tools/crypto';

// Encrypt any JSON-serializable data
const encrypted = await encrypt({ message: 'Hello' }, key);

// Decrypt
const decrypted = await decrypt(encrypted, key);

ECDH Key Exchange

import {
  generateECDHKeyPair,
  deriveConversationKey,
  encryptForConversation,
  decryptFromConversation
} from '@penumbra-tools/crypto';

// Generate key pair
const { publicKeyJwk, privateKeyJwk } = await generateECDHKeyPair();

// Share publicKeyJwk with other user
// Store privateKeyJwk (encrypted) for yourself

// Derive shared conversation key
const conversationKey = await deriveConversationKey(
  myPrivateKey,
  theirPublicKeyJwk
);

// Encrypt message
const encrypted = await encryptForConversation(data, conversationKey);

// Decrypt message
const decrypted = await decryptFromConversation(encrypted, conversationKey);

Group Encryption

import {
  generateGroupKey,
  encryptGroupKeyForMember,
  decryptGroupKey
} from '@penumbra-tools/crypto';

// Owner creates group key
const groupKey = await generateGroupKey();

// Encrypt for each member
const encryptedForMember = await encryptGroupKeyForMember(
  groupKey,
  ownerPrivateKey,
  memberPublicKeyJwk
);

// Member decrypts group key
const memberGroupKey = await decryptGroupKey(
  encryptedForMember,
  memberPrivateKey,
  ownerPublicKeyJwk
);

KeyManager Class

High-level key management:

import { KeyManager } from '@penumbra-tools/crypto';

const km = new KeyManager();

// Initialize
await km.init(saltBase64, 'passphrase');

// Check state
km.isUnlocked();  // true
km.hasECDH();     // false (until initECDH called)

// Encrypt/decrypt
const enc = await km.encrypt(data);
const dec = await km.decrypt(enc);

// ECDH
await km.initECDH(encryptedPrivateKeyBlob);
const msg = await km.encryptForConnection(data, connId, theirPubKey);

// Groups
km.cacheGroupKey(groupId, groupKey);
const groupEnc = await km.encryptForGroup(data, groupId);

// Lock (clear all keys)
km.lock();

Security

See SECURITY.md for:

  • Cryptographic design details
  • Threat model
  • Security properties
  • Compliance information

Summary

| Component | Algorithm | |-----------|-----------| | Key Derivation | PBKDF2-SHA256 (100k iterations) | | Encryption | AES-256-GCM | | Key Exchange | ECDH P-256 + HKDF |

Design Decisions

This library uses only the Web Crypto API with no external dependencies. This constrains algorithm choices but ensures broad compatibility and auditability.

| Choice | Why | Tradeoff | |--------|-----|----------| | PBKDF2 over Argon2id | Web Crypto API doesn't support Argon2id | PBKDF2 is more GPU-parallelizable; mitigate with higher iterations and strong passphrases | | P-256 over X25519 | Web Crypto API doesn't support Curve25519 | P-256 is NIST-standardized and widely audited; some prefer Curve25519's simpler implementation | | 100k iterations | Balance of security and UX on mobile | Increase for high-security applications; OWASP suggests 600k+ |

On iteration count: The PBKDF2_ITERATIONS constant is exported—fork or wrap the library to increase it for your deployment. A future v2 may add an optional WASM Argon2id fallback for applications that can accept the dependency.

See SECURITY.md for detailed cryptographic rationale.

Browser Support

Requires Web Crypto API support:

  • Chrome 37+
  • Firefox 34+
  • Safari 11+
  • Edge 12+
  • Node.js 18+

Development

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

License

MIT