WebCrypt
Zero-dependency end-to-end encryption for the modern web.

AES-256-GCM symmetric encryption, RSA-4096 hybrid asymmetric encryption, ECDH key exchange, digital signatures, HMAC, and streaming file encryption — all powered by the native Web Crypto API with zero runtime dependencies.
Quick Start
npm install webcrypt
Encrypt and decrypt text:
import { WebCrypt } from "webcrypt";
const wc = new WebCrypt();
const encrypted = await wc.encryptText("Secret message", "my-password");
const decrypted = await wc.decryptText(encrypted, "my-password");
Encrypt a file:
const { blob, filename } = await wc.encryptFile(file, "my-password");
Public-key encryption (RSA-4096):
import { WebCryptAsym } from "webcrypt";
const wca = new WebCryptAsym();
const keys = await wca.generateKeyPair();
const encrypted = await wca.encryptText("Secret", keys.publicKey);
const decrypted = await wca.decryptText(encrypted, keys.privateKey);
Table of Contents
Features
| Text encryption | ✅ Done | AES-256-GCM, returns base64 string |
| File encryption | ✅ Done | Streaming — handles large files (10 MB decrypt limit) |
| WebRTC E2EE | ✅ Done | Insertable Streams for video + audio |
| Digital signatures | ✅ Done | ECDSA, RSA-PSS |
| ECDH key exchange | ✅ Done | P-256 / P-384 Diffie-Hellman |
| HMAC | ✅ Done | SHA-256/384/512 and SHA-3 |
| Key derivation | ✅ Done | PBKDF2 (600k iterations), SHA-3 KDF, HKDF |
| Key caching | ✅ Done | 5-min TTL, LRU eviction (max 10) |
| TypeScript | ✅ Done | Full .d.ts for all modules |
| Zero dependencies | ✅ Done | Pure Web Crypto API |
| JWE (JSON Web Encryption) | ✅ Done | RFC 7516 Compact Serialization (RSA-OAEP/A256GCM) |
| Post-quantum (Kyber/Dilith) | ⚠️ Stub | Placeholder — see docs/PQC.md |
Modules
WebCrypt is split into three modules. Import only what you need:
import { WebCrypt } from "webcrypt";
import { WebCryptAsym } from "webcrypt";
import { WebCryptPQC } from "webcrypt";
WebCrypt | Password-based encryption | AES-256-GCM | ✅ Yes (Grover-resistant) |
WebCryptAsym | Public-key encryption | RSA-4096 + AES | ⚠️ RSA vulnerable to Shor |
WebCryptPQC | Post-quantum (future) | Kyber + Dilithium | ⚠️ Stub — not real PQC yet |
Symmetric Encryption (WebCrypt)
Password-based AES-256-GCM encryption with PBKDF2 key derivation (600,000 iterations).
Text
import { WebCrypt } from "webcrypt";
const wc = new WebCrypt();
const encrypted = await wc.encryptText("The treasure is buried under the oak tree", "password");
const decrypted = await wc.decryptText(encrypted, "password");
JSON Data
const data = { message: "Hello", users: ["Alice", "Bob"] };
const encrypted = await wc.encryptData(data, "password");
const decrypted = await wc.decryptData(encrypted, "password");
Files
const { blob, filename } = await wc.encryptFile(file, "password");
const { blob: decrypted, filename: originalName } = await wc.decryptFile(encryptedBlob, "password");
WebRTC End-to-End Encryption
const wc = new WebCrypt();
const PASSWORD = "shared-call-secret";
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
const pc = new RTCPeerConnection();
stream.getTracks().forEach(async track => {
const sender = pc.addTrack(track, stream);
sender.transform = new RTCRtpScriptTransform(await wc.createEncryptTransform(PASSWORD));
});
pc.ontrack = async event => {
event.receiver.transform = new RTCRtpScriptTransform(await wc.createDecryptTransform(PASSWORD));
document.getElementById("remoteVideo").srcObject = event.streams[0];
};
Both peers use the same password. The SFU/server sees only encrypted data.
Asymmetric Encryption (WebCryptAsym)
RSA-4096 hybrid encryption: RSA-OAEP encrypts an ephemeral AES-256-GCM session key, which encrypts the payload.
Encrypt / Decrypt
import { WebCryptAsym } from "webcrypt";
const crypt = new WebCryptAsym();
const keys = await crypt.generateKeyPair();
const publicKeyB64 = await crypt.exportPublicKey(keys.publicKey);
const publicKey = await crypt.importPublicKey(publicKeyB64);
const encrypted = await crypt.encryptText("Secret message", publicKey);
const decrypted = await crypt.decryptText(encrypted, keys.privateKey);
ECDH Key Exchange
Derive a shared secret between two parties without transmitting any secret material.
const alice = await crypt.generateECDHKeyPair();
const bob = await crypt.generateECDHKeyPair();
const encrypted = await crypt.encryptWithECDH(
{ data: "Secret from Alice" },
alice.privateKey,
await crypt.importECDHPublicKey(bob.publicKeyB64)
);
const decrypted = await crypt.decryptWithECDH(
encrypted,
bob.privateKey,
await crypt.importECDHPublicKey(alice.publicKeyB64)
);
Digital Signatures (ECDSA)
const { publicKey, privateKey, publicKeyB64 } = await crypt.generateSigningKeyPair("P-256");
const signature = await crypt.signText("I approve transaction #123", privateKey);
const valid = await crypt.verifyText("I approve transaction #123", signature, publicKey);
const { signatureB64 } = await crypt.signFile(file, privateKey);
const fileValid = await crypt.verifyFile(file, signatureB64, publicKey);
File Encryption with Progress
const { blob, filename } = await crypt.encryptFileWithProgress(file, publicKey, progress => {
console.log(`${Math.round(progress * 100)}%`);
});
JSON Web Encryption (JWE)
Create and decrypt standard JWE Compact Serialization tokens (RFC 7516) using RSA-OAEP-256 and A256GCM.
import { WebCryptAsym } from "webcrypt";
const crypt = new WebCryptAsym();
const keys = await crypt.generateKeyPair();
const payload = { userId: 123, role: "admin" };
const jweToken = await crypt.encryptJWE(payload, keys.publicKey, { kid: "my-key-id" });
const decrypted = await crypt.decryptJWE(jweToken, keys.privateKey);
HMAC
Message authentication codes using SHA-256, SHA-384, SHA-512, or SHA-3.
import { WebCrypt } from "webcrypt";
const wc = new WebCrypt();
const key = await wc.generateHmacKey("password");
const hmac = await wc.computeHmac("Important message", key);
const valid = await wc.verifyHmac("Important message", hmac, key);
const sha3Key = await wc.generateHmacKeySHA3("password");
const sha3Hmac = await wc.computeHmacSHA3("Important message", sha3Key);
const sha3Valid = await wc.verifyHmacSHA3("Important message", sha3Hmac, sha3Key);
Key Derivation
PBKDF2 (default)
const key = await crypt.deriveKeyPBKDF2("password", "salt");
SHA-3 KDF
const key = await crypt.deriveKeySHA3("password", 50000, "SHA3-256");
HKDF-SHA3
const masterSecret = new TextEncoder().encode("master-password");
const key = await crypt.deriveKeyHKDFSHA3(masterSecret, saltBytes, infoBytes, 256);
Key rotation and hierarchical keys
const rotatedKey = await crypt.rotateKeyNew("password", newSaltBytes, "PBKDF2");
const encKey = await crypt.deriveChildKeyHierarchical(parentKey, childSalt, "encryption");
const sigKey = await crypt.deriveChildKeyHierarchical(parentKey, childSalt, "signing");
Post-Quantum Cryptography
⚠️ STUB IMPLEMENTATION — WebCryptPQC currently uses SHA-3 hashing stubs, not real lattice-based cryptography. For production PQC, integrate liboqs-js directly.
WebCryptPQC provides a placeholder API for Kyber (key encapsulation) and Dilithium (digital signatures) that mirrors the real API surface. Build against it today, swap in real PQC when v0.6+ ships.
import { WebCryptPQC } from "webcrypt";
const pqc = new WebCryptPQC();
const kyberKeys = await pqc.generateKyberKeyPair("Kyber768");
const { ciphertext, sharedSecret } = await pqc.kyberEncapsulate(kyberKeys.publicKey, "Kyber768");
const recovered = await pqc.kyberDecapsulate(ciphertext, kyberKeys.privateKey, "Kyber768");
Full PQC documentation: docs/PQC.md — includes Kyber, Dilithium, hybrid encryption, security levels, and migration path.
API Reference
WebCrypt (Symmetric)
const wc = new WebCrypt();
wc.encryptText(text: string, password: string): Promise<string>
wc.decryptText(b64: string, password: string): Promise<string>
wc.encryptData(data: any, password: string): Promise<string>
wc.decryptData(b64: string, password: string): Promise<any>
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>
wc.generateHmacKey(password?: string, hash?: string): Promise<CryptoKey>
wc.computeHmac(data: string | ArrayBuffer, key: CryptoKey): Promise<string>
wc.verifyHmac(data: string | ArrayBuffer, hmac: string, key: CryptoKey): Promise<boolean>
wc.generateHmacKeySHA3(password?: string, hash?: string): Promise<CryptoKey>
wc.computeHmacSHA3(data: string | ArrayBuffer, key: CryptoKey): Promise<string>
wc.verifyHmacSHA3(data: string | ArrayBuffer, hmac: string, key: CryptoKey): Promise<boolean>
wc.generateRandomPassword(length?: number): string
wc.clearKeyCache(): void
wc.stopAutoCleanup(): void
WebCryptAsym (Asymmetric)
const crypt = new WebCryptAsym();
crypt.generateKeyPair(): Promise<CryptoKeyPair>
crypt.exportPublicKey(key: CryptoKey): Promise<string>
crypt.exportPrivateKey(key: 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.encryptData(data: any, publicKey: CryptoKey): Promise<string>
crypt.decryptData(b64: string, privateKey: CryptoKey): Promise<any>
crypt.encryptFile(file: File | Blob, publicKey: CryptoKey): Promise<{ blob, filename }>
crypt.decryptFile(file: File | Blob, privateKey: CryptoKey): Promise<{ blob, filename }>
crypt.encryptFileWithProgress(file, publicKey, onProgress?): Promise<{ blob, filename }>
crypt.decryptFileWithProgress(file, privateKey, onProgress?): Promise<{ blob, filename }>
crypt.generateECDHKeyPair(curve?: string): Promise<{ publicKey, privateKey, publicKeyB64 }>
crypt.importECDHPublicKey(b64: string, curve?: string): Promise<CryptoKey>
crypt.encryptWithECDH(data: any, privateKey, recipientPublicKey): Promise<string>
crypt.decryptWithECDH(b64: string, privateKey, senderPublicKey): Promise<any>
crypt.generateSigningKeyPair(curve?: string): Promise<{ publicKey, privateKey, publicKeyB64 }>
crypt.signText(text: string, privateKey: CryptoKey): Promise<string>
crypt.verifyText(text: string, sig: string, publicKey: CryptoKey): Promise<boolean>
crypt.signFile(file: File | Blob, privateKey: CryptoKey): Promise<{ signatureB64, blob }>
crypt.verifyFile(file: File | Blob, sig: string, publicKey: CryptoKey): Promise<boolean>
crypt.signTextWithAlgorithm(text, privateKey, algorithm?: 'ECDSA' | 'RSA-PSS'): Promise<string>
crypt.verifyTextWithAlgorithm(text, sig, publicKey, algorithm?): Promise<boolean>
crypt.encryptJWE(payload: any, publicKey: CryptoKey, headers?: object): Promise<string>
crypt.decryptJWE(jweToken: string, privateKey: CryptoKey): Promise<any>
crypt.signHMAC(data: string, key: CryptoKey, hash?: string): Promise<string>
crypt.verifyHMAC(data: string, sig: string, key: CryptoKey, hash?: string): Promise<boolean>
crypt.deriveKeyPBKDF2(password, salt, iterations?): Promise<CryptoKey>
crypt.deriveKeySHA3(password, iterations?, algorithm?): Promise<CryptoKey>
crypt.deriveKeyHKDFSHA3(secret, salt?, info?, keyLength?): Promise<CryptoKey>
crypt.rotateKeyNew(password, newSalt, method?): Promise<CryptoKey>
crypt.deriveChildKeyHierarchical(parentKey, childSalt, purpose?): Promise<CryptoKey>
crypt.secureKeyErase(key: Uint8Array): void
crypt.createEncryptTransform(publicKey: CryptoKey): Promise<TransformFunction>
crypt.createDecryptTransform(privateKey: CryptoKey): Promise<TransformFunction>
WebCryptPQC (Post-Quantum)
See docs/PQC.md for the full API reference.
Security
What's quantum-safe today
| Symmetric encryption | AES-256-GCM | ✅ Safe — 128-bit security even with Grover |
| Key derivation | PBKDF2 600k | ✅ Safe — no quantum speedup for password cracking |
| HMAC | SHA-256/3 | ✅ Safe — collision resistance holds |
| Asymmetric encryption | RSA-4096 | ⚠️ Vulnerable to Shor's algorithm (est. 2030–2040) |
| Signatures | ECDSA | ⚠️ Vulnerable to Shor's algorithm |
| PQC (Kyber/Dilithium) | Stubs | ❌ Not real PQC yet |
Security hardening (v0.6.1)
- PBKDF2 iterations: 600,000 (OWASP 2025+ compliant)
- Unique 128-bit salt per message/file
- Unique 96-bit IV per chunk/frame
- Key cache with 5-minute TTL, LRU eviction, and automatic
unref() timer cleanup
- Deterministic password-derived HMAC key derivation (
generateHmacKey / generateHmacKeySHA3)
- Non-blocking timing-attack resistant verification via
TimingSafeHelper
- High-speed 32KB chunked Base64 conversion
- Input validation / DoS protection (10 MB size limit)
- Error messages sanitized in production (
NODE_ENV=production)
- No keys ever leave your device
Known limitations
- PQC is a stub — Kyber/Dilithium use SHA-3 hashing, not real lattice-based crypto
- Argon2id is not supported by Web Crypto API — falls back to PBKDF2 with a warning
- JavaScript cannot guarantee secure memory erasure — key cleanup is best-effort
- WebRTC E2EE uses a fixed salt for key derivation from passwords
For vulnerability reporting, see SECURITY.md.
For security fix details, see SECURITY_FIXES.md.
Environment Support
Browser: Chrome 80+ · Edge 80+ · Firefox 90+ · Safari 15+
Runtime: Node.js 18+ · Deno · Cloudflare Workers · Electron
Frameworks: React · Next.js · Vue · Angular · Svelte
import { WebCrypt } from "webcrypt";
const { WebCrypt } = require("webcrypt");
License
MIT License — free for personal and commercial use.
© 2025-2026 PuterVision LLC
Legal & Usage Disclaimers
[!WARNING]
Limitation of Liability & Disclaimer of Warranty
WebCrypt is maintained by PuterVision LLC and provided "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL PUTERVISION LLC, ITS AFFILIATES, OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. USERS AND DEVELOPERS ARE SOLELY RESPONSIBLE FOR VERIFYING CRYPTOGRAPHIC PARAMETERS, CONDUCTING INDEPENDENT SECURITY AUDITS, AND DETERMINING SUITABILITY FOR PRODUCTION DEPLOYMENTS.
[!NOTE]
Data Privacy & Local Execution Guarantee
WebCrypt operations execute 100% locally in your browser or Node.js runtime using the native Web Crypto API. No private keys, passwords, plaintext data, or encrypted payloads are ever transmitted to external cloud servers.
[!IMPORTANT]
Third-Party AI Model & API Fees Disclaimer
This software is provided as free, open-source software under the MIT License. If integrated into developer AI agent frameworks (such as OpenAI, Anthropic Claude, Google Gemini, xAI Grok, or Ollama), any third-party API usage and billing fees remain the sole responsibility of the user. PuterVision LLC is not responsible for third-party API costs.
[!NOTE]
Trademark & Open Specification Attributions
Model Context Protocol (MCP) is an open specification created by Anthropic, PBC. Web Crypto API, W3C standards, and third-party IDE trademarks are property of their respective owners. PuterVision LLC is an independent open-source software developer.
[!NOTE]
Performance & Cost Savings Estimates
Encryption performance benchmarks, throughput speeds, and zero-dependency efficiency metrics reported in documentation are derived from standard browser and Node.js WebCrypto benchmarks and may vary based on hardware acceleration.