@quantakrypto/sieve 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +227 -0
- package/dist/categories/correctness.d.ts +13 -0
- package/dist/categories/correctness.d.ts.map +1 -0
- package/dist/categories/correctness.js +78 -0
- package/dist/categories/correctness.js.map +1 -0
- package/dist/categories/determinism.d.ts +12 -0
- package/dist/categories/determinism.d.ts.map +1 -0
- package/dist/categories/determinism.js +78 -0
- package/dist/categories/determinism.js.map +1 -0
- package/dist/categories/dsa.d.ts +17 -0
- package/dist/categories/dsa.d.ts.map +1 -0
- package/dist/categories/dsa.js +17 -0
- package/dist/categories/dsa.js.map +1 -0
- package/dist/categories/helpers.d.ts +50 -0
- package/dist/categories/helpers.d.ts.map +1 -0
- package/dist/categories/helpers.js +127 -0
- package/dist/categories/helpers.js.map +1 -0
- package/dist/categories/implicit-rejection.d.ts +29 -0
- package/dist/categories/implicit-rejection.d.ts.map +1 -0
- package/dist/categories/implicit-rejection.js +126 -0
- package/dist/categories/implicit-rejection.js.map +1 -0
- package/dist/categories/index.d.ts +30 -0
- package/dist/categories/index.d.ts.map +1 -0
- package/dist/categories/index.js +33 -0
- package/dist/categories/index.js.map +1 -0
- package/dist/categories/kat.d.ts +21 -0
- package/dist/categories/kat.d.ts.map +1 -0
- package/dist/categories/kat.js +173 -0
- package/dist/categories/kat.js.map +1 -0
- package/dist/categories/robustness.d.ts +15 -0
- package/dist/categories/robustness.d.ts.map +1 -0
- package/dist/categories/robustness.js +98 -0
- package/dist/categories/robustness.js.map +1 -0
- package/dist/categories/signature.d.ts +29 -0
- package/dist/categories/signature.d.ts.map +1 -0
- package/dist/categories/signature.js +259 -0
- package/dist/categories/signature.js.map +1 -0
- package/dist/categories/sizes.d.ts +23 -0
- package/dist/categories/sizes.d.ts.map +1 -0
- package/dist/categories/sizes.js +150 -0
- package/dist/categories/sizes.js.map +1 -0
- package/dist/categories/slh-dsa.d.ts +23 -0
- package/dist/categories/slh-dsa.d.ts.map +1 -0
- package/dist/categories/slh-dsa.js +23 -0
- package/dist/categories/slh-dsa.js.map +1 -0
- package/dist/categories/timing.d.ts +16 -0
- package/dist/categories/timing.d.ts.map +1 -0
- package/dist/categories/timing.js +98 -0
- package/dist/categories/timing.js.map +1 -0
- package/dist/categories/types.d.ts +68 -0
- package/dist/categories/types.d.ts.map +1 -0
- package/dist/categories/types.js +31 -0
- package/dist/categories/types.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +181 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +169 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +115 -0
- package/dist/protocol.js.map +1 -0
- package/dist/report.d.ts +48 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +79 -0
- package/dist/report.js.map +1 -0
- package/dist/runner.d.ts +129 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +305 -0
- package/dist/runner.js.map +1 -0
- package/dist/sizes.d.ts +84 -0
- package/dist/sizes.d.ts.map +1 -0
- package/dist/sizes.js +214 -0
- package/dist/sizes.js.map +1 -0
- package/dist/vectors.d.ts +69 -0
- package/dist/vectors.d.ts.map +1 -0
- package/dist/vectors.js +198 -0
- package/dist/vectors.js.map +1 -0
- package/package.json +43 -0
- package/vectors/README.md +83 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Sieve <-> SUT wire protocol.
|
|
3
|
+
*
|
|
4
|
+
* Sieve drives a system-under-test (SUT) — any ML-KEM / ML-DSA implementation
|
|
5
|
+
* the user provides — by spawning it as a child process and exchanging
|
|
6
|
+
* newline-delimited JSON (NDJSON) over stdin/stdout. One request per line, one
|
|
7
|
+
* response per line. All byte fields are base64-encoded strings.
|
|
8
|
+
*
|
|
9
|
+
* This module defines the request/response TypeScript types and the
|
|
10
|
+
* (de)serialization + validation logic. It performs NO cryptography. See
|
|
11
|
+
* PROTOCOL.md for the human-readable specification.
|
|
12
|
+
*/
|
|
13
|
+
import type { Family, ParamSet } from "./sizes.js";
|
|
14
|
+
/** Protocol version. Bumped on any breaking wire change. */
|
|
15
|
+
export declare const PROTOCOL_VERSION: 1;
|
|
16
|
+
/** Operation names, shared across families where the semantics overlap. */
|
|
17
|
+
export type Op = "keygen" | "encaps" | "decaps" | "sign" | "verify";
|
|
18
|
+
/** Families that support the signature operations (sign / verify). */
|
|
19
|
+
export type SignatureFamily = "ml-dsa" | "slh-dsa";
|
|
20
|
+
/** Fields common to every request. */
|
|
21
|
+
interface RequestBase {
|
|
22
|
+
/** Correlation id; the SUT MUST echo it back on the matching response. */
|
|
23
|
+
id: number;
|
|
24
|
+
/** Algorithm family this request targets. */
|
|
25
|
+
family: Family;
|
|
26
|
+
/** Parameter-set identifier, e.g. "ml-kem-768". */
|
|
27
|
+
param: ParamSet;
|
|
28
|
+
/** Operation to perform. */
|
|
29
|
+
op: Op;
|
|
30
|
+
}
|
|
31
|
+
/** ML-KEM / ML-DSA key generation. `seed` (base64) makes it deterministic. */
|
|
32
|
+
export interface KeygenRequest extends RequestBase {
|
|
33
|
+
op: "keygen";
|
|
34
|
+
/** Optional base64 seed for deterministic keygen (impl-defined length). */
|
|
35
|
+
seed?: string;
|
|
36
|
+
}
|
|
37
|
+
/** ML-KEM encapsulation against a public key. */
|
|
38
|
+
export interface EncapsRequest extends RequestBase {
|
|
39
|
+
family: "ml-kem";
|
|
40
|
+
op: "encaps";
|
|
41
|
+
/** Base64 public (encapsulation) key. */
|
|
42
|
+
pk: string;
|
|
43
|
+
/** Optional base64 coins/randomness for deterministic encapsulation. */
|
|
44
|
+
coins?: string;
|
|
45
|
+
}
|
|
46
|
+
/** ML-KEM decapsulation of a ciphertext with a secret key. */
|
|
47
|
+
export interface DecapsRequest extends RequestBase {
|
|
48
|
+
family: "ml-kem";
|
|
49
|
+
op: "decaps";
|
|
50
|
+
/** Base64 secret (decapsulation) key. */
|
|
51
|
+
sk: string;
|
|
52
|
+
/** Base64 ciphertext. */
|
|
53
|
+
ct: string;
|
|
54
|
+
}
|
|
55
|
+
/** ML-DSA / SLH-DSA signature generation. */
|
|
56
|
+
export interface SignRequest extends RequestBase {
|
|
57
|
+
family: SignatureFamily;
|
|
58
|
+
op: "sign";
|
|
59
|
+
/** Base64 secret (signing) key. */
|
|
60
|
+
sk: string;
|
|
61
|
+
/** Base64 message to sign. */
|
|
62
|
+
msg: string;
|
|
63
|
+
}
|
|
64
|
+
/** ML-DSA / SLH-DSA signature verification. */
|
|
65
|
+
export interface VerifyRequest extends RequestBase {
|
|
66
|
+
family: SignatureFamily;
|
|
67
|
+
op: "verify";
|
|
68
|
+
/** Base64 public (verification) key. */
|
|
69
|
+
pk: string;
|
|
70
|
+
/** Base64 message. */
|
|
71
|
+
msg: string;
|
|
72
|
+
/** Base64 signature. */
|
|
73
|
+
sig: string;
|
|
74
|
+
}
|
|
75
|
+
/** Any request Sieve may send. */
|
|
76
|
+
export type Request = KeygenRequest | EncapsRequest | DecapsRequest | SignRequest | VerifyRequest;
|
|
77
|
+
/**
|
|
78
|
+
* Omit that distributes over a union, so each variant keeps its own fields
|
|
79
|
+
* (plain `Omit<Request, "id">` would collapse to only the common keys).
|
|
80
|
+
*/
|
|
81
|
+
type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never;
|
|
82
|
+
/** A request as handed to the runner — the `id` is assigned by the runner. */
|
|
83
|
+
export type RequestInput = DistributiveOmit<Request, "id"> & {
|
|
84
|
+
id?: number;
|
|
85
|
+
};
|
|
86
|
+
/** Successful keygen result. */
|
|
87
|
+
export interface KeygenResult {
|
|
88
|
+
id: number;
|
|
89
|
+
ok: true;
|
|
90
|
+
/** Base64 public key. */
|
|
91
|
+
pk: string;
|
|
92
|
+
/** Base64 secret key. */
|
|
93
|
+
sk: string;
|
|
94
|
+
}
|
|
95
|
+
/** Successful encapsulation result. */
|
|
96
|
+
export interface EncapsResult {
|
|
97
|
+
id: number;
|
|
98
|
+
ok: true;
|
|
99
|
+
/** Base64 ciphertext. */
|
|
100
|
+
ct: string;
|
|
101
|
+
/** Base64 shared secret. */
|
|
102
|
+
ss: string;
|
|
103
|
+
}
|
|
104
|
+
/** Successful decapsulation result. */
|
|
105
|
+
export interface DecapsResult {
|
|
106
|
+
id: number;
|
|
107
|
+
ok: true;
|
|
108
|
+
/** Base64 shared secret. */
|
|
109
|
+
ss: string;
|
|
110
|
+
}
|
|
111
|
+
/** Successful signing result. */
|
|
112
|
+
export interface SignResult {
|
|
113
|
+
id: number;
|
|
114
|
+
ok: true;
|
|
115
|
+
/** Base64 signature. */
|
|
116
|
+
sig: string;
|
|
117
|
+
}
|
|
118
|
+
/** Successful verification result. `valid` is the verification verdict. */
|
|
119
|
+
export interface VerifyResult {
|
|
120
|
+
id: number;
|
|
121
|
+
ok: true;
|
|
122
|
+
/** Whether the signature verified against (pk, msg). */
|
|
123
|
+
valid: boolean;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A defined, well-formed error. The SUT MUST return this (not crash, not hang,
|
|
127
|
+
* not silently produce garbage) when it cannot or will not honor a request —
|
|
128
|
+
* for example when given a wrong-length key. Sieve treats a clean `error`
|
|
129
|
+
* response as a *correct* rejection in the size/robustness categories.
|
|
130
|
+
*/
|
|
131
|
+
export interface ErrorResult {
|
|
132
|
+
id: number;
|
|
133
|
+
ok: false;
|
|
134
|
+
/** Short machine-readable code, e.g. "invalid-length", "unsupported". */
|
|
135
|
+
code: string;
|
|
136
|
+
/** Human-readable detail. */
|
|
137
|
+
message: string;
|
|
138
|
+
}
|
|
139
|
+
/** Any response the SUT may emit. */
|
|
140
|
+
export type Response = KeygenResult | EncapsResult | DecapsResult | SignResult | VerifyResult | ErrorResult;
|
|
141
|
+
/** A successful (non-error) response. */
|
|
142
|
+
export type SuccessResponse = Exclude<Response, ErrorResult>;
|
|
143
|
+
/** Serialize a request to a single NDJSON line (including the trailing "\n"). */
|
|
144
|
+
export declare function encodeRequest(req: Request): string;
|
|
145
|
+
/** Raised when a line from the SUT cannot be parsed into a valid Response. */
|
|
146
|
+
export declare class ProtocolError extends Error {
|
|
147
|
+
/** The offending raw line, truncated for safety. */
|
|
148
|
+
readonly raw: string;
|
|
149
|
+
constructor(message: string, raw: string);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Parse one NDJSON line from the SUT into a validated {@link Response}.
|
|
153
|
+
*
|
|
154
|
+
* Validates structural shape only (presence and types of fields); it does NOT
|
|
155
|
+
* validate cryptographic content or byte lengths — that is the categories'
|
|
156
|
+
* job. Throws {@link ProtocolError} on malformed input.
|
|
157
|
+
*/
|
|
158
|
+
export declare function decodeResponse(line: string): Response;
|
|
159
|
+
/** Encode raw bytes to a base64 string. */
|
|
160
|
+
export declare function toB64(bytes: Uint8Array): string;
|
|
161
|
+
/**
|
|
162
|
+
* Decode a base64 string to bytes.
|
|
163
|
+
*
|
|
164
|
+
* @throws {ProtocolError} if the input is not valid base64 (i.e. re-encoding
|
|
165
|
+
* the decoded bytes does not reproduce the canonical form).
|
|
166
|
+
*/
|
|
167
|
+
export declare function fromB64(b64: string): Uint8Array;
|
|
168
|
+
export {};
|
|
169
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Sieve <-> SUT wire protocol.
|
|
3
|
+
*
|
|
4
|
+
* Sieve drives a system-under-test (SUT) — any ML-KEM / ML-DSA implementation
|
|
5
|
+
* the user provides — by spawning it as a child process and exchanging
|
|
6
|
+
* newline-delimited JSON (NDJSON) over stdin/stdout. One request per line, one
|
|
7
|
+
* response per line. All byte fields are base64-encoded strings.
|
|
8
|
+
*
|
|
9
|
+
* This module defines the request/response TypeScript types and the
|
|
10
|
+
* (de)serialization + validation logic. It performs NO cryptography. See
|
|
11
|
+
* PROTOCOL.md for the human-readable specification.
|
|
12
|
+
*/
|
|
13
|
+
/** Protocol version. Bumped on any breaking wire change. */
|
|
14
|
+
export const PROTOCOL_VERSION = 1;
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Serialization
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/** Serialize a request to a single NDJSON line (including the trailing "\n"). */
|
|
19
|
+
export function encodeRequest(req) {
|
|
20
|
+
return JSON.stringify(req) + "\n";
|
|
21
|
+
}
|
|
22
|
+
/** Raised when a line from the SUT cannot be parsed into a valid Response. */
|
|
23
|
+
export class ProtocolError extends Error {
|
|
24
|
+
/** The offending raw line, truncated for safety. */
|
|
25
|
+
raw;
|
|
26
|
+
constructor(message, raw) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "ProtocolError";
|
|
29
|
+
this.raw = raw.length > 512 ? raw.slice(0, 512) + "…" : raw;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function isObject(v) {
|
|
33
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
34
|
+
}
|
|
35
|
+
function isStr(v) {
|
|
36
|
+
return typeof v === "string";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Parse one NDJSON line from the SUT into a validated {@link Response}.
|
|
40
|
+
*
|
|
41
|
+
* Validates structural shape only (presence and types of fields); it does NOT
|
|
42
|
+
* validate cryptographic content or byte lengths — that is the categories'
|
|
43
|
+
* job. Throws {@link ProtocolError} on malformed input.
|
|
44
|
+
*/
|
|
45
|
+
export function decodeResponse(line) {
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
if (trimmed.length === 0) {
|
|
48
|
+
throw new ProtocolError("empty line", line);
|
|
49
|
+
}
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = JSON.parse(trimmed);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
throw new ProtocolError(`not valid JSON: ${err.message}`, line);
|
|
56
|
+
}
|
|
57
|
+
if (!isObject(parsed)) {
|
|
58
|
+
throw new ProtocolError("response is not a JSON object", line);
|
|
59
|
+
}
|
|
60
|
+
const id = parsed["id"];
|
|
61
|
+
if (typeof id !== "number" || !Number.isInteger(id)) {
|
|
62
|
+
throw new ProtocolError("missing/invalid integer 'id'", line);
|
|
63
|
+
}
|
|
64
|
+
const ok = parsed["ok"];
|
|
65
|
+
if (typeof ok !== "boolean") {
|
|
66
|
+
throw new ProtocolError("missing/invalid boolean 'ok'", line);
|
|
67
|
+
}
|
|
68
|
+
if (ok === false) {
|
|
69
|
+
const code = parsed["code"];
|
|
70
|
+
const message = parsed["message"];
|
|
71
|
+
if (!isStr(code) || !isStr(message)) {
|
|
72
|
+
throw new ProtocolError("error response must have string 'code' and 'message'", line);
|
|
73
|
+
}
|
|
74
|
+
return { id, ok: false, code, message };
|
|
75
|
+
}
|
|
76
|
+
// ok === true: discriminate by which payload fields are present.
|
|
77
|
+
if (isStr(parsed["pk"]) && isStr(parsed["sk"])) {
|
|
78
|
+
return { id, ok: true, pk: parsed["pk"], sk: parsed["sk"] };
|
|
79
|
+
}
|
|
80
|
+
if (isStr(parsed["ct"]) && isStr(parsed["ss"])) {
|
|
81
|
+
return { id, ok: true, ct: parsed["ct"], ss: parsed["ss"] };
|
|
82
|
+
}
|
|
83
|
+
if (isStr(parsed["ss"])) {
|
|
84
|
+
return { id, ok: true, ss: parsed["ss"] };
|
|
85
|
+
}
|
|
86
|
+
if (isStr(parsed["sig"])) {
|
|
87
|
+
return { id, ok: true, sig: parsed["sig"] };
|
|
88
|
+
}
|
|
89
|
+
if (typeof parsed["valid"] === "boolean") {
|
|
90
|
+
return { id, ok: true, valid: parsed["valid"] };
|
|
91
|
+
}
|
|
92
|
+
throw new ProtocolError("ok response has no recognizable payload " + "(expected pk+sk, ct+ss, ss, sig, or valid)", line);
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Base64 helpers (Node built-in Buffer — no external dependency)
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
/** Encode raw bytes to a base64 string. */
|
|
98
|
+
export function toB64(bytes) {
|
|
99
|
+
return Buffer.from(bytes).toString("base64");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Decode a base64 string to bytes.
|
|
103
|
+
*
|
|
104
|
+
* @throws {ProtocolError} if the input is not valid base64 (i.e. re-encoding
|
|
105
|
+
* the decoded bytes does not reproduce the canonical form).
|
|
106
|
+
*/
|
|
107
|
+
export function fromB64(b64) {
|
|
108
|
+
const buf = Buffer.from(b64, "base64");
|
|
109
|
+
// Buffer.from is lenient; sanity-check by re-encoding the canonical form.
|
|
110
|
+
if (buf.toString("base64").replace(/=+$/, "") !== b64.replace(/\s/g, "").replace(/=+$/, "")) {
|
|
111
|
+
throw new ProtocolError("invalid base64", b64);
|
|
112
|
+
}
|
|
113
|
+
return new Uint8Array(buf);
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/report.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report aggregation and formatting.
|
|
3
|
+
*
|
|
4
|
+
* Combines per-category results into a single {@link SieveReport} with an
|
|
5
|
+
* overall PASS/FAIL verdict, then renders it as JSON or as a human-readable
|
|
6
|
+
* terminal summary. Advisory categories (timing) never affect the verdict.
|
|
7
|
+
*/
|
|
8
|
+
import type { CategoryResult } from "./categories/types.js";
|
|
9
|
+
import type { ParamSet } from "./sizes.js";
|
|
10
|
+
/** Per-category counts. */
|
|
11
|
+
export interface CategoryCounts {
|
|
12
|
+
pass: number;
|
|
13
|
+
fail: number;
|
|
14
|
+
skip: number;
|
|
15
|
+
}
|
|
16
|
+
/** The full report. */
|
|
17
|
+
export interface SieveReport {
|
|
18
|
+
tool: "sieve";
|
|
19
|
+
protocolVersion: number;
|
|
20
|
+
param: ParamSet;
|
|
21
|
+
/** Argv of the SUT that was tested. */
|
|
22
|
+
impl: string[];
|
|
23
|
+
iterations: number;
|
|
24
|
+
vectorsDir?: string;
|
|
25
|
+
startedAt: string;
|
|
26
|
+
durationMs: number;
|
|
27
|
+
/** Overall verdict (advisory categories excluded). */
|
|
28
|
+
overall: "PASS" | "FAIL";
|
|
29
|
+
categories: CategoryResult[];
|
|
30
|
+
counts: CategoryCounts;
|
|
31
|
+
}
|
|
32
|
+
/** Compute the overall verdict: FAIL if any non-advisory category failed. */
|
|
33
|
+
export declare function overallVerdict(categories: readonly CategoryResult[]): "PASS" | "FAIL";
|
|
34
|
+
/** Assemble a {@link SieveReport} from category results and run metadata. */
|
|
35
|
+
export declare function buildReport(args: {
|
|
36
|
+
param: ParamSet;
|
|
37
|
+
impl: string[];
|
|
38
|
+
iterations: number;
|
|
39
|
+
vectorsDir?: string;
|
|
40
|
+
startedAt: Date;
|
|
41
|
+
durationMs: number;
|
|
42
|
+
categories: CategoryResult[];
|
|
43
|
+
}): SieveReport;
|
|
44
|
+
/** Pretty JSON rendering. */
|
|
45
|
+
export declare function formatJson(report: SieveReport): string;
|
|
46
|
+
/** Human-readable terminal rendering (no color codes; CI-friendly). */
|
|
47
|
+
export declare function formatHuman(report: SieveReport): string;
|
|
48
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAU,MAAM,uBAAuB,CAAC;AAEpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,2BAA2B;AAC3B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,uBAAuB;AACvB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,QAAQ,CAAC;IAChB,uCAAuC;IACvC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,MAAM,EAAE,cAAc,CAAC;CACxB;AAWD,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,GAAG,MAAM,CAMrF;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAChC,KAAK,EAAE,QAAQ,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B,GAAG,WAAW,CAcd;AAED,6BAA6B;AAC7B,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAEtD;AAID,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CA4BvD"}
|
package/dist/report.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Report aggregation and formatting.
|
|
3
|
+
*
|
|
4
|
+
* Combines per-category results into a single {@link SieveReport} with an
|
|
5
|
+
* overall PASS/FAIL verdict, then renders it as JSON or as a human-readable
|
|
6
|
+
* terminal summary. Advisory categories (timing) never affect the verdict.
|
|
7
|
+
*/
|
|
8
|
+
import { PROTOCOL_VERSION } from "./protocol.js";
|
|
9
|
+
/** Categories that are informational and must not change the overall verdict. */
|
|
10
|
+
const ADVISORY_CATEGORIES = new Set(["timing"]);
|
|
11
|
+
/** Tally check-level pass/fail/skip across all categories. */
|
|
12
|
+
function tally(categories) {
|
|
13
|
+
const counts = { pass: 0, fail: 0, skip: 0 };
|
|
14
|
+
for (const c of categories) {
|
|
15
|
+
for (const chk of c.checks)
|
|
16
|
+
counts[chk.status]++;
|
|
17
|
+
}
|
|
18
|
+
return counts;
|
|
19
|
+
}
|
|
20
|
+
/** Compute the overall verdict: FAIL if any non-advisory category failed. */
|
|
21
|
+
export function overallVerdict(categories) {
|
|
22
|
+
for (const c of categories) {
|
|
23
|
+
if (ADVISORY_CATEGORIES.has(c.category))
|
|
24
|
+
continue;
|
|
25
|
+
if (c.status === "fail")
|
|
26
|
+
return "FAIL";
|
|
27
|
+
}
|
|
28
|
+
return "PASS";
|
|
29
|
+
}
|
|
30
|
+
/** Assemble a {@link SieveReport} from category results and run metadata. */
|
|
31
|
+
export function buildReport(args) {
|
|
32
|
+
return {
|
|
33
|
+
tool: "sieve",
|
|
34
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
35
|
+
param: args.param,
|
|
36
|
+
impl: args.impl,
|
|
37
|
+
iterations: args.iterations,
|
|
38
|
+
...(args.vectorsDir ? { vectorsDir: args.vectorsDir } : {}),
|
|
39
|
+
startedAt: args.startedAt.toISOString(),
|
|
40
|
+
durationMs: args.durationMs,
|
|
41
|
+
overall: overallVerdict(args.categories),
|
|
42
|
+
categories: args.categories,
|
|
43
|
+
counts: tally(args.categories),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Pretty JSON rendering. */
|
|
47
|
+
export function formatJson(report) {
|
|
48
|
+
return JSON.stringify(report, null, 2);
|
|
49
|
+
}
|
|
50
|
+
const SYMBOL = { pass: "PASS", fail: "FAIL", skip: "SKIP" };
|
|
51
|
+
/** Human-readable terminal rendering (no color codes; CI-friendly). */
|
|
52
|
+
export function formatHuman(report) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
lines.push(`sieve — ML-KEM/ML-DSA/SLH-DSA conformance battery`);
|
|
55
|
+
lines.push(` param : ${report.param}`);
|
|
56
|
+
lines.push(` impl : ${report.impl.join(" ")}`);
|
|
57
|
+
lines.push(` iterations : ${report.iterations}`);
|
|
58
|
+
if (report.vectorsDir)
|
|
59
|
+
lines.push(` vectors : ${report.vectorsDir}`);
|
|
60
|
+
lines.push(` duration : ${report.durationMs} ms`);
|
|
61
|
+
lines.push("");
|
|
62
|
+
for (const cat of report.categories) {
|
|
63
|
+
const advisory = ADVISORY_CATEGORIES.has(cat.category) ? " (advisory)" : "";
|
|
64
|
+
const bug = cat.bugClass ? ` [${cat.bugClass}]` : "";
|
|
65
|
+
lines.push(`[${SYMBOL[cat.status]}] ${cat.category}${bug}${advisory} — ${cat.summary}`);
|
|
66
|
+
// Show failing and skipped checks; collapse passes.
|
|
67
|
+
for (const chk of cat.checks) {
|
|
68
|
+
if (chk.status === "pass")
|
|
69
|
+
continue;
|
|
70
|
+
const cbug = chk.bugClass ? ` [${chk.bugClass}]` : "";
|
|
71
|
+
lines.push(` - ${SYMBOL[chk.status]} ${chk.name}${cbug}: ${chk.detail}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
lines.push("");
|
|
75
|
+
lines.push(`checks: ${report.counts.pass} pass, ${report.counts.fail} fail, ${report.counts.skip} skip`);
|
|
76
|
+
lines.push(`OVERALL: ${report.overall}`);
|
|
77
|
+
return lines.join("\n");
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SUT runner: spawns a user-provided implementation as a child process and
|
|
3
|
+
* exchanges newline-delimited JSON requests/responses over stdin/stdout.
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* - spawn the command, wire up a readline interface on its stdout,
|
|
7
|
+
* - correlate responses to requests by `id`,
|
|
8
|
+
* - enforce a per-request timeout,
|
|
9
|
+
* - surface SUT crashes / stderr,
|
|
10
|
+
* - tear the process down cleanly.
|
|
11
|
+
*
|
|
12
|
+
* Uses only node:child_process and node:readline. No cryptography here.
|
|
13
|
+
*/
|
|
14
|
+
import { type Request, type RequestInput, type Response } from "./protocol.js";
|
|
15
|
+
/** Options for constructing a {@link Runner}. */
|
|
16
|
+
export interface RunnerOptions {
|
|
17
|
+
/** Argv of the SUT, e.g. ["node", "./my-impl.js"]. First item is the bin. */
|
|
18
|
+
command: readonly string[];
|
|
19
|
+
/** Per-request timeout in milliseconds. */
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
/** Working directory for the child process. */
|
|
22
|
+
cwd?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Extra environment variables for the SUT. These are layered ON TOP of the
|
|
25
|
+
* scrubbed base env (see below) — they are the ONLY way to pass arbitrary
|
|
26
|
+
* variables to a SUT unless {@link RunnerOptions.inheritEnv} is set.
|
|
27
|
+
*/
|
|
28
|
+
env?: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* Inherit the FULL parent environment (`process.env`) instead of the scrubbed
|
|
31
|
+
* minimal env. **Default `false`.** The SUT is user-supplied code that Sieve
|
|
32
|
+
* drives; inheriting the parent env hands it every secret in the harness's
|
|
33
|
+
* environment (CI tokens, cloud creds, signing keys). Only enable this for
|
|
34
|
+
* trusted, local implementations you control. See docs/audits/security.md Q-17
|
|
35
|
+
* (CWE-526 / CWE-200).
|
|
36
|
+
*/
|
|
37
|
+
inheritEnv?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Additional environment variable NAMES to copy from `process.env` into the
|
|
40
|
+
* scrubbed base env (allow-list extension). Has no effect when
|
|
41
|
+
* {@link RunnerOptions.inheritEnv} is `true`. Values are read from the parent
|
|
42
|
+
* env at spawn time; names absent from `process.env` are skipped.
|
|
43
|
+
*/
|
|
44
|
+
envAllowlist?: readonly string[];
|
|
45
|
+
/** Optional sink for the SUT's stderr lines (for diagnostics). */
|
|
46
|
+
onStderr?: (line: string) => void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Minimal environment variables a child process generally needs to locate its
|
|
50
|
+
* interpreter, resolve its home directory, and produce sane text output. This
|
|
51
|
+
* is the default base env handed to the SUT — secrets in the parent env
|
|
52
|
+
* (tokens, credentials) are NOT forwarded. Extend via
|
|
53
|
+
* {@link RunnerOptions.envAllowlist} or pass explicit {@link RunnerOptions.env}.
|
|
54
|
+
*/
|
|
55
|
+
export declare const DEFAULT_ENV_ALLOWLIST: readonly string[];
|
|
56
|
+
/**
|
|
57
|
+
* Build the environment handed to the spawned SUT.
|
|
58
|
+
*
|
|
59
|
+
* By default this is a SCRUBBED, minimal env: only the names in
|
|
60
|
+
* {@link DEFAULT_ENV_ALLOWLIST} (plus any in `opts.envAllowlist`) are copied
|
|
61
|
+
* from the parent `process.env`, then `opts.env` is layered on top. This keeps
|
|
62
|
+
* harness secrets out of untrusted SUT code (security.md Q-17). When
|
|
63
|
+
* `opts.inheritEnv` is set, the full parent env is used instead (legacy /
|
|
64
|
+
* trusted-local behavior).
|
|
65
|
+
*
|
|
66
|
+
* Exported for testing; the returned object never aliases `process.env`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function buildSutEnv(opts: Pick<RunnerOptions, "env" | "inheritEnv" | "envAllowlist">, parentEnv?: NodeJS.ProcessEnv): Record<string, string>;
|
|
69
|
+
/** Thrown when a request exceeds its timeout. */
|
|
70
|
+
export declare class TimeoutError extends Error {
|
|
71
|
+
readonly request: Request;
|
|
72
|
+
readonly timeoutMs: number;
|
|
73
|
+
constructor(request: Request, timeoutMs: number);
|
|
74
|
+
}
|
|
75
|
+
/** Thrown when the SUT process dies before/while a request is in flight. */
|
|
76
|
+
export declare class SutCrashError extends Error {
|
|
77
|
+
readonly stderr: string;
|
|
78
|
+
constructor(message: string, stderr: string);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A long-lived handle to a spawned SUT. Construct once per test run, issue many
|
|
82
|
+
* requests, then `close()`. Requests are matched to responses by `id`, so the
|
|
83
|
+
* SUT MAY answer out of order, though in practice it answers serially.
|
|
84
|
+
*/
|
|
85
|
+
export declare class Runner {
|
|
86
|
+
private readonly child;
|
|
87
|
+
private readonly rl;
|
|
88
|
+
private readonly pending;
|
|
89
|
+
private readonly timeoutMs;
|
|
90
|
+
private nextId;
|
|
91
|
+
private stderrBuf;
|
|
92
|
+
private closed;
|
|
93
|
+
private fatal;
|
|
94
|
+
constructor(opts: RunnerOptions);
|
|
95
|
+
/** Stderr captured from the SUT so far (most recent 64 KiB). */
|
|
96
|
+
get stderr(): string;
|
|
97
|
+
private onLine;
|
|
98
|
+
private failAll;
|
|
99
|
+
/**
|
|
100
|
+
* Send one request and await its response. The `id` field is assigned by the
|
|
101
|
+
* runner; any `id` on the passed object is overwritten.
|
|
102
|
+
*/
|
|
103
|
+
send(req: RequestInput): Promise<Response>;
|
|
104
|
+
/**
|
|
105
|
+
* Issue many INDEPENDENT requests with bounded concurrency (pipelining).
|
|
106
|
+
*
|
|
107
|
+
* The protocol is id-correlated (each response carries the request's `id`),
|
|
108
|
+
* so multiple requests may be in flight against the SUT at once. This writes
|
|
109
|
+
* up to `maxInFlight` requests before awaiting any response, refilling as each
|
|
110
|
+
* completes, and returns results in the SAME ORDER as `reqs` (independent of
|
|
111
|
+
* the order the SUT answers).
|
|
112
|
+
*
|
|
113
|
+
* CORRECTNESS CONSTRAINT: every request in `reqs` MUST be independent of the
|
|
114
|
+
* others — no request may depend on another's response, and the SUT must not
|
|
115
|
+
* carry cross-request state that ordering would expose. Dependent or
|
|
116
|
+
* order-sensitive sequences (e.g. keygen→encaps→decaps for a single key, or
|
|
117
|
+
* the timing category's isolated measurements) MUST use serial `send()`
|
|
118
|
+
* instead. See docs/audits/performance.md §7.1.
|
|
119
|
+
*
|
|
120
|
+
* Setting `maxInFlight <= 1` degrades to strictly serial behavior.
|
|
121
|
+
*/
|
|
122
|
+
sendMany(reqs: readonly RequestInput[], maxInFlight?: number): Promise<Response[]>;
|
|
123
|
+
/**
|
|
124
|
+
* Gracefully shut the SUT down: end stdin, wait briefly for a clean exit,
|
|
125
|
+
* then SIGTERM, then SIGKILL. Idempotent.
|
|
126
|
+
*/
|
|
127
|
+
close(graceMs?: number): Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAIL,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAEvB,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,kEAAkE;IAClE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAelD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,YAAY,GAAG,cAAc,CAAC,EAChE,SAAS,GAAE,MAAM,CAAC,UAAwB,GACzC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBxB;AAED,iDAAiD;AACjD,qBAAa,YAAa,SAAQ,KAAK;aAEnB,OAAO,EAAE,OAAO;aAChB,SAAS,EAAE,MAAM;gBADjB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM;CAKpC;AAED,4EAA4E;AAC5E,qBAAa,aAAc,SAAQ,KAAK;aAGpB,MAAM,EAAE,MAAM;gBAD9B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM;CAKjC;AAUD;;;;GAIG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAoB;gBAErB,IAAI,EAAE,aAAa;IA0C/B,gEAAgE;IAChE,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,OAAO,CAAC,MAAM;IAsBd,OAAO,CAAC,OAAO;IASf;;;OAGG;IACH,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC;IA8B1C;;;;;;;;;;;;;;;;;OAiBG;IACG,QAAQ,CAAC,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,SAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA8BpF;;;OAGG;IACG,KAAK,CAAC,OAAO,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAgC1C"}
|