kpqc
v0.1.3
Published
JavaScript APIs for AIMer, HAETAE, NTRU+, and SMAUG-T.
Maintainers
Readme
KpqC
KpqC provides typed, asynchronous JavaScript APIs for AIMer, HAETAE, NTRU+, and SMAUG-T.
Runtime support
- Node.js 18 or newer
- A browser that provides
globalThis.crypto.getRandomValues - ESM or CommonJS
Install
npm install kpqcAvailable schemes
| Algorithm | Type | Exports |
| --- | --- | --- |
| AIMer | Signature | aimer128f, aimer128s, aimer192f, aimer192s, aimer256f, aimer256s |
| HAETAE | Signature | haetae2, haetae3, haetae5 |
| NTRU+ | Key encapsulation | ntruplus768, ntruplus864, ntruplus1152 |
| SMAUG‑T | Key encapsulation | smaugt128, smaugt192, smaugt256, timer |
Importing from an algorithm subpath keeps the entry point focused:
import { aimer128f } from "kpqc/aimer";
const payload = new TextEncoder().encode("release-manifest:v3");
const keys = await aimer128f.generateKeyPair();
const proof = await aimer128f.sign(payload, keys.secretKey);
if (!(await aimer128f.verify(payload, proof, keys.publicKey))) {
throw new Error("Signature verification failed");
}Signature contexts
AIMer and HAETAE accept an optional context. A context separates signatures created for different application purposes and may contain up to 255 bytes.
import { haetae3 } from "kpqc/haetae";
const payload = new TextEncoder().encode("account=42");
const context = new TextEncoder().encode("audit-record");
const keys = await haetae3.generateKeyPair();
const signature = await haetae3.sign(payload, keys.secretKey, { context });
const valid = await haetae3.verify(
payload,
signature,
keys.publicKey,
{ context },
);
console.log(valid); // trueVerification fails when the supplied context does not match the one used for signing.
Key encapsulation
A KEM creates a shared secret for a sender and a recipient. The public key may be distributed; the secret key and resulting shared secret must remain private.
import { smaugt192 } from "kpqc/smaugt";
const recipient = await smaugt192.generateKeyPair();
const outbound = await smaugt192.encapsulate(recipient.publicKey);
// Send `outbound.ciphertext` to the recipient.
const inboundSecret = await smaugt192.decapsulate(
outbound.ciphertext,
recipient.secretKey,
);
console.log(
inboundSecret.every((byte, index) => byte === outbound.sharedSecret[index]),
); // trueImports
Each family has a dedicated entry point:
import { aimer256s } from "kpqc/aimer";
import { haetae5 } from "kpqc/haetae";
import { ntruplus1152 } from "kpqc/ntruplus";
import { timer } from "kpqc/smaugt";All named algorithms are also exported from the package root:
import {
aimer192f,
ntruplus864,
type KeyEncapsulationAlgorithm,
type SignatureAlgorithm,
} from "kpqc";
const signer: SignatureAlgorithm = aimer192f;
const keyExchange: KeyEncapsulationAlgorithm = ntruplus864;CommonJS consumers use the matching paths:
const { smaugt128 } = require("kpqc/smaugt");
smaugt128.generateKeyPair().then(({ publicKey }) => {
console.log(publicKey.byteLength);
});Data and failures
Inputs and outputs are Uint8Array instances. Each algorithm exposes an id
and a frozen sizes object.
Parameter sizes
All sizes are in bytes.
Signatures
| Algorithm | Public key | Secret key | Signature |
| --- | ---: | ---: | ---: |
| aimer128f | 32 | 48 | 5,888 |
| aimer128s | 32 | 48 | 4,160 |
| aimer192f | 48 | 72 | 13,056 |
| aimer192s | 48 | 72 | 9,120 |
| aimer256f | 64 | 96 | 25,120 |
| aimer256s | 64 | 96 | 17,056 |
| haetae2 | 992 | 1,408 | 1,474 |
| haetae3 | 1,472 | 2,112 | 2,349 |
| haetae5 | 2,080 | 2,752 | 2,948 |
Key encapsulation
| Algorithm | Public key | Secret key | Ciphertext | Shared secret |
| --- | ---: | ---: | ---: | ---: |
| ntruplus768 | 1,152 | 2,336 | 1,152 | 32 |
| ntruplus864 | 1,296 | 2,624 | 1,296 | 32 |
| ntruplus1152 | 1,728 | 3,488 | 1,728 | 32 |
| smaugt128 | 672 | 832 | 672 | 32 |
| smaugt192 | 1,088 | 1,312 | 992 | 32 |
| smaugt256 | 1,440 | 1,728 | 1,376 | 32 |
| timer | 672 | 832 | 608 | 32 |
Methods reject values of the wrong type or size. Signature verification returns
false for an invalid signature. NTRU+ rejects an invalid ciphertext.
SMAUG-T performs implicit rejection and returns a replacement secret instead;
that value will not equal the sender's shared secret.
Distribution
The published package includes ESM, CommonJS, and TypeScript declarations. Each algorithm family is initialized on first use. The package has no runtime dependencies or install script.
Security
The JavaScript cores are generated from the upstream algorithm implementations. This package has not received an independent security audit, and JavaScript engines do not provide a constant-time execution guarantee. Assess those constraints before using it with sensitive production keys.
Third-party licenses and attributions are listed in THIRD_PARTY_NOTICES.md.
