
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
@quantakrypto/sieve
Advanced tools
Sieve — conformance battery for ML-KEM / ML-DSA implementations. Drives any implementation over a simple stdin/stdout JSON protocol. Zero runtime dependencies.
Conformance battery for ML-KEM (FIPS 203), ML-DSA (FIPS 204), and SLH-DSA (FIPS 205) implementations. Sieve drives any implementation through a small stdin/stdout JSON protocol and reports where it deviates from the standard.
Zero runtime dependencies — Node built-ins only. Node ≥ 20, ESM.
Sieve tests other people's implementations. It deliberately does not implement ML-KEM/ML-DSA/SLH-DSA, and it never fabricates cryptographic Known-Answer-Test values. Its power comes from two honest sources:
vectors/README.md.The only hard-coded cryptographic constants in Sieve are the public, standardized parameter sizes (e.g. ML-KEM-768 public key = 1184 bytes; SLH-DSA-SHA2-128f signature = 17088 bytes, FIPS 205 Table 2). Those are not secrets and not KAT values.
Within the quantakrypto-tools monorepo it builds with the workspace:
npm install
npm run build # tsc -b across the workspace
The package exposes a sieve bin and a programmatic API.
Sieve spawns your implementation (the SUT) as a child process and exchanges
newline-delimited JSON: one request per line in, one response per line out, all
byte fields base64. Full spec: PROTOCOL.md.
sieve ──request (NDJSON)──▶ your SUT process
◀─response (NDJSON)── (ML-KEM / ML-DSA)
examples/mock-sut.ts is a non-cryptographic reference SUT used only to
exercise the harness. It satisfies the protocol with deterministic, correctly
sized fake bytes — it provides no security; never use it for anything real.
# run the full ML-KEM-768 battery against the mock
node --import tsx src/cli.ts \
--impl "node --import tsx examples/mock-sut.ts" \
--param ml-kem-768 --iterations 32
After npm run build the bin runs directly:
sieve --impl "node ./my-impl.js" --param ml-kem-768 --iterations 64 --json
Write a thin adapter that speaks the protocol and calls a vetted library. The adapter is the SUT; Sieve never bundles a crypto implementation.
Example sketch using a Node binding (e.g. a WASM/native ML-KEM library):
// my-impl.js — adapter only; the crypto comes from a vetted dependency.
import { createInterface } from "node:readline";
import { mlKem768 } from "some-vetted-pqc-lib"; // <- you choose & audit this
const b64 = (u8) => Buffer.from(u8).toString("base64");
const un = (s) => new Uint8Array(Buffer.from(s, "base64"));
createInterface({ input: process.stdin }).on("line", (line) => {
if (!line.trim()) return;
const r = JSON.parse(line);
const out = (o) => process.stdout.write(JSON.stringify({ id: r.id, ok: true, ...o }) + "\n");
const err = (code, message) => process.stdout.write(JSON.stringify({ id: r.id, ok: false, code, message }) + "\n");
try {
if (r.op === "keygen") { const { publicKey, secretKey } = mlKem768.keygen(); return out({ pk: b64(publicKey), sk: b64(secretKey) }); }
if (r.op === "encaps") { const { cipherText, sharedSecret } = mlKem768.encapsulate(un(r.pk)); return out({ ct: b64(cipherText), ss: b64(sharedSecret) }); }
if (r.op === "decaps") { const ss = mlKem768.decapsulate(un(r.ct), un(r.sk)); return out({ ss: b64(ss) }); }
return err("unsupported", `op ${r.op}`);
} catch (e) { err("error", String(e?.message ?? e)); }
});
Then:
sieve --impl "node ./my-impl.js" --param ml-kem-768
For exact-value KAT, also pass --vectors <dir> with official NIST files.
sieve --impl "<command...>" --param <set> [options]
--impl "<cmd...>" command Sieve spawns and drives (quote the whole thing)
--param <set> ml-kem-512 | ml-kem-768 | ml-kem-1024 |
ml-dsa-44 | ml-dsa-65 | ml-dsa-87 |
slh-dsa-{sha2,shake}-{128,192,256}{s,f} (12 sets)
--iterations <N> randomized iterations (default 32)
--vectors <dir> official NIST ACVP vector dir for KAT (else SKIPPED)
--timing include the advisory decaps-timing probe
--only <a,b,...> run only these categories
--timeout-ms <N> per-request timeout (default 10000)
--pipeline-depth <N> max concurrent in-flight requests for independent-iteration
categories (default 16; 1 = strictly serial)
--inherit-env pass the FULL parent env to the SUT (DANGEROUS; see below)
--json machine-readable report
exit: 0 = PASS, 1 = FAIL, 2 = usage error
import { runSieve, formatHuman } from "@quantakrypto/sieve";
const report = await runSieve({
command: ["node", "./my-impl.js"],
param: "ml-kem-768",
iterations: 64,
// vectorsDir: "./vectors/ml-kem-768",
});
console.log(formatHuman(report));
process.exit(report.overall === "PASS" ? 0 : 1);
| Category | Family | Bug class | What it checks |
|---|---|---|---|
correctness | ML-KEM | — | keygen→encaps(pk)→decaps(sk,ct) ⇒ ss_encaps === ss_decaps over N iterations. |
determinism | ML-KEM | — | repeated decaps(sk, ct) returns identical ss. |
implicit-rejection | ML-KEM | AF-02 | decaps of a corrupted ct returns a valid-length ss with no error/crash, is deterministic, and differs from the honest ss. No exact-value assertions. |
sizes | ML-KEM | AF-05 | emitted pk/sk/ct/ss lengths match the set; wrong-length pk/ct/sk are rejected with a defined error; FIPS 203 §7.2 ek modulus-range check — a correctly-sized-but-out-of-range encapsulation key (t̂ coefficient ≥ q) is rejected. |
robustness | ML-KEM | AF-05 | empty / non-base64 / oversized inputs ⇒ defined error, never crash/silent-accept. |
dsa | ML-DSA | AF-05 | sign→verify ⇒ true; tampered message/signature ⇒ false; wrong message ⇒ false; pk/sk/sig sizes for ml-dsa-44/65/87; wrong-length verify input rejected; deterministic-vs-hedged signing advisory (surfaced, never fails). |
slh-dsa | SLH-DSA | AF-05 | same self-consistency surface as dsa for the 12 FIPS 205 parameter sets: sign→verify, tamper checks, pk/sk/sig sizes, signing-mode advisory, wrong-length verify rejection. |
kat | all | — | SKIPPED unless --vectors <dir> of official ACVP files is given; then checks decaps/seeded-keygen/seeded-encaps/sigVer against those exact expected values. Never fabricated. |
timing | ML-KEM | (AF-02) | ADVISORY only. Coarse decaps timing for valid vs. invalid ct. Reports a signal but never changes the verdict — cross-process timing is noisy and not proof of (non-)constant-time behavior. |
The overall verdict is FAIL if any non-advisory category fails, else
PASS. A skip (e.g. KAT with no vectors) never causes a failure.
Sieve ships no test vectors. Hard-coding "expected" ciphertext or shared-secret bytes would be both a maintenance hazard and a correctness lie if ever mistyped. Instead:
--vectors. The loader (src/vectors.ts) parses the standard ACVP JSON.See vectors/README.md for sources and the file format.
ek).The SUT is code you point Sieve at — frequently a third-party or candidate
implementation. Sieve drives it with the harness's own OS privileges, so by
default Sieve spawns it with a scrubbed, minimal environment: only a small
allow-list (PATH, HOME, LANG, locale/temp vars, plus Windows spawn
essentials) is copied from the parent process. Harness secrets in the parent env
— CI tokens, NPM_TOKEN, cloud credentials, signing keys — are not
forwarded, so an untrusted SUT cannot read them.
env (programmatic) — these are
layered on top of the scrubbed base.envAllowlist (copy named parent vars).--inherit-env / inheritEnv: true restores the full parent environment.
Use it only for trusted, local implementations you control.Treat any non-trusted SUT as you would untrusted code generally — sandbox it (container / seccomp) if its provenance is unknown.
The SUT protocol is id-correlated (each response echoes its request id), so
independent requests can be in flight at once. Categories whose iterations are
mutually independent (correctness, determinism, dsa, slh-dsa) pipeline up
to --pipeline-depth (default 16) requests against the single SUT process; the
results are reassembled in request order so the verdict is identical to a serial
run. Dependent steps within one iteration (e.g. keygen→encaps→decaps for a single
key) and order-sensitive categories (timing) stay strictly serial. Set
--pipeline-depth 1 to force the legacy serial behavior.
Sieve drives the three stateless NIST PQC schemes: ML-KEM (FIPS 203), ML-DSA
(FIPS 204), and SLH-DSA (FIPS 205). The 12 standardized SLH-DSA parameter sets
(slh-dsa-{sha2,shake}-{128,192,256}{s,f}) are supported with size and
self-consistency categories; their public byte sizes come from FIPS 205 Table 2.
Out of scope: SP 800-208 stateful hash signatures (LMS / XMSS / HSS). These are deliberately not supported. Their security depends on one-time use of each leaf key — the signer must persist and advance secret state across signing operations, and reusing state catastrophically breaks the scheme. A stateless, request/response conformance harness like Sieve cannot model or verify the state-management guarantees that dominate SP 800-208 correctness; doing so safely requires a different, state-aware harness with durable per-key state tracking. LMS/XMSS are also intended for narrow uses (firmware/boot signing), not the general PQC migration Sieve targets.
Apache-2.0.
Questions, commercial support, or post-quantum readiness training for your team — visit quantakrypto.com or email hello@quantakrypto.com.
FAQs
Sieve — conformance battery for ML-KEM / ML-DSA implementations. Drives any implementation over a simple stdin/stdout JSON protocol. Zero runtime dependencies.
The npm package @quantakrypto/sieve receives a total of 207 weekly downloads. As such, @quantakrypto/sieve popularity was classified as not popular.
We found that @quantakrypto/sieve demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.