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 🙏

© 2025 – Pkg Stats / Ryan Hefner

webcrypt

v0.3.0

Published

Zero-dependency AES-256-GCM encryption for text, files & WebRTC E2EE. Includes symmetric (password) and asymmetric (RSA-4096 hybrid) modes. Streaming, quantum-resistant key derivation, pure Web Crypto API.

Readme

webcrypt

Zero-dependency • Strong End-to-End Encryption for the Modern Web (Updated 2025)
Pure Web Crypto API-powered AES-256-GCM symmetric encryption + full RSA-4096 hybrid asymmetric mode.
Now includes ECDSA digital signatures (P-256/P-384) for authenticity, detached file signing, and streaming-friendly signing workflows.

  • Password-based symmetric encryption (WebCrypt) – simple, fast, perfect for shared-secret scenarios
  • Public/private key asymmetric encryption (WebCryptAsym) – true public-key cryptography for messaging & file sharing
  • Digital signatures (WebCryptAsym) – ECDSA sign/verify for text and files
  • Zero dependencies • Works offline • Browser + Node.js • Quantum-resistant key derivation
npm install webcrypt
import { WebCrypt } from "webcrypt";
import { WebCryptAsym } from "webcrypt";

const wc = new WebCrypt();
const wca = new WebCryptAsym();

Works in: Browser • Node.js • React • Angular • Next.js • Vue • Svelte • Electron • Deno • Cloudflare Workers

Features

| Feature | Status | Details | | --------------------------- | ------ | ------------------------------------- | | Text encryption | Done | Returns base64 string | | File encryption | Done | Streaming — handles 10 GB+ files | | File decryption | Done | Restores original filename | | WebRTC E2EE (video + audio) | Done | Insertable Streams — true end-to-end | | Digital signatures | Done | ECDSA (P-256/P-384) sign & verify | | Zero dependencies | Done | Pure Web Crypto API | | Node.js 18+ support | Done | Native crypto.webcrypto | | Strong key derivation | Done | 600k PBKDF2 iterations + random salts | | Key caching | Done | Same password = instant reuse | | TypeScript support | Done | Full .d.ts included | | Asymmetric version | Done | See WebCryptAsym below |

What's new (2025)

  • ECDSA digital signatures added (WebCryptAsym): signText/verifyText, signFile/verifyFile, export/import signing keys.
  • Streaming-safe base64 utilities and improved file header formats for robust large-file handling.
  • Documentation & examples expanded for asymmetric signing and WebRTC hybrid key exchange.

Library overview

  • WebCrypt (symmetric)
    • Password-based AES-256-GCM encryption (PBKDF2 600k iterations).
    • Streaming file encryption with counter-derived IVs for low memory usage.
    • WebRTC Insertable Stream transforms derived from a shared password.
  • WebCryptAsym (asymmetric + signing)
    • RSA-4096 hybrid: RSA-OAEP encrypts ephemeral AES-256-GCM session keys; AES encrypts payloads.
    • ECDSA (P-256 / P-384) signing for text and files (detached signatures).
    • Streaming file handling and WebRTC hybrid key exchange (session key in first frame).
    • Export/import helpers for public/private keys (base64 SPKI/PKCS8).

Symmetric Usage (WebCrypt)

Encrypt & Decrypt Text
import { WebCrypt } from "webcrypt";

const wc = new WebCrypt();

const secret = "The treasure is buried under the old oak tree";

const encrypted = await wc.encryptText(secret, "my-super-secret-password");
console.log(encrypted);
// → long base64 string

const decrypted = await wc.decryptText(encrypted, "my-super-secret-password");
console.log(decrypted);
// → "The treasure is buried under the old oak tree"
Encrypt & Decrypt Files (streaming, low memory)
<input type="file" id="fileInput" />
<input type="password" id="pass" placeholder="Password" />
<button onclick="encrypt()">Encrypt File</button>
const wc = new WebCrypt();

async function encrypt() {
  const file = document.getElementById("fileInput").files[0];
  const password = document.getElementById("pass").value;

  const { blob, filename } = await wc.encryptFile(file, password);

  const a = document.createElement("a");
  a.href = URL.createObjectURL(blob);
  a.download = filename;
  a.click();
}

// Decryption is identical — just call decryptFile()
End-to-End Encrypted WebRTC Video Call
const wc = new WebCrypt();
const CALL_PASSWORD = "our-private-call-2025";

const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
document.getElementById("localVideo").srcObject = stream;

const pc = new RTCPeerConnection();

// Encrypt everything we send
stream.getTracks().forEach(async track => {
  const sender = pc.addTrack(track, stream);
  sender.transform = new RTCRtpScriptTransform(await wc.createEncryptTransform(CALL_PASSWORD));
});

// Decrypt everything we receive
pc.ontrack = async event => {
  const receiver = event.receiver;
  receiver.transform = new RTCRtpScriptTransform(await wc.createDecryptTransform(CALL_PASSWORD));
  document.getElementById("remoteVideo").srcObject = event.streams[0];
};

Both users use the exact same password → SFU/server sees only encrypted garbage.

Asymmetric Usage (WebCryptAsym)

import { WebCryptAsym } from "webcrypt";

const crypt = new WebCryptAsym();

// Generate and share public key
const keyPair = await crypt.generateKeyPair();
const publicKeyB64 = await crypt.exportPublicKey(keyPair.publicKey);

// Recipient imports your public key
const publicKey = await crypt.importPublicKey(publicKeyB64);

// Encrypt file for recipient
const { blob, filename } = await crypt.encryptFile(file, publicKey);

// Decrypt with private key
const { blob: decryptedBlob, filename: originalName } = await crypt.decryptFile(
  encryptedFile,
  keyPair.privateKey
);
Signing & Verifying (ECDSA)
// Generate a signing key pair (ECDSA)
const { publicKey, privateKey, publicKeyB64 } = await crypt.generateSigningKeyPair("P-256");

// Share publicKeyB64 with verifiers, keep privateKey safe

// Sign a short message
const message = "I approve transaction #123";
const signatureB64 = await crypt.signText(message, privateKey);

// Verify the message
const ok = await crypt.verifyText(message, signatureB64, publicKey);
// ok === true

// Sign a file (detached signature)
const { signatureB64: fileSig } = await crypt.signFile(myLargeFile, privateKey);

// Verify a file later
const valid = await crypt.verifyFile(myLargeFile, fileSig, publicKey);
// valid === true

// Import a verifier's public signing key (SPKI base64)
const verifierPub = await crypt.importPublicSigningKey(publicKeyB64, "P-256");

WebRTC transforms also available (session key sent encrypted in first frame).

API

Symmetric (WebCrypt)
const wc = new WebCrypt()

wc.encryptText(text: string, password: string): Promise<string>
wc.decryptText(b64: string, password: string): Promise<string>

wc.encryptFile(file: File|Blob, password: string): Promise<{ blob: Blob, filename: string }>
wc.decryptFile(file: File|Blob, password: string): Promise<{ blob: Blob, filename: string }>

wc.createEncryptTransform(password: string): Promise<TransformFunction>
wc.createDecryptTransform(password: string): Promise<TransformFunction>
Asymmetric (WebCryptAsym)
const crypt = new WebCryptAsym()

crypt.generateKeyPair(): Promise<CryptoKeyPair>
crypt.exportPublicKey(publicKey: CryptoKey): Promise<string>
crypt.exportPrivateKey(privateKey: CryptoKey): Promise<string>
crypt.importPublicKey(b64: string): Promise<CryptoKey>
crypt.importPrivateKey(b64: string): Promise<CryptoKey>

crypt.encryptText(text: string, publicKey: CryptoKey): Promise<string>
crypt.decryptText(b64: string, privateKey: CryptoKey): Promise<string>

crypt.encryptFile(file: File|Blob, publicKey: CryptoKey): Promise<{ blob: Blob, filename: string }>
crypt.decryptFile(file: File|Blob, privateKey: CryptoKey): Promise<{ blob: Blob, filename: string }>

crypt.createEncryptTransform(publicKey: CryptoKey): Promise<TransformFunction>
crypt.createDecryptTransform(privateKey: CryptoKey): Promise<TransformFunction>

-- Signing / Verification (ECDSA) --
crypt.generateSigningKeyPair(curve?: 'P-256' | 'P-384'): Promise<{ publicKey, privateKey, publicKeyB64 }>
crypt.importPublicSigningKey(publicKeyB64: string, curve?: string): Promise<CryptoKey>
crypt.signText(text: string, privateKey: CryptoKey): Promise<string>
crypt.verifyText(text: string, signatureB64: string, publicKey: CryptoKey): Promise<boolean>
crypt.signFile(file: File|Blob, privateKey: CryptoKey): Promise<{signatureB64: string, blob: Blob}>
crypt.verifyFile(file: File|Blob, signatureB64: string, publicKey: CryptoKey): Promise<boolean>

Security

  • AES-256-GCM (authenticated encryption)
  • 600,000 PBKDF2-SHA256 iterations (strong against brute-force)
  • Unique 128-bit salt per message/file
  • Unique 96-bit IV per chunk/frame
  • No keys ever leave your device
  • ECDSA (P-256/P-384) for signatures — compact and widely supported
  • AES-256 offers strong protection even against future quantum threats (Grover-resistant at this key size)

Browser Support

Chrome 80+ • Edge 80+ • Firefox 90+ • Safari 15+
All support Web Crypto API + Insertable Streams.

Node.js Support

Works natively in Node.js 18+ via built-in crypto.webcrypto.

const { WebCrypt } = require("webcrypt");

License

MIT License — free for personal and commercial use
© 2025 Lucas Armstrong
https://github.com/lucasarmstrong/webcrypt

No telemetry. No servers. No backdoors.
Just pure, unbreakable encryption that works offline, forever.

Star this repo if you believe in private communication.

Made with passion for a freer, safer internet.