Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Attestation-evidence envelope (#8541, attested-evaluation epic) — the typed evidence seam that lets a later
// runner attach "this backtest run executed inside an attested TEE" evidence to a persisted run, without
// inventing an ad-hoc shape. This file is PURE STRUCTURAL code only: a schema type, a deterministic
// report-data binder, and a never-throwing structural validator. It performs NO cryptographic verification of
// the attestation report (that is later maintainer work in the epic), reads no IO, and adds no dependency.

import { createHash } from "node:crypto";

/** The verification outcome recorded on an envelope. Discriminated on `status`: an envelope may be captured
* before any verifier has run (`unverified`), or carry a verifier's pass (`verified`) / fail (`failed`)
* judgment. `verifiedAt` is an ISO-8601 datetime; `reason` explains a failure. */
export type AttestationVerification =
| { status: "unverified" }
| { status: "verified"; verifierId: string; verifiedAt: string }
| { status: "failed"; verifierId: string; verifiedAt: string; reason: string };

/** Evidence that a run executed inside an attested environment. Structural only — the `attestationReport` is
* an opaque base64 blob this module never cryptographically verifies. */
export type AttestationEnvelope = {
schemaVersion: 1;
teeTechnology: "sev-snp" | "tdx";
runtimeClass: string;
measurement: string;
reportData: string;
attestationReport: string;
verification: AttestationVerification;
};

/**
* The 32-byte `reportData` an attestation report must bind to, as lowercase-hex sha256 of
* `${corpusChecksum}:${headSha}:${baseSha}` — the same tuple that already makes a persisted run
* third-party reproducible (#8136). Deterministic; mirrors backtest-split.ts's `createHash("sha256")` usage.
*/
export function buildAttestationReportData(binding: { corpusChecksum: string; headSha: string; baseSha: string }): string {
return createHash("sha256").update(`${binding.corpusChecksum}:${binding.headSha}:${binding.baseSha}`).digest("hex");
}

const KNOWN_ENVELOPE_KEYS = new Set(["schemaVersion", "teeTechnology", "runtimeClass", "measurement", "reportData", "attestationReport", "verification"]);
const MEASUREMENT_RE = /^[0-9a-f]{32,128}$/;
const REPORT_DATA_RE = /^[0-9a-f]{64}$/;
const BASE64_RE = /^[A-Za-z0-9+/]+={0,2}$/;
// An ISO-8601 datetime (date + time + zone). Paired with a Date.parse check so a well-shaped but impossible
// value (e.g. month 13) is still rejected -- the regex governs shape, Date.parse governs real-calendar validity.
const ISO_DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;

function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}

function isNonEmptyString(value: unknown): value is string {
return typeof value === "string" && value.length > 0;
}

function isIsoDateTime(value: unknown): boolean {
return typeof value === "string" && ISO_DATETIME_RE.test(value) && !Number.isNaN(Date.parse(value));
}

function validateVerification(value: unknown, errors: string[]): void {
if (!isRecord(value)) {
errors.push("verification: must be an object");
return;
}
if (value.status === "unverified") return;
if (value.status === "verified" || value.status === "failed") {
if (!isNonEmptyString(value.verifierId)) errors.push("verification.verifierId: must be a non-empty string");
if (!isIsoDateTime(value.verifiedAt)) errors.push("verification.verifiedAt: must be an ISO-8601 datetime");
if (value.status === "failed" && !isNonEmptyString(value.reason)) errors.push("verification.reason: must be a non-empty string");
return;
}
errors.push('verification.status: must be "unverified", "verified", or "failed"');
}

/**
* Structurally validate an unknown value against {@link AttestationEnvelope}. Never throws for any input
* (null, primitives, arrays, objects with extra keys). On failure, `errors` names every failing field path;
* on success, `envelope` is the input narrowed to the type. Structural only — no cryptographic verification.
*/
export function validateAttestationEnvelope(value: unknown): { valid: true; envelope: AttestationEnvelope } | { valid: false; errors: string[] } {
if (!isRecord(value)) {
return { valid: false, errors: ["envelope: must be a non-null object"] };
}
const errors: string[] = [];
for (const key of Object.keys(value)) {
if (!KNOWN_ENVELOPE_KEYS.has(key)) errors.push(`envelope: unexpected key "${key}"`);
}
if (value.schemaVersion !== 1) errors.push("schemaVersion: must be the literal 1");
if (value.teeTechnology !== "sev-snp" && value.teeTechnology !== "tdx") errors.push('teeTechnology: must be "sev-snp" or "tdx"');
if (!isNonEmptyString(value.runtimeClass) || value.runtimeClass.length > 128) errors.push("runtimeClass: must be a non-empty string of at most 128 chars");
if (typeof value.measurement !== "string" || !MEASUREMENT_RE.test(value.measurement)) errors.push("measurement: must be 32-128 lowercase hex chars");
if (typeof value.reportData !== "string" || !REPORT_DATA_RE.test(value.reportData)) errors.push("reportData: must be exactly 64 lowercase hex chars");
if (typeof value.attestationReport !== "string" || value.attestationReport.length > 65536 || !BASE64_RE.test(value.attestationReport)) {
errors.push("attestationReport: must be non-empty base64 of at most 65536 chars");
}
validateVerification(value.verification, errors);
if (errors.length > 0) return { valid: false, errors };
return { valid: true, envelope: value as AttestationEnvelope };
}
1 change: 1 addition & 0 deletions packages/loopover-engine/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export * from "./governor/kill-switch.js";
export * from "./governor/action-mode.js";
export * from "./governor/chokepoint.js";
export * from "./calibration/signal-tracking.js";
export * from "./calibration/attestation-envelope.js";
export * from "./calibration/backtest-corpus.js";
export * from "./calibration/repo-corpus-slice.js";
export * from "./calibration/ams-prediction-corpus.js";
Expand Down
97 changes: 97 additions & 0 deletions packages/loopover-engine/test/attestation-envelope.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import assert from "node:assert/strict";
import { test } from "node:test";

import { buildAttestationReportData, validateAttestationEnvelope, type AttestationEnvelope } from "../dist/index.js";

function validEnvelope(overrides: Partial<Record<keyof AttestationEnvelope, unknown>> = {}): Record<string, unknown> {
return {
schemaVersion: 1,
teeTechnology: "sev-snp",
runtimeClass: "confidential-runner-v1",
measurement: "a".repeat(64),
reportData: "b".repeat(64),
attestationReport: "QUJDZA==",
verification: { status: "unverified" },
...overrides,
};
}

function errorsOf(value: unknown): string[] {
const result = validateAttestationEnvelope(value);
assert.equal(result.valid, false);
return result.valid ? [] : result.errors;
}

test("buildAttestationReportData binds corpusChecksum:headSha:baseSha as lowercase-hex sha256 (pinned vector)", () => {
assert.equal(
buildAttestationReportData({ corpusChecksum: "corpus-abc", headSha: "head-111", baseSha: "base-222" }),
"f3fe846e3d8db839cfa76d48577cad76159f75460d2bf881568ba5d926319e28",
);
});

test("validateAttestationEnvelope accepts a fully valid envelope", () => {
const result = validateAttestationEnvelope(validEnvelope());
assert.equal(result.valid, true);
});

test("validateAttestationEnvelope never throws and rejects non-object inputs", () => {
for (const bad of [null, undefined, 42, "str", true, []]) {
const result = validateAttestationEnvelope(bad);
assert.equal(result.valid, false);
if (!result.valid) assert.deepEqual(result.errors, ["envelope: must be a non-null object"]);
}
});

test("validateAttestationEnvelope rejects an unexpected extra key, naming it", () => {
assert.ok(errorsOf(validEnvelope({ extra: 1 } as never)).includes('envelope: unexpected key "extra"'));
});

test("validateAttestationEnvelope requires schemaVersion literal 1", () => {
assert.ok(errorsOf(validEnvelope({ schemaVersion: 2 })).includes("schemaVersion: must be the literal 1"));
assert.ok(errorsOf(validEnvelope({ schemaVersion: "1" })).includes("schemaVersion: must be the literal 1"));
});

test("validateAttestationEnvelope accepts both teeTechnology values, rejects others", () => {
assert.equal(validateAttestationEnvelope(validEnvelope({ teeTechnology: "tdx" })).valid, true);
assert.ok(errorsOf(validEnvelope({ teeTechnology: "sgx" })).includes('teeTechnology: must be "sev-snp" or "tdx"'));
});

test("validateAttestationEnvelope enforces runtimeClass (empty/non-string/too-long/boundary)", () => {
assert.ok(errorsOf(validEnvelope({ runtimeClass: "" })).includes("runtimeClass: must be a non-empty string of at most 128 chars"));
assert.ok(errorsOf(validEnvelope({ runtimeClass: 5 })).includes("runtimeClass: must be a non-empty string of at most 128 chars"));
assert.ok(errorsOf(validEnvelope({ runtimeClass: "a".repeat(129) })).includes("runtimeClass: must be a non-empty string of at most 128 chars"));
assert.equal(validateAttestationEnvelope(validEnvelope({ runtimeClass: "a".repeat(128) })).valid, true);
});

test("validateAttestationEnvelope enforces measurement 32-128 lowercase hex", () => {
assert.equal(validateAttestationEnvelope(validEnvelope({ measurement: "a".repeat(32) })).valid, true);
assert.equal(validateAttestationEnvelope(validEnvelope({ measurement: "a".repeat(128) })).valid, true);
for (const bad of ["a".repeat(31), "a".repeat(129), "A".repeat(64), "g".repeat(64), 123]) {
assert.ok(errorsOf(validEnvelope({ measurement: bad })).includes("measurement: must be 32-128 lowercase hex chars"));
}
});

test("validateAttestationEnvelope enforces reportData exactly-64 lowercase hex", () => {
for (const bad of ["b".repeat(63), "b".repeat(65), "B".repeat(64), 64]) {
assert.ok(errorsOf(validEnvelope({ reportData: bad })).includes("reportData: must be exactly 64 lowercase hex chars"));
}
});

test("validateAttestationEnvelope enforces attestationReport base64 (empty/too-long/non-base64/non-string/boundary)", () => {
for (const bad of ["", "A".repeat(65537), "not base64!", 7]) {
assert.ok(errorsOf(validEnvelope({ attestationReport: bad })).includes("attestationReport: must be non-empty base64 of at most 65536 chars"));
}
assert.equal(validateAttestationEnvelope(validEnvelope({ attestationReport: "A".repeat(65536) })).valid, true);
});

test("validateAttestationEnvelope validates the verification discriminated union", () => {
assert.ok(errorsOf(validEnvelope({ verification: "unverified" })).includes("verification: must be an object"));
assert.ok(errorsOf(validEnvelope({ verification: { status: "maybe" } })).includes('verification.status: must be "unverified", "verified", or "failed"'));
assert.equal(validateAttestationEnvelope(validEnvelope({ verification: { status: "unverified" } })).valid, true);
assert.equal(validateAttestationEnvelope(validEnvelope({ verification: { status: "verified", verifierId: "v1", verifiedAt: "2026-07-24T00:00:00.000Z" } })).valid, true);
assert.equal(validateAttestationEnvelope(validEnvelope({ verification: { status: "failed", verifierId: "v1", verifiedAt: "2026-07-24T00:00:00+00:00", reason: "mismatch" } })).valid, true);
assert.ok(errorsOf(validEnvelope({ verification: { status: "verified", verifiedAt: "2026-07-24T00:00:00Z" } })).includes("verification.verifierId: must be a non-empty string"));
assert.ok(errorsOf(validEnvelope({ verification: { status: "verified", verifierId: "v1", verifiedAt: "not-a-date" } })).includes("verification.verifiedAt: must be an ISO-8601 datetime"));
assert.ok(errorsOf(validEnvelope({ verification: { status: "verified", verifierId: "v1", verifiedAt: "2026-13-99T00:00:00Z" } })).includes("verification.verifiedAt: must be an ISO-8601 datetime"));
assert.ok(errorsOf(validEnvelope({ verification: { status: "failed", verifierId: "v1", verifiedAt: "2026-07-24T00:00:00Z" } })).includes("verification.reason: must be a non-empty string"));
});
Loading
Loading