An ML-KEM (NIST FIPS 203) and CRYSTALS-KYBER implementation written in TypeScript.
This module is based on ntontutoveanu/crystals-kyber-javascript, but includes the following improvements:
deno bench to see the benchmark results.This repository has the following packages:
| package | registry | description |
|---|---|---|
| crystals-kyber-js | v1.x implements CRYSTALS-KYBER, and v2.x- implements ML-KEM (NIST FIPS 203). crystals-kyber-js may become deprecated in the near future. Instead, we recommend switching to the following mlkem or @dajiaji/mlkem. |
|
| mlkem | Implements only ML-KEM (NIST FIPS 203). It is an alias for the above crystals-kyber-js starting from v2 onwards. We recommend using this package going forward. |
|
| @dajiaji/mlkem | Implements only ML-KEM (NIST FIPS 203). It is an ML-KEM package for jsr.io. The above mlkem is an npm package of @dajiaji/mlkem, which has been converted using @deno/dnt. |
For Node.js, you can install mlkem or crystals-kyber-js via npm, yarn or
pnpm:
# RECOMMENTED using `mlkem`
npm install mlkem
# `crystals-kyber-js` is still available for use, but it may become deprecated in the near future.
npm install crystals-kyber-js
Then, you can use it as follows:
import { createMlKem768 } from "mlkem"; // or from "crystals-kyber-js"
async function doMlKem() {
// Create a pre-initialized instance (async, only needed once).
// createMlKem512 and createMlKem1024 are also available.
const recipient = await createMlKem768();
// A recipient generates a key pair.
const [pkR, skR] = recipient.generateKeyPair();
//// Deterministic key generation is also supported
// const seed = new Uint8Array(64);
// globalThis.crypto.getRandomValues(seed); // node >= 19
// const [pkR, skR] = recipient.deriveKeyPair(seed);
// A sender generates a ciphertext and a shared secret with pkR.
const sender = await createMlKem768();
const [ct, ssS] = sender.encap(pkR);
// The recipient decapsulates the ciphertext and generates the same shared secret with skR.
const ssR = recipient.decap(ct, skR);
// ssS === ssR
return;
}
try {
doMlKem();
} catch (err: unknown) {
console.log("failed:", (err as Error).message);
}
# Using npm:
npm install mlkem # or crystals-kyber-js
yarn add mlkem # or crystals-kyber-js
pnpm install mlkem # or crystals-kyber-js
# Using jsr:
npx jsr add @dajiaji/mlkem
yarn dlx jsr add @dajiaji/mlkem
pnpm dlx jsr add @dajiaji/mlkem
Starting from version 2.0.0, @dajiaji/mlkem is available from the
jsr.io. From this version onwards, please use JSR import
instead of HTTPS import in Deno.
JSR import (>=2.0.0):
Add @dajiaji/mlkem package using the commands below:
deno add @dajiaji/mlkem
Then, you can use the module from code like this:
import {
createMlKem1024,
createMlKem512,
createMlKem768,
} from "@dajiaji/mlkem";
HTTPS import (deprecated):
import {
Kyber1024,
Kyber512,
Kyber768,
} from "https://deno.land/x/crystals_kyber@<SEMVER>/mod.ts";
# Using npm:
npm install mlkem # or crystals-kyber-js
yarn add mlkem # or crystals-kyber-js
pnpm install mlkem # or crystals-kyber-js
# Using jsr:
npx jsr add @dajiaji/mlkem
yarn dlx jsr add @dajiaji/mlkem
pnpm dlx jsr add @dajiaji/mlkem
import {
createMlKem1024,
createMlKem512,
createMlKem768,
} from "@dajiaji/mlkem";
# Using npm:
npm install mlkem # or crystals-kyber-js
yarn add mlkem # or crystals-kyber-js
pnpm install mlkem # or crystals-kyber-js
# Using jsr:
bunx jsr add @dajiaji/bhttp
import {
createMlKem1024,
createMlKem512,
createMlKem768,
} from "@dajiaji/mlkem";
Followings are how to use this module with typical CDNs. Other CDNs can be used as well.
<script type="module">
// Using esm.sh:
import {
createMlKem1024,
createMlKem512,
createMlKem768,
} from "https://esm.sh/mlkem@<SEMVER>";
// Using unpkg.com:
// import { createMlKem768 } from "https://unpkg.com/mlkem@SEMVER";
// ...
</script>
This section shows some typical usage examples.
import { createMlKem768 } from "mlkem";
// const { createMlKem768 } = require("mlkem");
async function doMlKem() {
const recipient = await createMlKem768();
const [pkR, skR] = recipient.generateKeyPair();
const sender = await createMlKem768();
const [ct, ssS] = sender.encap(pkR);
const ssR = recipient.decap(ct, skR);
// ssS === ssR
return;
}
try {
doMlKem();
} catch (err) {
console.log("failed: ", err.message);
}
import { createMlKem512 } from "@dajiaji/mlkem";
async function doMlKem() {
const recipient = await createMlKem512();
const [pkR, skR] = recipient.generateKeyPair();
const sender = await createMlKem512();
const [ct, ssS] = sender.encap(pkR);
const ssR = recipient.decap(ct, skR);
// ssS === ssR
return;
}
try {
doMlKem();
} catch (err: unknown) {
console.log("failed:", (err as Error).message);
}
<html>
<head></head>
<body>
<script type="module">
import { createMlKem1024 } from "https://esm.sh/mlkem";
globalThis.doMlKem = async () => {
try {
const recipient = await createMlKem1024();
const [pkR, skR] = recipient.generateKeyPair();
const sender = await createMlKem1024();
const [ct, ssS] = sender.encap(pkR);
const ssR = recipient.decap(ct, skR);
// ssS === ssR
return;
} catch (err) {
alert("failed: ", err.message);
}
};
</script>
<button type="button" onclick="doMlKem()">do ML-KEM</button>
</body>
</html>
MlKem512, MlKem768, MlKem1024)The class-based async API (new MlKem768()) is deprecated and will be removed
in a future release. Please migrate to the createMlKem* functions shown above.
// DEPRECATED - avoid using this pattern
import { MlKem768 } from "mlkem";
const recipient = new MlKem768();
const [pkR, skR] = await recipient.generateKeyPair();
const [ct, ssS] = await recipient.encap(pkR);
const ssR = await recipient.decap(ct, skR);
// RECOMMENDED - use createMlKem* instead
import { createMlKem768 } from "mlkem";
const recipient = await createMlKem768();
const [pkR, skR] = recipient.generateKeyPair(); // sync
const [ct, ssS] = recipient.encap(pkR); // sync
const ssR = recipient.decap(ct, skR); // sync
We welcome all kind of contributions, filing issues, suggesting new features or sending PRs.
$ claude mcp add crystals-kyber-js \
-- python -m otcore.mcp_server <graph>