Skip to main content

ML-KEM

Module-lattice KEMs built from mlkem-native (C) via Emscripten.

Node.js

import { loadMlKem1024, loadMlKem768, loadMlKem512 } from "quantum-resistant-rustykey";

async function main() {
const mlkem = await loadMlKem1024(); // or 768 / 512

const keypair = mlkem.keypair();
const publicKey = mlkem.buffer_to_string(keypair.get("public_key"));
const privateKey = mlkem.buffer_to_string(keypair.get("private_key"));

const message = "Rusty keys, the rustier the better!";
const encrypt = mlkem.encrypt(keypair.get("public_key"));
const sharedSecret = encrypt.get("secret");
const encryptedMessage = await mlkem.encryptMessage(message, sharedSecret);

const decryptedSharedSecret = mlkem.decrypt(
encrypt.get("cyphertext"),
keypair.get("private_key"),
);
const decryptedMessage = await mlkem.decryptMessage(
encryptedMessage,
decryptedSharedSecret,
);
console.log(decryptedMessage);
}

main();

Browser (Vite)

import { loadMlKem768 } from "quantum-resistant-rustykey";

const output = document.querySelector("#output");

async function run() {
const kem = await loadMlKem768();
const kp = kem.keypair();

const enc = kem.encrypt(kp.get("public_key"));
const sharedSecretA = await enc.get("secret");
const sharedSecretB = await kem.decrypt(
enc.get("cyphertext"),
kp.get("private_key"),
);

const encrypted = await kem.encryptMessage("hello from browser", sharedSecretA);
const decrypted = await kem.decryptMessage(encrypted, sharedSecretB);
output.textContent = decrypted;
}

run().catch((err) => {
console.error(err);
output.textContent = "failed";
});

Source path

ML-KEM C comes from mlkem-native, compiled under wasm/, wrapped in mlkem-src/, then bundled into src/vendor/mlkem*.js. See Building from source.