@brivora/crypto
v0.1.0
Published
Post-quantum cryptography in 3 lines of TypeScript. The Stripe for PQC.
Maintainers
Readme
@brivora/crypto
Post-quantum cryptography in 3 lines of TypeScript.
import { crypto } from '@brivora/crypto';
const keys = await crypto.createIdentity();
const signed = await crypto.sign(data, keys.privateKey);What is this?
@brivora/crypto is an opinionated, batteries-included post-quantum cryptography library for JavaScript/TypeScript. The "Stripe for PQC."
- Hybrid by default — Classical (Ed25519 + X25519) + Post-Quantum (ML-DSA-65 + ML-KEM-768). Both must be broken to compromise security.
- NIST FIPS compliant — ML-KEM-768 (FIPS 203), ML-DSA-65 (FIPS 204), finalized August 2024.
- Self-describing payloads — Every encrypted/signed payload includes version and algorithm metadata.
- Zero configuration — Sensible defaults. No crypto PhD required.
- Pure TypeScript — No native modules. No WASM. Works everywhere: Node.js, Deno, Bun, browsers.
- Zero telemetry — No analytics. No network calls. Pure local computation.
Install
npm install @brivora/crypto
# or
pnpm add @brivora/crypto
# or
yarn add @brivora/cryptoQuick Start
Create an Identity
import { crypto } from '@brivora/crypto';
const identity = await crypto.createIdentity();
// {
// publicKey: { classical: { signing, encryption }, pqc: { signing, encryption } },
// privateKey: { classical: { signing, encryption }, pqc: { signing, encryption } },
// fingerprint: 'a1b2c3d4...',
// algorithm: 'hybrid-pqc-v1',
// createdAt: '2026-02-13T...'
// }Encrypt & Decrypt
// Alice encrypts a message for Bob
const encrypted = await crypto.encrypt('secret message', bob.publicKey);
// Bob decrypts
const plaintext = await crypto.decrypt(encrypted, bob.privateKey);
// Uint8Array → use new TextDecoder().decode(plaintext) for stringsSign & Verify
// Sign data
const signed = await crypto.sign('important data', alice.privateKey);
// Verify signature
const { valid, data } = await crypto.verify(signed, alice.publicKey);
// valid: true, data: Uint8Array of original dataKey Rotation
const { newIdentity, migration } = await crypto.rotateKeys(oldIdentity);
// migration is a signed proof linking old key → new keyKey Derivation
const masterKey = crypto.randomBytes(32);
const encKey = crypto.deriveKey(masterKey, 'encryption');
const authKey = crypto.deriveKey(masterKey, 'authentication');
// Different contexts → different deterministic keysMigrate from Ed25519
const upgraded = await crypto.upgradeKey({
publicKey: existingEd25519PublicKey,
secretKey: existingEd25519SecretKey,
});
// upgraded.identity is now a hybrid PQC identity
// upgraded.migration is a signed proof of the upgradePQC-Only Mode
// Disable classical crypto (PQC-only)
const encrypted = await crypto.encrypt(data, pubKey, { hybrid: false });
const signed = await crypto.sign(data, privKey, { hybrid: false });API Reference
Identity
| Method | Description |
|--------|-------------|
| crypto.createIdentity(options?) | Create a new hybrid identity |
| crypto.exportKey(identity, format?) | Serialize identity for storage |
| crypto.importKey(serialized) | Restore identity from serialized form |
| crypto.exportPublicKey(identity) | Export only the public key (safe to share) |
| crypto.importPublicKey(encoded) | Import a shared public key |
Encryption
| Method | Description |
|--------|-------------|
| crypto.encrypt(data, recipientPublicKey, options?) | Hybrid encrypt (X25519 + ML-KEM-768 + AES-256-GCM) |
| crypto.decrypt(encrypted, privateKey) | Decrypt with your private key |
Signatures
| Method | Description |
|--------|-------------|
| crypto.sign(data, privateKey, options?) | Hybrid sign (Ed25519 + ML-DSA-65) |
| crypto.verify(signed, publicKey?) | Verify a signed payload |
Key Management
| Method | Description |
|--------|-------------|
| crypto.rotateKeys(oldIdentity, newIdentity?) | Rotate keys with migration proof |
| crypto.deriveKey(masterKey, context, length?) | HKDF-SHA256 key derivation |
Migration
| Method | Description |
|--------|-------------|
| crypto.upgradeKey(ed25519KeyPair) | Upgrade Ed25519 → hybrid PQC |
Utilities
| Method | Description |
|--------|-------------|
| crypto.hash(data, algorithm?) | SHA-256 (default), SHA-384/512, SHA3-256/512 |
| crypto.randomBytes(length) | CSPRNG random bytes |
| crypto.fingerprint(...keys) | SHA-256 fingerprint of public keys |
Algorithms
| Algorithm | Standard | Usage | |-----------|----------|-------| | ML-KEM-768 | FIPS 203 | Key encapsulation (encryption) | | ML-DSA-65 | FIPS 204 | Digital signatures | | X25519 | RFC 7748 | Classical key exchange (hybrid) | | Ed25519 | RFC 8032 | Classical signatures (hybrid) | | AES-256-GCM | FIPS 197 | Symmetric encryption | | HKDF-SHA256 | RFC 5869 | Key derivation | | SHA-256 | FIPS 180-4 | Hashing, fingerprinting |
How Hybrid Mode Works
Encryption (X25519 + ML-KEM-768)
- Generate ephemeral X25519 key pair
- Compute classical shared secret via X25519 ECDH
- Encapsulate PQC shared secret via ML-KEM-768
- Combine both secrets via HKDF-SHA256
- Encrypt plaintext with AES-256-GCM
An attacker must break both X25519 and ML-KEM-768 to recover the plaintext.
Signing (Ed25519 + ML-DSA-65)
- Sign data with Ed25519
- Sign data with ML-DSA-65
- Both signatures must verify for the payload to be valid
An attacker must forge both an Ed25519 and ML-DSA-65 signature.
Tree Shaking
Import only what you need:
// Only signing — no encryption code in your bundle
import { sign, verify, createIdentity } from '@brivora/crypto';Security
- Constant-time comparisons — All byte comparisons use constant-time algorithms to prevent timing side-channels.
- CSPRNG — All random bytes from
crypto.getRandomValues()(OS-level CSPRNG). - No network calls — Everything runs locally. Zero telemetry. Zero analytics.
- Audited primitives — Built on @noble/post-quantum (audited by Cure53).
See SECURITY.md for our security policy and responsible disclosure process.
Requirements
- Node.js 20+ (also works in Deno, Bun, and modern browsers)
- Web Crypto API (
globalThis.crypto.subtle)
Built On
- @noble/post-quantum — ML-KEM, ML-DSA (audited)
- @noble/hashes — SHA-2, SHA-3, HKDF
- @noble/curves — Ed25519, X25519
