Skip to content
Open
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
29 changes: 29 additions & 0 deletions docs/ga/CACERT_VERIFICATION_NETWORK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# CACert Verification Network (CEN-V)

This document defines the external CACert verification network for deterministic admissibility checks outside Summit internal systems.

## Verification flow

1. Input CACert and verification_request.json.
2. Validate signature against trust anchors (Cosign-compatible public key distribution).
3. Validate evidence hashes for report.json, metrics.json, stamp.json, and decision trace references.
4. Validate admissibility verdict consistency and validity window.
5. Emit verification_response.json with deterministic reasons.

## Components

- **Verification service** (read-only): `POST /api/v1/cacert/verify` and `POST /api/v1/cacert/verify/offline`.
- **Public key distribution**: static trust-anchor bundle keyed by `key_id`, with revocation list support.
- **Evidence reference validation**: URI-to-sha256 checks over portable artifacts, no internal API dependency.
- **Offline verifier**: `cacert-verify` CLI for air-gapped validation.

## Trust model

- Summit admissibility authority signs CACert using Ed25519 (`key_id` identifies trust anchor).
- External verifiers independently resolve public key trust anchors and revocation sets.
- Trust anchors are distributed via signed bundles and pinned by hash in verifier configuration.
- CACert validity depends only on signed certificate fields + referenced artifact digests.

## Failure guarantees

CEN-V never returns false PASS. Any signature, evidence, decision trace, key status, or validity-window anomaly returns FAIL with structured reason codes.
1 change: 1 addition & 0 deletions docs/ga/MVP-4-GA-VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This sweep captures the minimum credible verification for the GA-hardening surfa
| Media Authenticity & Provenance | `scripts/ci/verify_media_provenance.ts` + `docs/governance/media_provenance.md` | B | `make ga-verify` | Blocks marketing/public media changes without deterministic provenance evidence. |
| GA Exit Criteria Scorecard | `docs/ga/GA_EXIT_CRITERIA_v1.md` (binary release gate, weighted GA Readiness Index, and acceptance criteria) | C | `make ga-verify` | Anchors GA promotion to an explicit scorecard with auditable acceptance tests and status tracking. |
| GA Release Artifact Convergence | `scripts/release/generate-release-artifacts.mjs` + `scripts/release/verify-release.mjs` + `.github/workflows/release-integrity.yml` | A | `node scripts/release/verify-release.mjs` | Produces deterministic release surface, manifest, SBOM, provenance, and fails CI on any integrity or reproducibility drift. |
| CACert Verification Network | `packages/cacert/src/verification.ts` + `packages/cacert/tools/cacert-verify.mjs` + `packages/cacert/schema/verification_response.schema.json` | B | `pnpm --filter @summit/cacert test` | Verifies portable CACert signature validation, evidence integrity, replay/key revocation detection, and deterministic FAIL responses for external auditors. |
| MVP GA Pilot Gate Artifacts | `scripts/ga/verify-mvp-ga-pilot-gate.mjs` + `testing/ga-verification/mvp-ga-pilot-docs.ga.test.mjs` + `.github/workflows/mvp-ga-pilot-gate.yml` | B | `make ga-verify` | Enforces MVP pilot cut line/remediation/evidence docs, gate checklist JSON, runbook links, and demo script as non-optional release artifacts. |

## Deferred / To-Improve Items
Expand Down
17 changes: 17 additions & 0 deletions docs/ga/verification-map.json
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,22 @@
],
"keyword": "rate limiting",
"notes": "Documentation-driven contract while runtime harness is stabilized."
},
{
"feature": "CACert Verification Network",
"tier": "B",
"artifact": "packages/cacert/src/verification.ts",
"ciStatus": "wired",
"evidence": [
"docs/ga/CACERT_VERIFICATION_NETWORK.md",
"packages/cacert/schema/verification_response.schema.json",
"packages/cacert/tools/cacert-verify.mjs"
],
"keywords": [
"CACert",
"admissibility_status",
"verifyCACert"
],
"notes": "Externalizes CACert into an independently verifiable network with offline CLI verification and deterministic structured failure codes."
}
]
7 changes: 6 additions & 1 deletion packages/cacert/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"cacert-verify": "tools/cacert-verify.mjs"
},
"scripts": {
"build": "tsc",
"test": "npx tsx --test tests/*.test.ts"
"test": "npx tsx --test tests/*.test.ts",
"verify": "node ./tools/cacert-verify.mjs"
},
"devDependencies": {
"@types/node": "^22.0.0",
"tsx": "^4.20.6",
"typescript": "^5.9.3"
}
}
58 changes: 56 additions & 2 deletions packages/cacert/schema/cacert.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,44 @@
"title": "CACert",
"type": "object",
"additionalProperties": false,
"required": ["cert_version", "verdict", "evidence_hash", "trace_ref", "policy_refs"],
"required": [
"cert_version",
"cert_id",
"issuer",
"key_id",
"issued_at",
"expires_at",
"verdict",
"evidence_hash",
"trace_ref",
"policy_refs",
"evidence",
"signature"
],
"properties": {
"cert_version": {
"type": "string",
"const": "1.0"
"const": "2.0"
},
"cert_id": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"issuer": {
"type": "string",
"minLength": 1
},
"key_id": {
"type": "string",
"minLength": 1
},
"issued_at": {
"type": "string",
"format": "date-time"
},
"expires_at": {
"type": "string",
"format": "date-time"
},
"verdict": {
"type": "string",
Expand All @@ -26,6 +59,27 @@
"items": {
"type": "string"
}
},
"evidence": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["uri", "sha256"],
"properties": {
"uri": { "type": "string", "minLength": 1 },
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
}
}
},
"signature": {
"type": "object",
"additionalProperties": false,
"required": ["algorithm", "value"],
"properties": {
"algorithm": { "type": "string", "const": "ed25519" },
"value": { "type": "string" }
}
}
}
}
33 changes: 33 additions & 0 deletions packages/cacert/schema/verification_request.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "verification_request",
"type": "object",
"additionalProperties": false,
"required": ["cert", "evidence_hashes", "trust_anchors"],
"properties": {
"cert": { "$ref": "./cacert.schema.json" },
"evidence_hashes": {
"type": "object",
"additionalProperties": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
}
},
"trace_hashes": {
"type": "object",
"additionalProperties": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
}
},
"now": { "type": "string", "format": "date-time" },
"revoked_keys": {
"type": "array",
"items": { "type": "string" }
},
"trust_anchors": {
"type": "object",
"additionalProperties": { "type": "string", "minLength": 1 }
}
}
}
20 changes: 20 additions & 0 deletions packages/cacert/schema/verification_response.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "verification_response",
"type": "object",
"additionalProperties": false,
"required": ["cert_id", "valid_signature", "evidence_integrity", "admissibility_status", "reasons"],
"properties": {
"cert_id": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"valid_signature": { "type": "boolean" },
"evidence_integrity": { "type": "boolean" },
"admissibility_status": { "type": "string", "enum": ["PASS", "FAIL"] },
"reasons": {
"type": "array",
"items": { "type": "string" }
}
}
}
86 changes: 81 additions & 5 deletions packages/cacert/src/cacert.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { createHash } from 'node:crypto';

export type CACertVerdict = 'PASS' | 'FAIL';

export interface CACertVerdictBundle {
Expand All @@ -11,22 +13,96 @@ export interface CACertBundle {
verdict: CACertVerdictBundle;
hash: string;
trace_ref?: string;
issuedAt?: string;
expiresAt?: string;
issuer?: string;
keyId?: string;
signature?: string;
}

export interface CACertEvidenceRef {
uri: string;
sha256: string;
}

export interface CACert {
cert_version: '1.0';
export interface CACertUnsigned {
cert_version: '2.0';
issuer: string;
key_id: string;
issued_at: string;
expires_at: string;
verdict: CACertVerdict;
evidence_hash: string;
trace_ref: string;
policy_refs: string[];
evidence: CACertEvidenceRef[];
}

export function generateCACert(bundle: CACertBundle): CACert {
export interface CACert extends CACertUnsigned {
cert_id: string;
signature: {
algorithm: 'ed25519';
value: string;
};
}

export function sha256Hex(value: string): string {
return createHash('sha256').update(value).digest('hex');
}

export function canonicalize(value: unknown): string {
if (value === null || typeof value !== 'object') {
return JSON.stringify(value);
}

if (Array.isArray(value)) {
return `[${value.map((item) => canonicalize(item)).join(',')}]`;
}

const entries = Object.entries(value as Record<string, unknown>).sort(([a], [b]) =>
a.localeCompare(b),
);

return `{${entries
.map(([key, val]) => `${JSON.stringify(key)}:${canonicalize(val)}`)
.join(',')}}`;
}

export function buildUnsignedCACert(bundle: CACertBundle): CACertUnsigned {
return {
cert_version: '1.0',
cert_version: '2.0',
issuer: bundle.issuer ?? 'summit://admissibility-authority',
key_id: bundle.keyId ?? 'summit-root-ed25519-v1',
issued_at: bundle.issuedAt ?? '2026-01-01T00:00:00.000Z',
expires_at: bundle.expiresAt ?? '2027-01-01T00:00:00.000Z',
verdict: bundle.verdict.verdict,
evidence_hash: bundle.hash,
trace_ref: bundle.trace_ref ?? 'embedded',
policy_refs: [...bundle.verdict.policy_failures],
policy_refs: [...bundle.verdict.policy_failures].sort(),
evidence: [...bundle.verdict.evidence_refs]
.sort((a, b) => a.localeCompare(b))
.map((uri) => ({ uri, sha256: bundle.hash })),
};
}

export function computeCACertId(unsignedCert: CACertUnsigned): string {
return sha256Hex(canonicalize(unsignedCert));
}

export function buildSigningPayload(cert: Pick<CACert, 'cert_id'> & CACertUnsigned): string {
return canonicalize(cert);
}

export function generateCACert(bundle: CACertBundle): CACert {
const unsigned = buildUnsignedCACert(bundle);
const cert_id = computeCACertId(unsigned);

return {
cert_id,
...unsigned,
signature: {
algorithm: 'ed25519',
value: bundle.signature ?? '',
},
};
}
1 change: 1 addition & 0 deletions packages/cacert/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './cacert.js';
export * from './verification.js';
Loading
Loading