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

@killd21/kpqc

v0.2.0

Published

KpqC (Korean Post-Quantum Cryptography) for Node.js and browsers — AIMer & HAETAE signatures and the NTRU+ & SMAUG-T KEMs, compiled to WebAssembly

Readme

@killd21/kpqc

KpqC (Korean Post-Quantum Cryptography) for JavaScript & TypeScript — Node.js and the browser.

npm license types

One package, four KpqC algorithms — the official reference implementations compiled to WebAssembly, wrapped in small, fully-typed APIs:

| Algorithm | Kind | Basis | Import | | ----------- | ------------------- | ---------------------------- | ------------------------- | | AIMer | Digital signature | Symmetric / MPC-in-the-head | @killd21/kpqc/aimer | | HAETAE | Digital signature | Lattice (Module-LWE/SIS) | @killd21/kpqc/haetae | | NTRU+ | Key encapsulation | Lattice (NTRU) | @killd21/kpqc/ntruplus | | SMAUG-T | Key encapsulation | Lattice (Module-LWE/LWR) | @killd21/kpqc/smaugt |

  • 🔐 Post-quantum secure — primitives that resist attacks by quantum computers
  • 🌐 Runs everywhere — Node.js, Deno, Bun, and browsers from portable .wasm
  • 📦 Zero dependencies — no native build step, nothing to compile on install
  • 💤 Lazy loading — each algorithm's wasm loads on first use; import what you need
  • 🔎 Verified — matches all 1600 official Known Answer Test vectors, byte-for-byte
  • 🟦 First-class TypeScript — ESM + CommonJS, types included

Install

npm install @killd21/kpqc
pnpm add @killd21/kpqc    # or: yarn add @killd21/kpqc / bun add @killd21/kpqc

Quick start

Signatures (HAETAE, AIMer)

import { haetae2 } from "@killd21/kpqc/haetae";
// AIMer works identically: import { aimer128f } from "@killd21/kpqc/aimer";

const message = new TextEncoder().encode("hello post-quantum world");

const { publicKey, secretKey } = await haetae2.keygen();
const signature = await haetae2.sign(message, secretKey);
const isValid = await haetae2.verify(message, signature, publicKey);
console.log(isValid); // true

Key encapsulation (NTRU+, SMAUG-T)

import { ntruplus768 } from "@killd21/kpqc/ntruplus";
// SMAUG-T works identically: import { smaugt128 } from "@killd21/kpqc/smaugt";

// Recipient: generate a keypair, publish the public key.
const { publicKey, secretKey } = await ntruplus768.keygen();

// Sender: encapsulate a fresh 32-byte shared secret against the public key.
const { ciphertext, sharedSecret } = await ntruplus768.encapsulate(publicKey);

// Recipient: recover the same shared secret (e.g. to key AES-256-GCM).
const recovered = await ntruplus768.decapsulate(ciphertext, secretKey);
// recovered ≡ sharedSecret

Everything is a plain Uint8Array, so you can store or transmit values however you like (base64, hex, files, …). All methods are async: each algorithm's wasm module loads once, lazily, on first use.

You can also import everything from the root (parameter-set lists get algorithm-prefixed names there to avoid clashes):

import {
  aimer128f, haetae2, ntruplus768, smaugt128,
  AIMER_PARAMETER_SETS, HAETAE_PARAMETER_SETS,
  NTRUPLUS_PARAMETER_SETS, SMAUGT_PARAMETER_SETS,
} from "@killd21/kpqc";

Parameter sets

AIMer — @killd21/kpqc/aimer

f = faster signing / larger signatures, s = smaller signatures / slower.

| Set | Security | Public key | Secret key | Signature | | ----------- | -------- | ---------: | ---------: | --------: | | aimer128f | 128-bit | 32 B | 48 B | 5.9 KB | | aimer128s | 128-bit | 32 B | 48 B | 4.2 KB | | aimer192f | 192-bit | 48 B | 72 B | 13.1 KB | | aimer192s | 192-bit | 48 B | 72 B | 9.1 KB | | aimer256f | 256-bit | 64 B | 96 B | 25.1 KB | | aimer256s | 256-bit | 64 B | 96 B | 17.1 KB |

HAETAE — @killd21/kpqc/haetae

| Set | Security | Public key | Secret key | Signature | | --------- | -------- | ---------: | ---------: | --------: | | haetae2 | Cat. 2 | 992 B | 1408 B | 1474 B | | haetae3 | Cat. 3 | 1472 B | 2112 B | 2349 B | | haetae5 | Cat. 5 | 2080 B | 2752 B | 2948 B |

NTRU+ — @killd21/kpqc/ntruplus

| Set | Security | Public key | Secret key | Ciphertext | Shared secret | | -------------- | -------- | ---------: | ---------: | ---------: | ------------: | | ntruplus768 | Cat. 1 | 1152 B | 2336 B | 1152 B | 32 B | | ntruplus864 | Cat. 3 | 1296 B | 2624 B | 1296 B | 32 B | | ntruplus1152 | Cat. 5 | 1728 B | 3488 B | 1728 B | 32 B |

SMAUG-T — @killd21/kpqc/smaugt

timer (TiMER) is SMAUG-T's IoT-oriented security-level-1 variant with the smallest ciphertext.

| Set | Security | Public key | Secret key | Ciphertext | Shared secret | | ----------- | -------- | ---------: | ---------: | ---------: | ------------: | | smaugt128 | Cat. 1 | 672 B | 832 B | 672 B | 32 B | | smaugt192 | Cat. 3 | 1088 B | 1312 B | 992 B | 32 B | | smaugt256 | Cat. 5 | 1440 B | 1728 B | 1376 B | 32 B | | timer | Cat. 1 | 672 B | 832 B | 608 B | 32 B |

Each subpath also exports PARAMETER_SETS and a lookup map (aimer / haetae / ntruplus / smaugt) for dynamic selection:

import { haetae, PARAMETER_SETS } from "@killd21/kpqc/haetae";

PARAMETER_SETS;               // ["haetae2", "haetae3", "haetae5"]
const scheme = haetae["haetae3"];

Context strings (signatures)

Bind a signature to a domain / protocol with an optional context (≤ 255 bytes). Verification must use the same context or it fails — handy for separating signatures across features so one can't be replayed in another.

const ctx = new TextEncoder().encode("login-token-v1");

const sig = await haetae2.sign(message, secretKey, { context: ctx });

await haetae2.verify(message, sig, publicKey, { context: ctx }); // true
await haetae2.verify(message, sig, publicKey);                   // false (no context)

API

Signature schemes (AIMer, HAETAE) implement SignatureScheme; the KEMs (NTRU+, SMAUG-T) implement KemScheme:

interface SignatureScheme {
  readonly name: string;
  readonly publicKeyBytes: number;
  readonly secretKeyBytes: number;
  readonly signatureBytes: number;

  keygen(): Promise<{ publicKey: Uint8Array; secretKey: Uint8Array }>;
  sign(message: Uint8Array, secretKey: Uint8Array,
       options?: { context?: Uint8Array }): Promise<Uint8Array>; // detached
  verify(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array,
         options?: { context?: Uint8Array }): Promise<boolean>;
}

interface KemScheme {
  readonly name: string;
  readonly publicKeyBytes: number;
  readonly secretKeyBytes: number;
  readonly ciphertextBytes: number;
  readonly sharedSecretBytes: number; // 32

  keygen(): Promise<{ publicKey: Uint8Array; secretKey: Uint8Array }>;
  encapsulate(publicKey: Uint8Array):
    Promise<{ ciphertext: Uint8Array; sharedSecret: Uint8Array }>;
  decapsulate(ciphertext: Uint8Array, secretKey: Uint8Array):
    Promise<Uint8Array>;
}

On an invalid ciphertext, NTRU+ decapsulate throws; SMAUG-T uses implicit rejection and instead returns a pseudo-random secret that will not match the sender's — the mismatch is only detected when the secrets are used.

Runtime support

| Environment | Supported | | ------------------ | -------------------- | | Node.js ≥ 18 | ✅ | | Browsers (ESM) | ✅ (WebAssembly) | | Deno / Bun | ✅ | | Web Workers | ✅ |

The secure random source is the Web Crypto API, falling back to Node's crypto module. In bundlers (Vite, webpack, esbuild, …) each .wasm is loaded as an asset next to the module — no special configuration is typically required.

Migrating from @killd21/aimer / @killd21/haetae

The subpath entries are drop-in replacements — only the import specifier changes:

- import { aimer128f } from "@killd21/aimer";
+ import { aimer128f } from "@killd21/kpqc/aimer";

- import { haetae2 } from "@killd21/haetae";
+ import { haetae2 } from "@killd21/kpqc/haetae";

Security notice

This package wraps the official reference implementations. They are validated against the official Known Answer Tests, but:

  • they have not undergone an independent security audit, and
  • WebAssembly / JavaScript cannot guarantee constant-time execution.

Evaluate carefully before using this to protect high-value secrets in production.

License

MIT. Distributes and builds upon the AIMer (© Samsung SDS), HAETAE (CryptoLab Inc. and collaborators), NTRU+ (© NTRU+ TEAM) and SMAUG-T (© Team SMAUG-T) reference implementations, retained under vendor/ in the source repository.