mirror of
https://github.com/saymrwulf/proof-aware-crypto-tooling-agent.git
synced 2026-09-05 20:10:34 +00:00
course refresh: the notebooks enter the SLH-DSA era
The 14-notebook course predated the post-quantum campaign entirely (coherence findings 10, 11). Now, authored in the GENERATOR and regenerated (AGENTS.md rule): - notebook 06: new section 'The second signature that actually shipped: SLH-DSA' — the deterministic co-signature since tree size 14, chosen because the log attests its own parameter set's verify path (leaf 18, 11 certs); absent-not-failed for older heads; determinism as an audit primitive; verify-only always. Plus a runnable keygen/sign/verify/ re-sign-byte-equality demo (honest skip below OpenSSL 3.5) and the --slhdsa-public-key consumer flag in the policy list. - notebook 09: the 'post-quantum line' is now three-legged — Ed25519 proven-verify dogfood, SLH-DSA shipped-and-attested, ML-DSA required- but-honest-unavailable — with the closing point that a slot stops being aspirational the day its verify path enters the log; stale 16/16 provenance count -> 44/44 (leaf 13 re-attestation). - notebook 07: policy exercise extended with the co-signature question; 00 course map goal updated; README course listing for 06/09. - GENERATOR DRIFT REPAIRED in passing: notebook 10's cockpit cell had been added to the .ipynb but never backported to the generator — regeneration would have silently dropped it; the cell is now IN the generator and round-trips (19 cells, content identical). Suite 157 green.
This commit is contained in:
parent
31a9dee10a
commit
1086a3ba02
8 changed files with 893 additions and 801 deletions
|
|
@ -125,11 +125,11 @@ The `notebooks/` directory contains a zero-to-hero teaching sequence for undergr
|
|||
- `03_lean_replay_and_axiom_audit.ipynb`: replay versus transpilation, Lean invocation, axiom audits.
|
||||
- `04_proof_hygiene_and_boundaries.ipynb`: `sorry`, local axioms, trivial targets, manifest coverage.
|
||||
- `05_third_party_attestation_provider.ipynb`: provider trust transformation and signed attestations.
|
||||
- `06_merkle_transparency_logs.ipynb`: RFC 9162-style Merkle proofs, STHs, Ed25519/ML-DSA policy.
|
||||
- `06_merkle_transparency_logs.ipynb`: RFC 9162-style Merkle proofs, STHs, the dual-signature head policy (Ed25519 + the shipped SLH-DSA co-signature) and the fail-closed ML-DSA slot.
|
||||
- `06a_provider_build_the_log.ipynb` / `06b_agent_verify_inclusion.ipynb`: the MIRRORED PAIR - one provider (builds, Lean-verifies, signs with the merkleized library, self-checks its own inclusion), many agents (verify inclusion from scratch in ~25 lines, no Lean); the domain separation is the design and the lecture structure mirrors it.
|
||||
- `07_agent_consequences.ipynb`: receipt-gated artifact builds and wallet-denial policy.
|
||||
- `08_capstone_research_program.ipynb`: audit the shipped R4 evidence; design the R5 discharge plan.
|
||||
- `09_dogfood_verified_crypto.ipynb`: the proven-path verifier in the agent's own loop; hybrid-PQC posture.
|
||||
- `09_dogfood_verified_crypto.ipynb`: the proven-path verifier in the agent's own loop; the three-legged PQ posture (Ed25519 dogfood, shipped SLH-DSA, honest ML-DSA slot).
|
||||
- `10_verified_custody_wallet.ipynb`: warden - the quorum custody boundary and signing firewall, ratchet-rule (toy 3-of-3, then the real four proven forks), plus the counterparty recomputing a custody card's inclusion proof.
|
||||
- `11_the_customers_eye_view.ipynb`: the counterparty's seat - what a customer can and cannot recompute about someone else's wallet from its custody card and the public log.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,188 +1,168 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# PACTA Curriculum: From Zero to Hero\n",
|
||||
"\n",
|
||||
"This curriculum teaches proof-aware cryptographic tooling from first principles to a research-grade professional workflow. It is designed for undergraduate students who know some programming and discrete math, but have not yet worked with formal verification, Lean, certificate transparency, or autonomous-agent risk gates.\n",
|
||||
"\n",
|
||||
"The practical anchor is PACTA: Proof-Aware Crypto Tooling Agent. The goal is not to build a trading bot. The goal is to teach an agent, and the engineer supervising it, to ask:\n",
|
||||
"\n",
|
||||
"> Does this theorem cover the exact code path that will protect funds?\n",
|
||||
"\n",
|
||||
"The course takes that question seriously. Every notebook connects theory to a runnable artifact in this repository.\n"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# PACTA Curriculum: From Zero to Hero\n",
|
||||
"\n",
|
||||
"This curriculum teaches proof-aware cryptographic tooling from first principles to a research-grade professional workflow. It is designed for undergraduate students who know some programming and discrete math, but have not yet worked with formal verification, Lean, certificate transparency, or autonomous-agent risk gates.\n",
|
||||
"\n",
|
||||
"The practical anchor is PACTA: Proof-Aware Crypto Tooling Agent. The goal is not to build a trading bot. The goal is to teach an agent, and the engineer supervising it, to ask:\n",
|
||||
"\n",
|
||||
"> Does this theorem cover the exact code path that will protect funds?\n",
|
||||
"\n",
|
||||
"The course takes that question seriously. Every notebook connects theory to a runnable artifact in this repository.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Learning Objectives\n",
|
||||
"\n",
|
||||
"By the end of the sequence, a strong student should be able to:\n",
|
||||
"\n",
|
||||
"- Explain why cryptographic implementation proofs have theorem boundaries.\n",
|
||||
"- Distinguish formal proof evidence from tests, audits, marketing claims, and operational controls.\n",
|
||||
"- Read a PACTA claim card and understand its guarantees, preconditions, exclusions, trusted base, and risk level.\n",
|
||||
"- Reproduce a local Lean replay or diagnose why local replay is unavailable.\n",
|
||||
"- Perform a proof hygiene scan and explain why `sorry`, local axioms, and trivial theorem targets are dangerous.\n",
|
||||
"- Explain how a third-party proof-checking provider changes the trusted base.\n",
|
||||
"- Implement and verify RFC 9162-style Merkle inclusion and consistency proofs.\n",
|
||||
"- Explain why Signed Tree Heads need accountable signatures, why Ed25519 is useful here, why ML-DSA requires a real backend, and how the shipped SLH-DSA co-signature differs from both.\n",
|
||||
"- Design policy gates that convert verification evidence into consequences.\n",
|
||||
"- Read R4 four-tier apex evidence, name its residual blockers, and write a research plan toward R5 production assurance.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Prerequisites\n",
|
||||
"\n",
|
||||
"Recommended background:\n",
|
||||
"\n",
|
||||
"- Python basics: functions, dictionaries, lists, files, subprocesses.\n",
|
||||
"- Discrete math: modular arithmetic, induction, trees, hashes.\n",
|
||||
"- Basic cryptography vocabulary: public keys, signatures, hashes, finite fields.\n",
|
||||
"- Basic command-line usage on macOS or Linux.\n",
|
||||
"\n",
|
||||
"Not required at the start:\n",
|
||||
"\n",
|
||||
"- Lean.\n",
|
||||
"- Rust internals.\n",
|
||||
"- Elliptic curve implementation expertise.\n",
|
||||
"- Certificate transparency expertise.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pathlib import Path\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"repo_root = Path.cwd()\n",
|
||||
"if not (repo_root / \"src\" / \"pacta\").exists():\n",
|
||||
" repo_root = repo_root.parent\n",
|
||||
"sys.path.insert(0, str(repo_root / \"src\"))\n",
|
||||
"\n",
|
||||
"print(repo_root)\n",
|
||||
"print((repo_root / \"README.md\").exists())\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Course Map\n",
|
||||
"\n",
|
||||
"1. `01_threat_model_and_truth_boundary.ipynb`\n",
|
||||
" Learn the product problem, the security boundary, and the difference between verified arithmetic and verified wallets.\n",
|
||||
"\n",
|
||||
"2. `02_claim_cards_and_risk_model.ipynb`\n",
|
||||
" Study PACTA claim cards, risk levels R0-R5, and how claim serialization supports machine decisions.\n",
|
||||
"\n",
|
||||
"3. `03_lean_replay_and_axiom_audit.ipynb`\n",
|
||||
" Learn how local Lean replay works, why PACTA avoids transpilation, and what an axiom audit proves.\n",
|
||||
"\n",
|
||||
"4. `04_proof_hygiene_and_boundaries.ipynb`\n",
|
||||
" Learn to scan proof artifacts for `sorry`, local `axiom`, trivial theorem statements, and missing manifest coverage.\n",
|
||||
"\n",
|
||||
"5. `05_third_party_attestation_provider.ipynb`\n",
|
||||
" Learn how a proof-checking service can transform hard local verification into provider trust.\n",
|
||||
"\n",
|
||||
"6. `06_merkle_transparency_logs.ipynb`, then the MIRRORED PAIR\n",
|
||||
" `06a_provider_build_the_log.ipynb` / `06b_agent_verify_inclusion.ipynb`\n",
|
||||
"\n",
|
||||
" The trust architecture has exactly two domains - ONE provider\n",
|
||||
" who builds and signs the authenticated structure (and pays the\n",
|
||||
" Lean bill), MANY agents who verify inclusion proofs in\n",
|
||||
" milliseconds. The course mirrors that split structurally: 6a is\n",
|
||||
" written entirely in the provider's voice, 6b entirely in the\n",
|
||||
" agent's. If you cannot say which notebook a step belongs to,\n",
|
||||
" you have not understood the step.\n",
|
||||
" Build the Merkle accumulator intuition behind inclusion proofs, consistency proofs, and Signed Tree Heads.\n",
|
||||
"\n",
|
||||
"7. `07_agent_consequences.ipynb`\n",
|
||||
" Connect evidence to action: build a lower-layer Rust capsule only when policy gates pass.\n",
|
||||
"\n",
|
||||
"8. `08_capstone_research_program.ipynb`\n",
|
||||
" Design a PhD-level roadmap for closing the gaps from R4 toward R5.\n",
|
||||
"\n",
|
||||
"9. `09_dogfood_verified_crypto.ipynb`\n",
|
||||
"\n",
|
||||
" Eat your own dogfood: run the agent's own signature checks through the proven code path, and hold the post-quantum line honestly.\n",
|
||||
"\n",
|
||||
"## The Ratchet Rule\n",
|
||||
"\n",
|
||||
"This course follows the same didactic contract as its companion book (*Verifying Cryptography with Lean 4*): **every load-bearing idea is worked at least twice** - once at napkin scale (trees of three leaves, toy claim cards you can score in your head) and once at real scale (the shipped R4 claim cards, real receipts, the real proven-path verifier), with nothing hidden in between. The napkin run teaches the moves; the real-size run proves the moves are the whole story. If a step ever feels like a leap, back up one cell: the smaller rung is there.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Assessment Model\n",
|
||||
"\n",
|
||||
"Each notebook contains:\n",
|
||||
"\n",
|
||||
"- A lecture section for concepts.\n",
|
||||
"- A lab section with runnable code.\n",
|
||||
"- Checkpoints that force precise answers.\n",
|
||||
"- Exercises for mastery.\n",
|
||||
"- Research prompts for advanced students.\n",
|
||||
"\n",
|
||||
"The capstone asks students to produce a defensible assurance case, not a slogan.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## References\n",
|
||||
"\n",
|
||||
"- RFC 9162, Certificate Transparency Version 2.0: https://datatracker.ietf.org/doc/html/rfc9162\n",
|
||||
"- RFC 8032, Edwards-Curve Digital Signature Algorithm: https://datatracker.ietf.org/doc/html/rfc8032\n",
|
||||
"- NIST FIPS 204, Module-Lattice-Based Digital Signature Standard: https://csrc.nist.gov/pubs/fips/204/final\n",
|
||||
"- PACTA README: `../README.md`\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Learning Objectives\n",
|
||||
"\n",
|
||||
"By the end of the sequence, a strong student should be able to:\n",
|
||||
"\n",
|
||||
"- Explain why cryptographic implementation proofs have theorem boundaries.\n",
|
||||
"- Distinguish formal proof evidence from tests, audits, marketing claims, and operational controls.\n",
|
||||
"- Read a PACTA claim card and understand its guarantees, preconditions, exclusions, trusted base, and risk level.\n",
|
||||
"- Reproduce a local Lean replay or diagnose why local replay is unavailable.\n",
|
||||
"- Perform a proof hygiene scan and explain why `sorry`, local axioms, and trivial theorem targets are dangerous.\n",
|
||||
"- Explain how a third-party proof-checking provider changes the trusted base.\n",
|
||||
"- Implement and verify RFC 9162-style Merkle inclusion and consistency proofs.\n",
|
||||
"- Explain why Signed Tree Heads need accountable signatures, why Ed25519 is useful here, and why ML-DSA requires a real backend.\n",
|
||||
"- Design policy gates that convert verification evidence into consequences.\n",
|
||||
"- Read R4 four-tier apex evidence, name its residual blockers, and write a research plan toward R5 production assurance.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Prerequisites\n",
|
||||
"\n",
|
||||
"Recommended background:\n",
|
||||
"\n",
|
||||
"- Python basics: functions, dictionaries, lists, files, subprocesses.\n",
|
||||
"- Discrete math: modular arithmetic, induction, trees, hashes.\n",
|
||||
"- Basic cryptography vocabulary: public keys, signatures, hashes, finite fields.\n",
|
||||
"- Basic command-line usage on macOS or Linux.\n",
|
||||
"\n",
|
||||
"Not required at the start:\n",
|
||||
"\n",
|
||||
"- Lean.\n",
|
||||
"- Rust internals.\n",
|
||||
"- Elliptic curve implementation expertise.\n",
|
||||
"- Certificate transparency expertise.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pathlib import Path\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"repo_root = Path.cwd()\n",
|
||||
"if not (repo_root / \"src\" / \"pacta\").exists():\n",
|
||||
" repo_root = repo_root.parent\n",
|
||||
"sys.path.insert(0, str(repo_root / \"src\"))\n",
|
||||
"\n",
|
||||
"print(repo_root)\n",
|
||||
"print((repo_root / \"README.md\").exists())\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Course Map\n",
|
||||
"\n",
|
||||
"1. `01_threat_model_and_truth_boundary.ipynb`\n",
|
||||
" Learn the product problem, the security boundary, and the difference between verified arithmetic and verified wallets.\n",
|
||||
"\n",
|
||||
"2. `02_claim_cards_and_risk_model.ipynb`\n",
|
||||
" Study PACTA claim cards, risk levels R0-R5, and how claim serialization supports machine decisions.\n",
|
||||
"\n",
|
||||
"3. `03_lean_replay_and_axiom_audit.ipynb`\n",
|
||||
" Learn how local Lean replay works, why PACTA avoids transpilation, and what an axiom audit proves.\n",
|
||||
"\n",
|
||||
"4. `04_proof_hygiene_and_boundaries.ipynb`\n",
|
||||
" Learn to scan proof artifacts for `sorry`, local `axiom`, trivial theorem statements, and missing manifest coverage.\n",
|
||||
"\n",
|
||||
"5. `05_third_party_attestation_provider.ipynb`\n",
|
||||
" Learn how a proof-checking service can transform hard local verification into provider trust.\n",
|
||||
"\n",
|
||||
"6. `06_merkle_transparency_logs.ipynb`, then the MIRRORED PAIR\n",
|
||||
" `06a_provider_build_the_log.ipynb` / `06b_agent_verify_inclusion.ipynb`\n",
|
||||
"\n",
|
||||
" The trust architecture has exactly two domains - ONE provider\n",
|
||||
" who builds and signs the authenticated structure (and pays the\n",
|
||||
" Lean bill), MANY agents who verify inclusion proofs in\n",
|
||||
" milliseconds. The course mirrors that split structurally: 6a is\n",
|
||||
" written entirely in the provider's voice, 6b entirely in the\n",
|
||||
" agent's. If you cannot say which notebook a step belongs to,\n",
|
||||
" you have not understood the step.\n",
|
||||
" Build the Merkle accumulator intuition behind inclusion proofs, consistency proofs, and Signed Tree Heads.\n",
|
||||
"\n",
|
||||
"7. `07_agent_consequences.ipynb`\n",
|
||||
" Connect evidence to action: build a lower-layer Rust capsule only when policy gates pass.\n",
|
||||
"\n",
|
||||
"8. `08_capstone_research_program.ipynb`\n",
|
||||
" Design a PhD-level roadmap for closing the gaps from R4 toward R5.\n",
|
||||
"\n",
|
||||
"9. `09_dogfood_verified_crypto.ipynb`\n",
|
||||
"\n",
|
||||
" Eat your own dogfood: run the agent's own signature checks through the proven code path, and hold the post-quantum line honestly.\n",
|
||||
"\n",
|
||||
"## The Ratchet Rule\n",
|
||||
"\n",
|
||||
"This course follows the same didactic contract as its companion book (*Verifying Cryptography with Lean 4*): **every load-bearing idea is worked at least twice** - once at napkin scale (trees of three leaves, toy claim cards you can score in your head) and once at real scale (the shipped R4 claim cards, real receipts, the real proven-path verifier), with nothing hidden in between. The napkin run teaches the moves; the real-size run proves the moves are the whole story. If a step ever feels like a leap, back up one cell: the smaller rung is there.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Assessment Model\n",
|
||||
"\n",
|
||||
"Each notebook contains:\n",
|
||||
"\n",
|
||||
"- A lecture section for concepts.\n",
|
||||
"- A lab section with runnable code.\n",
|
||||
"- Checkpoints that force precise answers.\n",
|
||||
"- Exercises for mastery.\n",
|
||||
"- Research prompts for advanced students.\n",
|
||||
"\n",
|
||||
"The capstone asks students to produce a defensible assurance case, not a slogan.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## References\n",
|
||||
"\n",
|
||||
"- RFC 9162, Certificate Transparency Version 2.0: https://datatracker.ietf.org/doc/html/rfc9162\n",
|
||||
"- RFC 8032, Edwards-Curve Digital Signature Algorithm: https://datatracker.ietf.org/doc/html/rfc8032\n",
|
||||
"- NIST FIPS 204, Module-Lattice-Based Digital Signature Standard: https://csrc.nist.gov/pubs/fips/204/final\n",
|
||||
"- PACTA README: `../README.md`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The course's operational twin: the cockpit and the lab manual\n",
|
||||
"\n",
|
||||
"These notebooks are the *code-level* track: you build the machinery with your own hands.\n",
|
||||
"Two sibling surfaces teach the *operational* track on the very same system:\n",
|
||||
"\n",
|
||||
"- **The custody cockpit** — `pacta wallet cockpit --demo` serves a read-only bridge of six\n",
|
||||
" role stations (proposer, quorum bench, operator, cryptographer, architect, newcomer) over\n",
|
||||
" live evidence, with a tmux-style deck (`/deck`) showing every station in parallel.\n",
|
||||
"- **The lab manual** (`docs/warden-lab-manual.md`, served at `/manual`) — a study-club course\n",
|
||||
" that teaches each *role* hands-on: labs with checkpoints, a safe ledger-tamper drill,\n",
|
||||
" self-tests, and a capstone incident walked through all six chairs.\n",
|
||||
"\n",
|
||||
"Recommended weave: notebooks 00–09 first (build it), then the lab manual (run it as a crew),\n",
|
||||
"with notebook 10 and the manual's warden sessions read side by side."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@
|
|||
"Policy matters:\n",
|
||||
"\n",
|
||||
"- `require-signatures ed25519`: verify Ed25519 and allow ML-DSA to be unavailable.\n",
|
||||
"- `require-signatures both`: require Ed25519 and ML-DSA verified. If ML-DSA is unavailable, fail closed.\n"
|
||||
"- `require-signatures both`: require Ed25519 and ML-DSA verified. If ML-DSA is unavailable, fail closed.\n",
|
||||
"- `--slhdsa-public-key <pem>`: additionally verify the second (post-quantum) SLH-DSA co-signature on the head; heads before tree size 14 report `absent` (allowed), a present-but-wrong signature fails closed.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -156,6 +157,59 @@
|
|||
"ML-DSA adds post-quantum robustness for the accumulator signature layer. But it must be a real signature, not an aspirational label. If a host lacks ML-DSA, the correct result is an explicit blocker.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The second signature that actually shipped: SLH-DSA\n",
|
||||
"\n",
|
||||
"Since tree size 14, every head of the LIVE log carries a second,\n",
|
||||
"deterministic **SLH-DSA-SHA2-128s** (FIPS 205) signature beside the\n",
|
||||
"required Ed25519 one. This is not the ML-DSA slot above - it is a\n",
|
||||
"hash-based scheme, and it was chosen because the estate has PROVEN\n",
|
||||
"its verify path (eleven certificates, log leaf 18): the log\n",
|
||||
"co-signs with the parameter set whose verification path it itself\n",
|
||||
"attests. Three design facts worth internalizing:\n",
|
||||
"\n",
|
||||
"1. Heads published before size 14 carry no co-signature, and\n",
|
||||
" verifiers report them `ABSENT` rather than failing them - an\n",
|
||||
" append-only log keeps the history of its own signature-scheme\n",
|
||||
" upgrades.\n",
|
||||
"2. The co-signature is deterministic on purpose: re-signing the\n",
|
||||
" same payload is byte-comparable, so \"same input, same\n",
|
||||
" signature\" becomes a diff you can run, not an assurance you\n",
|
||||
" must trust.\n",
|
||||
"3. Signing is still never proven - here, as everywhere in this\n",
|
||||
" estate, certificates cover the VERIFY path only.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Runnable where OpenSSL >= 3.5 is present; honest skip otherwise.\n",
|
||||
"import tempfile\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"from pacta import slhdsa\n",
|
||||
"\n",
|
||||
"tmp = Path(tempfile.mkdtemp(prefix=\"nb06-slhdsa-\"))\n",
|
||||
"try:\n",
|
||||
" slhdsa.generate_slhdsa_keypair(tmp / \"slh.key\", tmp / \"slh.pub\")\n",
|
||||
"except Exception as exc:\n",
|
||||
" print(\"SLH-DSA unavailable on this host (OpenSSL >= 3.5 needed):\", exc)\n",
|
||||
"else:\n",
|
||||
" payload = b\"canonical STH payload bytes\"\n",
|
||||
" block = slhdsa.slh_dsa_signature_block(payload, tmp / \"slh.key\", tmp / \"slh.pub\")\n",
|
||||
" ok, err = slhdsa.verify_payload_slhdsa(payload, block[\"signature_base64\"], tmp / \"slh.pub\")\n",
|
||||
" print(\"co-signature verifies:\", ok, err or \"\")\n",
|
||||
" block2 = slhdsa.slh_dsa_signature_block(payload, tmp / \"slh.key\", tmp / \"slh.pub\")\n",
|
||||
" print(\"deterministic (byte-equal re-sign):\",\n",
|
||||
" block[\"signature_base64\"] == block2[\"signature_base64\"])\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@
|
|||
"- Modify a claim card to R2 and show that `build-library` is refused.\n",
|
||||
"- Explain why a denial artifact is useful for auditability.\n",
|
||||
"- Design a policy where an agent requires `both` Ed25519 and ML-DSA signatures for production deployment but allows Ed25519-only in a local lab.\n",
|
||||
"- Extend it: when should the agent also require the SLH-DSA co-signature, given that heads before tree size 14 legitimately lack it?\n",
|
||||
"- Write a downstream Rust pseudo-code snippet that imports the generated capsule before enabling a code path.\n"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"- State precisely which parts of the dogfood verifier are certificate-covered and which are its trusted base.\n",
|
||||
"- Extract a raw Ed25519 key from an OpenSSL PEM by hand (napkin) and mechanically (real).\n",
|
||||
"- Demonstrate backend dispatch and the fail-closed `--require-verified-verifier` policy.\n",
|
||||
"- Defend the hybrid post-quantum posture: one proven-classical signature plus one required-but-honest ML-DSA slot.\n"
|
||||
"- Defend the three-legged post-quantum posture: proven-classical Ed25519, the shipped SLH-DSA co-signature with its attested verify path, and a required-but-honest ML-DSA slot.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -122,7 +122,8 @@
|
|||
"leaf, against the very tree it is about to sign. The verdict is\n",
|
||||
"embedded in the signature block (`signing_provenance`: backend,\n",
|
||||
"library commit, leaf index, `self_inclusion: verified`,\n",
|
||||
"certificates 16/16). Lectures 6a/6b walk both sides of this.\n",
|
||||
"certificates 44/44 - the signer's source family was re-attested at 44\n",
|
||||
"certificates as leaf 13). Lectures 6a/6b walk both sides of this.\n",
|
||||
"Honesty note unchanged: the library's VERIFY path is\n",
|
||||
"certificate-covered; the signing path is declared trusted base -\n",
|
||||
"but it is the attested artifact, not an un-attested third\n",
|
||||
|
|
@ -130,12 +131,13 @@
|
|||
"\n",
|
||||
"## The post-quantum line, held honestly\n",
|
||||
"\n",
|
||||
"The dogfood loop deliberately does NOT extend to ML-DSA. There is no formally verified ML-DSA implementation in this corpus, and pretending otherwise would poison the whole posture. The hybrid strategy is therefore asymmetric on purpose:\n",
|
||||
"The posture has three legs now, and each is exactly as strong as it claims:\n",
|
||||
"\n",
|
||||
"- **Ed25519 (classical): proven path.** The signature everyone can check today runs on certificate-covered code.\n",
|
||||
"- **ML-DSA-65 (post-quantum): required, honest, unavailable-until-real.** The tree-head slot exists in every signed structure; `--require-signatures both` fails CLOSED on hosts without a real FIPS 204 backend; and when a real backend lands, the policy flips on without a schema change.\n",
|
||||
"- **Ed25519 (classical): proven verify path, dogfooded.** The signature everyone can check today runs on certificate-covered code.\n",
|
||||
"- **SLH-DSA-SHA2-128s (post-quantum): shipped and attested.** Since tree size 14 every live head carries a second, deterministic SLH-DSA co-signature. The estate proved the VERIFY path of a pinned Rust FIPS 205 implementation (eleven certificates) and appended that attestation as leaf 18 - so the co-signature uses exactly the parameter set the log itself attests. Consumers check it with `pacta receipt-verify ... --slhdsa-public-key provider.slhdsa.pub` or the mirror's `verify.py`. Signing remains unproven - verify paths only, always.\n",
|
||||
"- **ML-DSA-65 (lattice PQ): required, honest, unavailable-until-real.** The tree-head slot exists in every signed structure; `--require-signatures both` fails CLOSED on hosts without a real FIPS 204 backend; when a real backend lands, the policy flips on without a schema change.\n",
|
||||
"\n",
|
||||
"A migration strategy that records \"we cannot do this yet\" as a deployment blocker is strictly stronger than one that ships a placeholder. Blockers get fixed; placeholders get trusted.\n"
|
||||
"A migration strategy that records \"we cannot do this yet\" as a deployment blocker is strictly stronger than one that ships a placeholder. Blockers get fixed; placeholders get trusted. And the SLH-DSA leg shows the endgame: a slot stops being aspirational the day its verify path enters the log.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,364 +1,364 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Lecture 10: The Verified-Custody Wallet (warden)\n",
|
||||
"\n",
|
||||
"Everything so far *decided* which cryptographic code to trust.\n",
|
||||
"This lecture *acts* on the decision: we build a custody boundary\n",
|
||||
"out of the four proven curve25519-dalek forks and use it to\n",
|
||||
"guard signatures - inbound and outbound.\n",
|
||||
"\n",
|
||||
"The one idea: **inbound acceptance requires a unanimous quorum of\n",
|
||||
"provably-equivalent verifiers, and every outbound signature must\n",
|
||||
"pass the same quorum before it is released.**\n",
|
||||
"\n",
|
||||
"We keep the course's ratchet rule: every load-bearing idea runs\n",
|
||||
"twice - napkin scale by hand, then real scale against the live\n",
|
||||
"system - and both are executable here.\n"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Lecture 10: The Verified-Custody Wallet (warden)\n",
|
||||
"\n",
|
||||
"Everything so far *decided* which cryptographic code to trust.\n",
|
||||
"This lecture *acts* on the decision: we build a custody boundary\n",
|
||||
"out of the four proven curve25519-dalek forks and use it to\n",
|
||||
"guard signatures - inbound and outbound.\n",
|
||||
"\n",
|
||||
"The one idea: **inbound acceptance requires a unanimous quorum of\n",
|
||||
"provably-equivalent verifiers, and every outbound signature must\n",
|
||||
"pass the same quorum before it is released.**\n",
|
||||
"\n",
|
||||
"We keep the course's ratchet rule: every load-bearing idea runs\n",
|
||||
"twice - napkin scale by hand, then real scale against the live\n",
|
||||
"system - and both are executable here.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Learning Objectives\n",
|
||||
"\n",
|
||||
"- Explain why a *unanimous* quorum of provably-equivalent\n",
|
||||
" verifiers turns disagreement into evidence of a fault, and why\n",
|
||||
" majority voting would hide exactly that fault.\n",
|
||||
"- Classify a quorum divergence as a documented semantic edge\n",
|
||||
" (note) versus unexplained (tamper -> latch).\n",
|
||||
"- Describe the outbound signing firewall as verify-after-sign\n",
|
||||
" with a proven verifier, and state warden's honest asymmetry\n",
|
||||
" (verify custody-grade, sign trusted base).\n",
|
||||
"- Recompute a custody card's inclusion proof as a counterparty -\n",
|
||||
" trust by recomputation, not by assertion.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Why a quorum, when one proof would do?\n",
|
||||
"\n",
|
||||
"Each member is *proven* to decide the same predicate,\n",
|
||||
"`accept(A,m,R,s) \u21d4 decompress(R) = [k](\u2212A) + [s]B`. So on the\n",
|
||||
"proven domain they cannot disagree about *meaning*. Classic\n",
|
||||
"N-version programming hopes independent code won't share a bug;\n",
|
||||
"we do not hope - we know the semantics coincide, so a runtime\n",
|
||||
"disagreement is not opinion, it is **evidence of a fault**: a\n",
|
||||
"corrupted build, a memory error, or tampering. The quorum turns\n",
|
||||
"\"the verifiers differed\" into an alarm with a theorem behind it.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Napkin scale: a 3-of-3 quorum with toy verifiers\n",
|
||||
"\n",
|
||||
"Forget real curves for a moment. Model three verifiers as\n",
|
||||
"functions and watch the boundary logic: unanimity accepts,\n",
|
||||
"any disagreement fails closed and is classified.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def toy_quorum(verdicts):\n",
|
||||
" kinds = set(verdicts.values())\n",
|
||||
" if kinds == {\"accept\"}:\n",
|
||||
" return \"unanimous-accept\", True\n",
|
||||
" if kinds == {\"reject\"}:\n",
|
||||
" return \"unanimous-reject\", False\n",
|
||||
" return \"divergence -> FAIL CLOSED + incident\", False\n",
|
||||
"\n",
|
||||
"print(toy_quorum({\"dalek\": \"accept\", \"anza\": \"accept\", \"risc0\": \"accept\"}))\n",
|
||||
"print(toy_quorum({\"dalek\": \"reject\", \"anza\": \"reject\", \"risc0\": \"reject\"}))\n",
|
||||
"print(toy_quorum({\"dalek\": \"accept\", \"anza\": \"reject\", \"risc0\": \"accept\"}))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The third line is the whole point: a lone dissenter does not get\n",
|
||||
"out-voted. Acceptance needs *everyone*; anything else is a\n",
|
||||
"refusal plus a recorded incident. Majority voting would hide\n",
|
||||
"exactly the fault we most want to see.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The divergence taxonomy\n",
|
||||
"\n",
|
||||
"The forks are *allowed* to differ on documented degenerate\n",
|
||||
"inputs (anza rejects `A = 0` and a legacy excluded-small-order-R\n",
|
||||
"list). We still fail closed; the taxonomy only grades the alarm:\n",
|
||||
"\n",
|
||||
"- **semantic-edge** - they differ AND a documented edge flag\n",
|
||||
" applies (small-order R, non-canonical s, zero key): severity\n",
|
||||
" *note*.\n",
|
||||
"- **unexplained** - they differ with no documented reason, or a\n",
|
||||
" member errored: severity *tamper* -> custody **latches**.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys, pathlib\n",
|
||||
"for parent in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n",
|
||||
" if (parent / \"src\" / \"pacta\").exists():\n",
|
||||
" sys.path.insert(0, str(parent / \"src\")); ROOT = parent; break\n",
|
||||
"\n",
|
||||
"from pacta.quorum import semantic_edge_flags, SMALL_ORDER_ENCODINGS\n",
|
||||
"\n",
|
||||
"small_order_R = sorted(SMALL_ORDER_ENCODINGS)[0]\n",
|
||||
"print(\"edge flags for a small-order R:\",\n",
|
||||
" semantic_edge_flags(b\"\\x02\" * 32, small_order_R + b\"\\x00\" * 32))\n",
|
||||
"print(\"edge flags for an ordinary sig:\",\n",
|
||||
" semantic_edge_flags(b\"\\x02\" * 32, b\"\\x01\" * 64))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"A divergence on the first input is a documented edge (note); a\n",
|
||||
"divergence on the second has no excuse (tamper). Same fail-closed\n",
|
||||
"verdict, very different alarm.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Real scale: the four proven forks, if built\n",
|
||||
"\n",
|
||||
"If you have run `pacta wallet build-quorum`, the next cell drives\n",
|
||||
"the **real** four-fork quorum: sign a payload with the dogfood\n",
|
||||
"(attested) signer, then watch all four proven verifiers agree on\n",
|
||||
"accept, and on reject for a flipped byte. If the binaries are not\n",
|
||||
"built, we say so and skip - honestly, the way the wallet itself\n",
|
||||
"fails closed.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pacta.quorum import load_quorum, binary_path\n",
|
||||
"\n",
|
||||
"built = [b for b in (\"dalek\", \"anza\", \"risc0\", \"betrusted\") if binary_path(b).exists()]\n",
|
||||
"if len(built) < 2:\n",
|
||||
" print(\"quorum not built (need >=2). Run: pacta wallet build-quorum --sources-root <...>\")\n",
|
||||
"else:\n",
|
||||
" import tempfile, os\n",
|
||||
" from pacta.dogfood import locate_verifier, pem_public_key_to_raw, sign_payload_dogfood\n",
|
||||
" from pacta.signing import generate_ed25519_keypair\n",
|
||||
" v = locate_verifier()\n",
|
||||
" if v is None:\n",
|
||||
" print(\"dogfood signer not built; run pacta dogfood-build\")\n",
|
||||
" else:\n",
|
||||
" d = tempfile.mkdtemp()\n",
|
||||
" key, pub = os.path.join(d, \"k.pem\"), os.path.join(d, \"k.pub\")\n",
|
||||
" generate_ed25519_keypair(key, pub)\n",
|
||||
" payload = b\"curriculum lecture 10 payload\"\n",
|
||||
" sig = sign_payload_dogfood(payload, key, v)\n",
|
||||
" pk = pem_public_key_to_raw(pub)\n",
|
||||
" q = load_quorum(min_members=2)\n",
|
||||
" print(\"members:\", sorted(q.members))\n",
|
||||
" good = q.verify(payload, sig, pk)\n",
|
||||
" print(\"valid signature ->\", good.classification, \"accepted =\", good.accepted)\n",
|
||||
" bad = q.verify(payload, bytes([sig[0] ^ 0xFF]) + sig[1:], pk)\n",
|
||||
" print(\"one flipped byte ->\", bad.classification, \"accepted =\", bad.accepted)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The signing firewall: verify-after-sign, but proven\n",
|
||||
"\n",
|
||||
"Outbound is `intent -> sign -> firewall -> release`. The fresh\n",
|
||||
"signature faces the same quorum; only unanimity releases it. A\n",
|
||||
"rejected self-signature is *quarantined, never returned*, and\n",
|
||||
"custody latches. This is the textbook fault-injection\n",
|
||||
"countermeasure - verify a signer's output before trusting it -\n",
|
||||
"with the verifier upgraded to machine-checked code.\n",
|
||||
"\n",
|
||||
"Note the honest asymmetry: the *verify* paths are certificate-\n",
|
||||
"covered (custody-grade), but the *signing* step is trusted base -\n",
|
||||
"the attested artifact, not a third implementation. The firewall\n",
|
||||
"is exactly how we fence that weaker edge.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Two voices, one boundary (the domain split, again)\n",
|
||||
"\n",
|
||||
"Lecture 06 split provider and agent. warden inherits the split:\n",
|
||||
"\n",
|
||||
"- **The operator voice** seals the capsule: it runs the R4 gate,\n",
|
||||
" pins the attested source commits, and stores the transparency-\n",
|
||||
" log receipts that authorized each member.\n",
|
||||
"- **The counterparty (agent) voice** never trusts the operator's\n",
|
||||
" adjectives. It reads the *custody card* and recomputes the\n",
|
||||
" inclusion proofs itself - trust by recomputation.\n",
|
||||
"\n",
|
||||
"The next cell is the counterparty side: given a card, verify a\n",
|
||||
"member's inclusion proof with nothing but stdlib hashing.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Counterparty-side check of ONE member's inclusion proof.\n",
|
||||
"# (Works whenever you have a wallet + its fetched evidence; here\n",
|
||||
"# we show the primitive the card relies on.)\n",
|
||||
"from pacta.transparency import verify_inclusion, leaf_bytes_for_attestation\n",
|
||||
"import json, glob\n",
|
||||
"\n",
|
||||
"ev = sorted(glob.glob(str(ROOT / \"examples\" / \"wallet-evidence\" / \"*.attestation.json\")))\n",
|
||||
"if not ev:\n",
|
||||
" print(\"no bundled evidence; fetch with `pacta log-fetch` to try live\")\n",
|
||||
"else:\n",
|
||||
" att = json.load(open(ev[0]))\n",
|
||||
" rec = json.load(open(ev[0].replace(\".attestation.\", \".receipt.\")))\n",
|
||||
" ok = verify_inclusion(\n",
|
||||
" leaf_bytes_for_attestation(att),\n",
|
||||
" rec[\"leaf_index\"], rec[\"tree_size\"],\n",
|
||||
" [bytes.fromhex(h) for h in rec[\"inclusion_proof\"]],\n",
|
||||
" bytes.fromhex(rec[\"sth\"][\"root_hash\"]),\n",
|
||||
" )\n",
|
||||
" print(f\"{att['subject']['component']}: inclusion recomputes ->\", ok)\n",
|
||||
" print(\"The counterparty believed no adjective; it recomputed a Merkle root.\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Corrupt a member, watch the pin catch it (executable)\n",
|
||||
"\n",
|
||||
"The wallet seals each member's SHA-256 into its capsule. The\n",
|
||||
"next cell stages a COPY of a real member binary in a temp\n",
|
||||
"directory, \"seals\" its hash the way the capsule does, appends\n",
|
||||
"one byte (a supply-chain attack in miniature), and re-checks.\n",
|
||||
"Nothing on your machine is modified.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import hashlib, shutil, tempfile, pathlib\n",
|
||||
"from pacta.quorum import binary_path\n",
|
||||
"\n",
|
||||
"member = binary_path(\"dalek\")\n",
|
||||
"if not member.exists():\n",
|
||||
" print(\"quorum not built; run pacta wallet build-quorum first\")\n",
|
||||
"else:\n",
|
||||
" stage = pathlib.Path(tempfile.mkdtemp()) / member.name\n",
|
||||
" shutil.copy2(member, stage)\n",
|
||||
" sealed = hashlib.sha256(stage.read_bytes()).hexdigest() # capsule pin\n",
|
||||
" print(\"sealed :\", sealed[:24], \"...\")\n",
|
||||
" with stage.open(\"ab\") as f:\n",
|
||||
" f.write(b\"\\x00\") # the attack\n",
|
||||
" current = hashlib.sha256(stage.read_bytes()).hexdigest()\n",
|
||||
" print(\"current:\", current[:24], \"...\")\n",
|
||||
" if current != sealed:\n",
|
||||
" print(\"PIN CAUGHT IT: wallet.quorum() would refuse to assemble ->\")\n",
|
||||
" print(\" 'quorum member dalek binary hash changed since the capsule was sealed'\")\n",
|
||||
" else:\n",
|
||||
" print(\"impossible: SHA-256 collision\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"One appended byte and the wallet refuses to even *assemble* the\n",
|
||||
"quorum - before any verification runs. Note what this control\n",
|
||||
"is and is not: it stops binary substitution *between* wallet\n",
|
||||
"sessions; an attacker with live root outranks it (see\n",
|
||||
"docs/threat-model.md, attacker #7 - that is what the choir and\n",
|
||||
"the airgap profiles are for).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercises\n",
|
||||
"\n",
|
||||
"- Change `toy_quorum` to majority voting and write two sentences\n",
|
||||
" on exactly which attack that lets through.\n",
|
||||
"- Extend the corrupt-a-member cell: corrupt the capsule JSON\n",
|
||||
" itself instead of the binary. What catches that, and when?\n",
|
||||
" (Hint: nothing does until the ledger genesis is compared -\n",
|
||||
" write down the exact trust statement the capsule hash in the\n",
|
||||
" genesis entry provides.)\n",
|
||||
"- The signing path is trusted base. Write the strongest *true*\n",
|
||||
" sentence you can about warden's outbound safety, and the\n",
|
||||
" strongest *false* one a marketer would write - and name the\n",
|
||||
" word that makes the second one false.\n",
|
||||
"- Design `warden-treasury`: which member re-verifies Solana\n",
|
||||
" transactions, and what exactly the RPC provider is still\n",
|
||||
" trusted for after you do.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The human surface: see this wallet through the cockpit\n",
|
||||
"\n",
|
||||
"Everything this notebook built programmatically has a read-only human console:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"pacta wallet cockpit --demo # throwaway demo wallet, zero setup\n",
|
||||
"pacta wallet cockpit --wallet DIR # the wallet you just sealed here\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Open `/deck` for all six role stations live in parallel (the quorum bench you built is the\n",
|
||||
"indigo pane; the ledger you hash-chained is re-verified on every page load), and `/manual`\n",
|
||||
"for the lab-manual sessions that teach each role \u2014 Session 4's tamper drill breaks a *copy*\n",
|
||||
"of a ledger exactly like this notebook's and watches two independent surfaces catch it.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Learning Objectives\n",
|
||||
"\n",
|
||||
"- Explain why a *unanimous* quorum of provably-equivalent\n",
|
||||
" verifiers turns disagreement into evidence of a fault, and why\n",
|
||||
" majority voting would hide exactly that fault.\n",
|
||||
"- Classify a quorum divergence as a documented semantic edge\n",
|
||||
" (note) versus unexplained (tamper -> latch).\n",
|
||||
"- Describe the outbound signing firewall as verify-after-sign\n",
|
||||
" with a proven verifier, and state warden's honest asymmetry\n",
|
||||
" (verify custody-grade, sign trusted base).\n",
|
||||
"- Recompute a custody card's inclusion proof as a counterparty -\n",
|
||||
" trust by recomputation, not by assertion.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Why a quorum, when one proof would do?\n",
|
||||
"\n",
|
||||
"Each member is *proven* to decide the same predicate,\n",
|
||||
"`accept(A,m,R,s) ⇔ decompress(R) = [k](−A) + [s]B`. So on the\n",
|
||||
"proven domain they cannot disagree about *meaning*. Classic\n",
|
||||
"N-version programming hopes independent code won't share a bug;\n",
|
||||
"we do not hope - we know the semantics coincide, so a runtime\n",
|
||||
"disagreement is not opinion, it is **evidence of a fault**: a\n",
|
||||
"corrupted build, a memory error, or tampering. The quorum turns\n",
|
||||
"\"the verifiers differed\" into an alarm with a theorem behind it.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Napkin scale: a 3-of-3 quorum with toy verifiers\n",
|
||||
"\n",
|
||||
"Forget real curves for a moment. Model three verifiers as\n",
|
||||
"functions and watch the boundary logic: unanimity accepts,\n",
|
||||
"any disagreement fails closed and is classified.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def toy_quorum(verdicts):\n",
|
||||
" kinds = set(verdicts.values())\n",
|
||||
" if kinds == {\"accept\"}:\n",
|
||||
" return \"unanimous-accept\", True\n",
|
||||
" if kinds == {\"reject\"}:\n",
|
||||
" return \"unanimous-reject\", False\n",
|
||||
" return \"divergence -> FAIL CLOSED + incident\", False\n",
|
||||
"\n",
|
||||
"print(toy_quorum({\"dalek\": \"accept\", \"anza\": \"accept\", \"risc0\": \"accept\"}))\n",
|
||||
"print(toy_quorum({\"dalek\": \"reject\", \"anza\": \"reject\", \"risc0\": \"reject\"}))\n",
|
||||
"print(toy_quorum({\"dalek\": \"accept\", \"anza\": \"reject\", \"risc0\": \"accept\"}))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The third line is the whole point: a lone dissenter does not get\n",
|
||||
"out-voted. Acceptance needs *everyone*; anything else is a\n",
|
||||
"refusal plus a recorded incident. Majority voting would hide\n",
|
||||
"exactly the fault we most want to see.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The divergence taxonomy\n",
|
||||
"\n",
|
||||
"The forks are *allowed* to differ on documented degenerate\n",
|
||||
"inputs (anza rejects `A = 0` and a legacy excluded-small-order-R\n",
|
||||
"list). We still fail closed; the taxonomy only grades the alarm:\n",
|
||||
"\n",
|
||||
"- **semantic-edge** - they differ AND a documented edge flag\n",
|
||||
" applies (small-order R, non-canonical s, zero key): severity\n",
|
||||
" *note*.\n",
|
||||
"- **unexplained** - they differ with no documented reason, or a\n",
|
||||
" member errored: severity *tamper* -> custody **latches**.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys, pathlib\n",
|
||||
"for parent in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n",
|
||||
" if (parent / \"src\" / \"pacta\").exists():\n",
|
||||
" sys.path.insert(0, str(parent / \"src\")); ROOT = parent; break\n",
|
||||
"\n",
|
||||
"from pacta.quorum import semantic_edge_flags, SMALL_ORDER_ENCODINGS\n",
|
||||
"\n",
|
||||
"small_order_R = sorted(SMALL_ORDER_ENCODINGS)[0]\n",
|
||||
"print(\"edge flags for a small-order R:\",\n",
|
||||
" semantic_edge_flags(b\"\\x02\" * 32, small_order_R + b\"\\x00\" * 32))\n",
|
||||
"print(\"edge flags for an ordinary sig:\",\n",
|
||||
" semantic_edge_flags(b\"\\x02\" * 32, b\"\\x01\" * 64))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"A divergence on the first input is a documented edge (note); a\n",
|
||||
"divergence on the second has no excuse (tamper). Same fail-closed\n",
|
||||
"verdict, very different alarm.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Real scale: the four proven forks, if built\n",
|
||||
"\n",
|
||||
"If you have run `pacta wallet build-quorum`, the next cell drives\n",
|
||||
"the **real** four-fork quorum: sign a payload with the dogfood\n",
|
||||
"(attested) signer, then watch all four proven verifiers agree on\n",
|
||||
"accept, and on reject for a flipped byte. If the binaries are not\n",
|
||||
"built, we say so and skip - honestly, the way the wallet itself\n",
|
||||
"fails closed.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pacta.quorum import load_quorum, binary_path\n",
|
||||
"\n",
|
||||
"built = [b for b in (\"dalek\", \"anza\", \"risc0\", \"betrusted\") if binary_path(b).exists()]\n",
|
||||
"if len(built) < 2:\n",
|
||||
" print(\"quorum not built (need >=2). Run: pacta wallet build-quorum --sources-root <...>\")\n",
|
||||
"else:\n",
|
||||
" import tempfile, os\n",
|
||||
" from pacta.dogfood import locate_verifier, pem_public_key_to_raw, sign_payload_dogfood\n",
|
||||
" from pacta.signing import generate_ed25519_keypair\n",
|
||||
" v = locate_verifier()\n",
|
||||
" if v is None:\n",
|
||||
" print(\"dogfood signer not built; run pacta dogfood-build\")\n",
|
||||
" else:\n",
|
||||
" d = tempfile.mkdtemp()\n",
|
||||
" key, pub = os.path.join(d, \"k.pem\"), os.path.join(d, \"k.pub\")\n",
|
||||
" generate_ed25519_keypair(key, pub)\n",
|
||||
" payload = b\"curriculum lecture 10 payload\"\n",
|
||||
" sig = sign_payload_dogfood(payload, key, v)\n",
|
||||
" pk = pem_public_key_to_raw(pub)\n",
|
||||
" q = load_quorum(min_members=2)\n",
|
||||
" print(\"members:\", sorted(q.members))\n",
|
||||
" good = q.verify(payload, sig, pk)\n",
|
||||
" print(\"valid signature ->\", good.classification, \"accepted =\", good.accepted)\n",
|
||||
" bad = q.verify(payload, bytes([sig[0] ^ 0xFF]) + sig[1:], pk)\n",
|
||||
" print(\"one flipped byte ->\", bad.classification, \"accepted =\", bad.accepted)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The signing firewall: verify-after-sign, but proven\n",
|
||||
"\n",
|
||||
"Outbound is `intent -> sign -> firewall -> release`. The fresh\n",
|
||||
"signature faces the same quorum; only unanimity releases it. A\n",
|
||||
"rejected self-signature is *quarantined, never returned*, and\n",
|
||||
"custody latches. This is the textbook fault-injection\n",
|
||||
"countermeasure - verify a signer's output before trusting it -\n",
|
||||
"with the verifier upgraded to machine-checked code.\n",
|
||||
"\n",
|
||||
"Note the honest asymmetry: the *verify* paths are certificate-\n",
|
||||
"covered (custody-grade), but the *signing* step is trusted base -\n",
|
||||
"the attested artifact, not a third implementation. The firewall\n",
|
||||
"is exactly how we fence that weaker edge.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Two voices, one boundary (the domain split, again)\n",
|
||||
"\n",
|
||||
"Lecture 06 split provider and agent. warden inherits the split:\n",
|
||||
"\n",
|
||||
"- **The operator voice** seals the capsule: it runs the R4 gate,\n",
|
||||
" pins the attested source commits, and stores the transparency-\n",
|
||||
" log receipts that authorized each member.\n",
|
||||
"- **The counterparty (agent) voice** never trusts the operator's\n",
|
||||
" adjectives. It reads the *custody card* and recomputes the\n",
|
||||
" inclusion proofs itself - trust by recomputation.\n",
|
||||
"\n",
|
||||
"The next cell is the counterparty side: given a card, verify a\n",
|
||||
"member's inclusion proof with nothing but stdlib hashing.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Counterparty-side check of ONE member's inclusion proof.\n",
|
||||
"# (Works whenever you have a wallet + its fetched evidence; here\n",
|
||||
"# we show the primitive the card relies on.)\n",
|
||||
"from pacta.transparency import verify_inclusion, leaf_bytes_for_attestation\n",
|
||||
"import json, glob\n",
|
||||
"\n",
|
||||
"ev = sorted(glob.glob(str(ROOT / \"examples\" / \"wallet-evidence\" / \"*.attestation.json\")))\n",
|
||||
"if not ev:\n",
|
||||
" print(\"no bundled evidence; fetch with `pacta log-fetch` to try live\")\n",
|
||||
"else:\n",
|
||||
" att = json.load(open(ev[0]))\n",
|
||||
" rec = json.load(open(ev[0].replace(\".attestation.\", \".receipt.\")))\n",
|
||||
" ok = verify_inclusion(\n",
|
||||
" leaf_bytes_for_attestation(att),\n",
|
||||
" rec[\"leaf_index\"], rec[\"tree_size\"],\n",
|
||||
" [bytes.fromhex(h) for h in rec[\"inclusion_proof\"]],\n",
|
||||
" bytes.fromhex(rec[\"sth\"][\"root_hash\"]),\n",
|
||||
" )\n",
|
||||
" print(f\"{att['subject']['component']}: inclusion recomputes ->\", ok)\n",
|
||||
" print(\"The counterparty believed no adjective; it recomputed a Merkle root.\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Corrupt a member, watch the pin catch it (executable)\n",
|
||||
"\n",
|
||||
"The wallet seals each member's SHA-256 into its capsule. The\n",
|
||||
"next cell stages a COPY of a real member binary in a temp\n",
|
||||
"directory, \"seals\" its hash the way the capsule does, appends\n",
|
||||
"one byte (a supply-chain attack in miniature), and re-checks.\n",
|
||||
"Nothing on your machine is modified.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import hashlib, shutil, tempfile, pathlib\n",
|
||||
"from pacta.quorum import binary_path\n",
|
||||
"\n",
|
||||
"member = binary_path(\"dalek\")\n",
|
||||
"if not member.exists():\n",
|
||||
" print(\"quorum not built; run pacta wallet build-quorum first\")\n",
|
||||
"else:\n",
|
||||
" stage = pathlib.Path(tempfile.mkdtemp()) / member.name\n",
|
||||
" shutil.copy2(member, stage)\n",
|
||||
" sealed = hashlib.sha256(stage.read_bytes()).hexdigest() # capsule pin\n",
|
||||
" print(\"sealed :\", sealed[:24], \"...\")\n",
|
||||
" with stage.open(\"ab\") as f:\n",
|
||||
" f.write(b\"\\x00\") # the attack\n",
|
||||
" current = hashlib.sha256(stage.read_bytes()).hexdigest()\n",
|
||||
" print(\"current:\", current[:24], \"...\")\n",
|
||||
" if current != sealed:\n",
|
||||
" print(\"PIN CAUGHT IT: wallet.quorum() would refuse to assemble ->\")\n",
|
||||
" print(\" 'quorum member dalek binary hash changed since the capsule was sealed'\")\n",
|
||||
" else:\n",
|
||||
" print(\"impossible: SHA-256 collision\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"One appended byte and the wallet refuses to even *assemble* the\n",
|
||||
"quorum - before any verification runs. Note what this control\n",
|
||||
"is and is not: it stops binary substitution *between* wallet\n",
|
||||
"sessions; an attacker with live root outranks it (see\n",
|
||||
"docs/threat-model.md, attacker #7 - that is what the choir and\n",
|
||||
"the airgap profiles are for).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercises\n",
|
||||
"\n",
|
||||
"- Change `toy_quorum` to majority voting and write two sentences\n",
|
||||
" on exactly which attack that lets through.\n",
|
||||
"- Extend the corrupt-a-member cell: corrupt the capsule JSON\n",
|
||||
" itself instead of the binary. What catches that, and when?\n",
|
||||
" (Hint: nothing does until the ledger genesis is compared -\n",
|
||||
" write down the exact trust statement the capsule hash in the\n",
|
||||
" genesis entry provides.)\n",
|
||||
"- The signing path is trusted base. Write the strongest *true*\n",
|
||||
" sentence you can about warden's outbound safety, and the\n",
|
||||
" strongest *false* one a marketer would write - and name the\n",
|
||||
" word that makes the second one false.\n",
|
||||
"- Design `warden-treasury`: which member re-verifies Solana\n",
|
||||
" transactions, and what exactly the RPC provider is still\n",
|
||||
" trusted for after you do.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The human surface: see this wallet through the cockpit\n",
|
||||
"\n",
|
||||
"Everything this notebook built programmatically has a read-only human console:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"pacta wallet cockpit --demo # throwaway demo wallet, zero setup\n",
|
||||
"pacta wallet cockpit --wallet DIR # the wallet you just sealed here\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Open `/deck` for all six role stations live in parallel (the quorum bench you built is the\n",
|
||||
"indigo pane; the ledger you hash-chained is re-verified on every page load), and `/manual`\n",
|
||||
"for the lab-manual sessions that teach each role — Session 4's tamper drill breaks a *copy*\n",
|
||||
"of a ledger exactly like this notebook's and watches two independent surfaces catch it."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,242 +1,229 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Lecture 11: The Customer's-Eye View — You Hold the Ruler\n",
|
||||
"\n",
|
||||
"Every earlier lecture looked at the log from the *operator's*\n",
|
||||
"side: replaying proofs, signing attestations, building the\n",
|
||||
"tree. This one flips the telescope. You are now a customer — an\n",
|
||||
"agent, a wallet, a developer — who wants to trust one of the\n",
|
||||
"four verified Ed25519 forks and has no theorem prover, no Lean,\n",
|
||||
"no desire to spend hours re-checking anything. What actually\n",
|
||||
"happens on your side? The answer reframes the whole system, and\n",
|
||||
"it is the most intuitive on-ramp to it.\n"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Lecture 11: The Customer's-Eye View \u2014 You Hold the Ruler\n",
|
||||
"\n",
|
||||
"Every earlier lecture looked at the log from the *operator's*\n",
|
||||
"side: replaying proofs, signing attestations, building the\n",
|
||||
"tree. This one flips the telescope. You are now a customer \u2014 an\n",
|
||||
"agent, a wallet, a developer \u2014 who wants to trust one of the\n",
|
||||
"four verified Ed25519 forks and has no theorem prover, no Lean,\n",
|
||||
"no desire to spend hours re-checking anything. What actually\n",
|
||||
"happens on your side? The answer reframes the whole system, and\n",
|
||||
"it is the most intuitive on-ramp to it.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Learning Objectives\n",
|
||||
"\n",
|
||||
"- Separate the three roles that a verification touches:\n",
|
||||
" *measurement* (operator), *publication* (the log), and\n",
|
||||
" *judgment* (you) \u2014 and see that only the last is yours.\n",
|
||||
"- Understand the \"allowed axioms\" list as a **requirements\n",
|
||||
" card** you own, can read, and could write yourself.\n",
|
||||
"- Explain why a self-written card meeting the supply *exactly*\n",
|
||||
" is engineered, not coincidental.\n",
|
||||
"- State the three honest outcomes when your card is stricter\n",
|
||||
" than the supply: relax (itemized), walk away, or grow the\n",
|
||||
" supply.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The card is a requirements card \u2014 and ideally you write it\n",
|
||||
"\n",
|
||||
"The list of axioms a certificate is *allowed* to rest on is not\n",
|
||||
"handed to you by the operator at verification time. It ships\n",
|
||||
"inside your own tooling (the ed25519 profile), on your disk,\n",
|
||||
"versioned by you. A maximally paranoid customer ignores the\n",
|
||||
"shipped copy and writes the card from first principles:\n",
|
||||
"\n",
|
||||
"> *\"I accept Lean's three foundational axioms, because that is\n",
|
||||
"> what the proof kernel's logic IS. For the signature-tier\n",
|
||||
"> theorems I accept named placeholders for SHA-512 and the wire\n",
|
||||
"> format, because RFC 8032 tells me those parts exist and I can\n",
|
||||
"> see they are declared, not smuggled. Nothing else.\"*\n",
|
||||
"\n",
|
||||
"That is a wish-list: the assumptions you are willing to live\n",
|
||||
"under. Call it your ruler.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys, pathlib\n",
|
||||
"for parent in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n",
|
||||
" if (parent / \"src\" / \"pacta\").exists():\n",
|
||||
" sys.path.insert(0, str(parent / \"src\")); break\n",
|
||||
"\n",
|
||||
"# A customer writes their OWN card, from first principles - no\n",
|
||||
"# peeking at the operator. Lean's three, plus named oracle slots.\n",
|
||||
"my_card = {\n",
|
||||
" \"foundational\": {\"propext\", \"Classical.choice\", \"Quot.sound\"},\n",
|
||||
" \"apex_oracle_allowed\": {\n",
|
||||
" \"sha2.Sha512\", \"verifying.sha512_new\",\n",
|
||||
" \"verifying.sha512_update\", \"verifying.sha512_finalize_bytes\",\n",
|
||||
" \"ed25519.Signature\", \"ed25519.Signature.to_bytes\",\n",
|
||||
" \"signature.error.Error\", \"signature.error.Error.new\",\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"print(\"my requirements card:\", sum(len(v) for v in my_card.values()), \"named assumptions\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Wish meets supply \u2014 and it is no accident\n",
|
||||
"\n",
|
||||
"Now compare your self-written card against what the four forks\n",
|
||||
"actually declare. They match. That convergence is *engineered*:\n",
|
||||
"the supply was deliberately shrunk \u2014 every axiom made to justify\n",
|
||||
"its existence \u2014 so that any reasonable person's independently\n",
|
||||
"written card lands on the same minimal list. When the wish meets\n",
|
||||
"the supply exactly, it is because the supplier spent months\n",
|
||||
"making the supply as small as honesty allows.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pacta.profiles.ed25519 import APEX_BOUNDARIES\n",
|
||||
"\n",
|
||||
"# The supply: the apex-tier certificate's documented, allowed\n",
|
||||
"# axiom cone for the upstream fork - the exact set the strongest\n",
|
||||
"# theorem is permitted to rest on, nothing more, nothing less.\n",
|
||||
"supply = set(APEX_BOUNDARIES[\"dalek-wrappers\"])\n",
|
||||
"wish = my_card[\"foundational\"] | my_card[\"apex_oracle_allowed\"]\n",
|
||||
"print(\"supply == my wish:\", supply == wish)\n",
|
||||
"print(\"axioms in supply my card did not anticipate:\", (supply - wish) or \"none\")\n",
|
||||
"print(\"axioms my card wanted that are absent:\", (wish - supply) or \"none\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The three roles, kept apart\n",
|
||||
"\n",
|
||||
"It is tempting to say \"the inclusion proof proves the Lean proof\n",
|
||||
"is about my card.\" It does not, and the precision matters:\n",
|
||||
"\n",
|
||||
"| role | who | what it establishes |\n",
|
||||
"|---|---|---|\n",
|
||||
"| measurement | operator's kernel run | \"this theorem rests on exactly these named axioms\" (recorded verbatim in the attestation) |\n",
|
||||
"| publication | the transparency log | \"this measurement is in the permanent record, shown identically to everyone\" (the inclusion proof + signed head) |\n",
|
||||
"| judgment | **you** | \"these observed axioms are inside my allowed card\" (re-derived locally, every time) |\n",
|
||||
"\n",
|
||||
"Only judgment is yours, and it is the only step that involves an\n",
|
||||
"opinion. The operator is trusted to *copy down what the kernel\n",
|
||||
"printed* \u2014 never to interpret it.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## If you are happy \u2014 you are already finished\n",
|
||||
"\n",
|
||||
"If your card covers the observed cone, verification is: check\n",
|
||||
"one signature, walk ~4 hashes to the signed root, compare cones\n",
|
||||
"to your card. Milliseconds, standard library, no Lean. Done.\n",
|
||||
"\n",
|
||||
"## If you are NOT happy \u2014 there is nothing to negotiate\n",
|
||||
"\n",
|
||||
"Suppose your card is stricter: *\"I require SHA-512 itself\n",
|
||||
"proven, not an oracle.\"* The system has no sales pitch for you.\n",
|
||||
"Its entire answer is an **itemized** statement of the gap:\n",
|
||||
"*not in supply; SHA-512-proven is the R5 frontier; here is the\n",
|
||||
"exact list of what you would be accepting if you proceed\n",
|
||||
"anyway.* Your decision is never \"lower my standards\" in the\n",
|
||||
"abstract \u2014 it is a named line item: *SHA-512 as oracle: yes/no.*\n",
|
||||
"Informed compromise, not diffuse trust.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The honest gap, itemized. A stricter customer wants no hash oracle.\n",
|
||||
"strict_card = my_card[\"foundational\"] # foundational ONLY, no oracle slots\n",
|
||||
"gap = supply - strict_card\n",
|
||||
"print(\"this customer must explicitly accept, or walk away:\")\n",
|
||||
"for ax in sorted(gap):\n",
|
||||
" print(\" -\", ax)\n",
|
||||
"print()\n",
|
||||
"print(\"verdict for the strict card:\", \"PASSES\" if supply <= strict_card else \"REFUSED (gap above)\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The third option: grow the supply\n",
|
||||
"\n",
|
||||
"The gap between anyone's dream card and today's supply is a\n",
|
||||
"to-do list, not a wall. Prove SHA-512, certify the wire\n",
|
||||
"parsers, and the new certificates enter the same log \u2014 and\n",
|
||||
"stricter cards start passing. The log is additive in exactly\n",
|
||||
"the way requirements are.\n",
|
||||
"\n",
|
||||
"**So: you hold the ruler. If your ruler is stricter than our\n",
|
||||
"supply, your ruler is our roadmap.** That is the quiet\n",
|
||||
"invitation built into the whole design.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercises\n",
|
||||
"\n",
|
||||
"- Write your own requirements card for the *foundational*\n",
|
||||
" (non-signature) certificates from scratch. Those certificates\n",
|
||||
" are expected to carry exactly Lean's three standard axioms\n",
|
||||
" (`propext`, `Classical.choice`, `Quot.sound`) and nothing\n",
|
||||
" else - did your card guess exactly those three?\n",
|
||||
"- A vendor's attestation says `status: proven`. Explain in two\n",
|
||||
" sentences why your tooling ignores that field entirely, and\n",
|
||||
" what it looks at instead.\n",
|
||||
"- You require reproducible builds. Is that a card you can write\n",
|
||||
" today and have pass? Name the tier that gap belongs to, and\n",
|
||||
" what would have to enter the log to close it.\n",
|
||||
"- Argue both sides: is \"wish meets supply exactly\" a strength\n",
|
||||
" (minimal, auditable) or a weakness (the same author wrote the\n",
|
||||
" card and the supply)? What single action by a customer\n",
|
||||
" settles the argument in their favor?\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Learning Objectives\n",
|
||||
"\n",
|
||||
"- Separate the three roles that a verification touches:\n",
|
||||
" *measurement* (operator), *publication* (the log), and\n",
|
||||
" *judgment* (you) — and see that only the last is yours.\n",
|
||||
"- Understand the \"allowed axioms\" list as a **requirements\n",
|
||||
" card** you own, can read, and could write yourself.\n",
|
||||
"- Explain why a self-written card meeting the supply *exactly*\n",
|
||||
" is engineered, not coincidental.\n",
|
||||
"- State the three honest outcomes when your card is stricter\n",
|
||||
" than the supply: relax (itemized), walk away, or grow the\n",
|
||||
" supply.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The card is a requirements card — and ideally you write it\n",
|
||||
"\n",
|
||||
"The list of axioms a certificate is *allowed* to rest on is not\n",
|
||||
"handed to you by the operator at verification time. It ships\n",
|
||||
"inside your own tooling (the ed25519 profile), on your disk,\n",
|
||||
"versioned by you. A maximally paranoid customer ignores the\n",
|
||||
"shipped copy and writes the card from first principles:\n",
|
||||
"\n",
|
||||
"> *\"I accept Lean's three foundational axioms, because that is\n",
|
||||
"> what the proof kernel's logic IS. For the signature-tier\n",
|
||||
"> theorems I accept named placeholders for SHA-512 and the wire\n",
|
||||
"> format, because RFC 8032 tells me those parts exist and I can\n",
|
||||
"> see they are declared, not smuggled. Nothing else.\"*\n",
|
||||
"\n",
|
||||
"That is a wish-list: the assumptions you are willing to live\n",
|
||||
"under. Call it your ruler.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys, pathlib\n",
|
||||
"for parent in [pathlib.Path.cwd(), *pathlib.Path.cwd().parents]:\n",
|
||||
" if (parent / \"src\" / \"pacta\").exists():\n",
|
||||
" sys.path.insert(0, str(parent / \"src\")); break\n",
|
||||
"\n",
|
||||
"# A customer writes their OWN card, from first principles - no\n",
|
||||
"# peeking at the operator. Lean's three, plus named oracle slots.\n",
|
||||
"my_card = {\n",
|
||||
" \"foundational\": {\"propext\", \"Classical.choice\", \"Quot.sound\"},\n",
|
||||
" \"apex_oracle_allowed\": {\n",
|
||||
" \"sha2.Sha512\", \"verifying.sha512_new\",\n",
|
||||
" \"verifying.sha512_update\", \"verifying.sha512_finalize_bytes\",\n",
|
||||
" \"ed25519.Signature\", \"ed25519.Signature.to_bytes\",\n",
|
||||
" \"signature.error.Error\", \"signature.error.Error.new\",\n",
|
||||
" },\n",
|
||||
"}\n",
|
||||
"print(\"my requirements card:\", sum(len(v) for v in my_card.values()), \"named assumptions\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Wish meets supply — and it is no accident\n",
|
||||
"\n",
|
||||
"Now compare your self-written card against what the four forks\n",
|
||||
"actually declare. They match. That convergence is *engineered*:\n",
|
||||
"the supply was deliberately shrunk — every axiom made to justify\n",
|
||||
"its existence — so that any reasonable person's independently\n",
|
||||
"written card lands on the same minimal list. When the wish meets\n",
|
||||
"the supply exactly, it is because the supplier spent months\n",
|
||||
"making the supply as small as honesty allows.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pacta.profiles.ed25519 import APEX_BOUNDARIES\n",
|
||||
"\n",
|
||||
"# The supply: the apex-tier certificate's documented, allowed\n",
|
||||
"# axiom cone for the upstream fork - the exact set the strongest\n",
|
||||
"# theorem is permitted to rest on, nothing more, nothing less.\n",
|
||||
"supply = set(APEX_BOUNDARIES[\"dalek-wrappers\"])\n",
|
||||
"wish = my_card[\"foundational\"] | my_card[\"apex_oracle_allowed\"]\n",
|
||||
"print(\"supply == my wish:\", supply == wish)\n",
|
||||
"print(\"axioms in supply my card did not anticipate:\", (supply - wish) or \"none\")\n",
|
||||
"print(\"axioms my card wanted that are absent:\", (wish - supply) or \"none\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The three roles, kept apart\n",
|
||||
"\n",
|
||||
"It is tempting to say \"the inclusion proof proves the Lean proof\n",
|
||||
"is about my card.\" It does not, and the precision matters:\n",
|
||||
"\n",
|
||||
"| role | who | what it establishes |\n",
|
||||
"|---|---|---|\n",
|
||||
"| measurement | operator's kernel run | \"this theorem rests on exactly these named axioms\" (recorded verbatim in the attestation) |\n",
|
||||
"| publication | the transparency log | \"this measurement is in the permanent record, shown identically to everyone\" (the inclusion proof + signed head) |\n",
|
||||
"| judgment | **you** | \"these observed axioms are inside my allowed card\" (re-derived locally, every time) |\n",
|
||||
"\n",
|
||||
"Only judgment is yours, and it is the only step that involves an\n",
|
||||
"opinion. The operator is trusted to *copy down what the kernel\n",
|
||||
"printed* — never to interpret it.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## If you are happy — you are already finished\n",
|
||||
"\n",
|
||||
"If your card covers the observed cone, verification is: check\n",
|
||||
"one signature, walk ~4 hashes to the signed root, compare cones\n",
|
||||
"to your card. Milliseconds, standard library, no Lean. Done.\n",
|
||||
"\n",
|
||||
"## If you are NOT happy — there is nothing to negotiate\n",
|
||||
"\n",
|
||||
"Suppose your card is stricter: *\"I require SHA-512 itself\n",
|
||||
"proven, not an oracle.\"* The system has no sales pitch for you.\n",
|
||||
"Its entire answer is an **itemized** statement of the gap:\n",
|
||||
"*not in supply; SHA-512-proven is the R5 frontier; here is the\n",
|
||||
"exact list of what you would be accepting if you proceed\n",
|
||||
"anyway.* Your decision is never \"lower my standards\" in the\n",
|
||||
"abstract — it is a named line item: *SHA-512 as oracle: yes/no.*\n",
|
||||
"Informed compromise, not diffuse trust.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The honest gap, itemized. A stricter customer wants no hash oracle.\n",
|
||||
"strict_card = my_card[\"foundational\"] # foundational ONLY, no oracle slots\n",
|
||||
"gap = supply - strict_card\n",
|
||||
"print(\"this customer must explicitly accept, or walk away:\")\n",
|
||||
"for ax in sorted(gap):\n",
|
||||
" print(\" -\", ax)\n",
|
||||
"print()\n",
|
||||
"print(\"verdict for the strict card:\", \"PASSES\" if supply <= strict_card else \"REFUSED (gap above)\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The third option: grow the supply\n",
|
||||
"\n",
|
||||
"The gap between anyone's dream card and today's supply is a\n",
|
||||
"to-do list, not a wall. Prove SHA-512, certify the wire\n",
|
||||
"parsers, and the new certificates enter the same log — and\n",
|
||||
"stricter cards start passing. The log is additive in exactly\n",
|
||||
"the way requirements are.\n",
|
||||
"\n",
|
||||
"**So: you hold the ruler. If your ruler is stricter than our\n",
|
||||
"supply, your ruler is our roadmap.** That is the quiet\n",
|
||||
"invitation built into the whole design.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercises\n",
|
||||
"\n",
|
||||
"- Write your own requirements card for the *foundational*\n",
|
||||
" (non-signature) certificates from scratch. Those certificates\n",
|
||||
" are expected to carry exactly Lean's three standard axioms\n",
|
||||
" (`propext`, `Classical.choice`, `Quot.sound`) and nothing\n",
|
||||
" else - did your card guess exactly those three?\n",
|
||||
"- A vendor's attestation says `status: proven`. Explain in two\n",
|
||||
" sentences why your tooling ignores that field entirely, and\n",
|
||||
" what it looks at instead.\n",
|
||||
"- You require reproducible builds. Is that a card you can write\n",
|
||||
" today and have pass? Name the tier that gap belongs to, and\n",
|
||||
" what would have to enter the log to close it.\n",
|
||||
"- Argue both sides: is \"wish meets supply exactly\" a strength\n",
|
||||
" (minimal, auditable) or a weakness (the same author wrote the\n",
|
||||
" card and the supply)? What single action by a customer\n",
|
||||
" settles the argument in their favor?\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Do it in the cockpit: the Inspect instrument\n",
|
||||
"\n",
|
||||
"The recomputation this notebook performs by hand is exactly what the cockpit's **Inspect**\n",
|
||||
"instrument (`/inspect`) runs on paste — same deployed verifier, verbatim diagnostics —\n",
|
||||
"and what the lab manual's Cryptographer session drills: verify the sample evidence, then\n",
|
||||
"deliberately corrupt one character and learn the distinct failure surfaces.\n",
|
||||
"`pacta wallet cockpit --demo`, then Inspect → “Load the sample evidence”."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ COURSE = {
|
|||
- Perform a proof hygiene scan and explain why `sorry`, local axioms, and trivial theorem targets are dangerous.
|
||||
- Explain how a third-party proof-checking provider changes the trusted base.
|
||||
- Implement and verify RFC 9162-style Merkle inclusion and consistency proofs.
|
||||
- Explain why Signed Tree Heads need accountable signatures, why Ed25519 is useful here, and why ML-DSA requires a real backend.
|
||||
- Explain why Signed Tree Heads need accountable signatures, why Ed25519 is useful here, why ML-DSA requires a real backend, and how the shipped SLH-DSA co-signature differs from both.
|
||||
- Design policy gates that convert verification evidence into consequences.
|
||||
- Read R4 four-tier apex evidence, name its residual blockers, and write a research plan toward R5 production assurance.
|
||||
"""
|
||||
|
|
@ -1029,6 +1029,7 @@ COURSE = {
|
|||
|
||||
- `require-signatures ed25519`: verify Ed25519 and allow ML-DSA to be unavailable.
|
||||
- `require-signatures both`: require Ed25519 and ML-DSA verified. If ML-DSA is unavailable, fail closed.
|
||||
- `--slhdsa-public-key <pem>`: additionally verify the second (post-quantum) SLH-DSA co-signature on the head; heads before tree size 14 report `absent` (allowed), a present-but-wrong signature fails closed.
|
||||
"""
|
||||
),
|
||||
code(
|
||||
|
|
@ -1050,6 +1051,53 @@ COURSE = {
|
|||
ML-DSA adds post-quantum robustness for the accumulator signature layer. But it must be a real signature, not an aspirational label. If a host lacks ML-DSA, the correct result is an explicit blocker.
|
||||
"""
|
||||
),
|
||||
md(
|
||||
"""
|
||||
## The second signature that actually shipped: SLH-DSA
|
||||
|
||||
Since tree size 14, every head of the LIVE log carries a second,
|
||||
deterministic **SLH-DSA-SHA2-128s** (FIPS 205) signature beside the
|
||||
required Ed25519 one. This is not the ML-DSA slot above - it is a
|
||||
hash-based scheme, and it was chosen because the estate has PROVEN
|
||||
its verify path (eleven certificates, log leaf 18): the log
|
||||
co-signs with the parameter set whose verification path it itself
|
||||
attests. Three design facts worth internalizing:
|
||||
|
||||
1. Heads published before size 14 carry no co-signature, and
|
||||
verifiers report them `ABSENT` rather than failing them - an
|
||||
append-only log keeps the history of its own signature-scheme
|
||||
upgrades.
|
||||
2. The co-signature is deterministic on purpose: re-signing the
|
||||
same payload is byte-comparable, so "same input, same
|
||||
signature" becomes a diff you can run, not an assurance you
|
||||
must trust.
|
||||
3. Signing is still never proven - here, as everywhere in this
|
||||
estate, certificates cover the VERIFY path only.
|
||||
"""
|
||||
),
|
||||
code(
|
||||
"""
|
||||
# Runnable where OpenSSL >= 3.5 is present; honest skip otherwise.
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from pacta import slhdsa
|
||||
|
||||
tmp = Path(tempfile.mkdtemp(prefix="nb06-slhdsa-"))
|
||||
try:
|
||||
slhdsa.generate_slhdsa_keypair(tmp / "slh.key", tmp / "slh.pub")
|
||||
except Exception as exc:
|
||||
print("SLH-DSA unavailable on this host (OpenSSL >= 3.5 needed):", exc)
|
||||
else:
|
||||
payload = b"canonical STH payload bytes"
|
||||
block = slhdsa.slh_dsa_signature_block(payload, tmp / "slh.key", tmp / "slh.pub")
|
||||
ok, err = slhdsa.verify_payload_slhdsa(payload, block["signature_base64"], tmp / "slh.pub")
|
||||
print("co-signature verifies:", ok, err or "")
|
||||
block2 = slhdsa.slh_dsa_signature_block(payload, tmp / "slh.key", tmp / "slh.pub")
|
||||
print("deterministic (byte-equal re-sign):",
|
||||
block["signature_base64"] == block2["signature_base64"])
|
||||
"""
|
||||
),
|
||||
md(
|
||||
"""
|
||||
## Two domains, two notebooks - by design
|
||||
|
|
@ -1749,6 +1797,7 @@ COURSE = {
|
|||
- Modify a claim card to R2 and show that `build-library` is refused.
|
||||
- Explain why a denial artifact is useful for auditability.
|
||||
- Design a policy where an agent requires `both` Ed25519 and ML-DSA signatures for production deployment but allows Ed25519-only in a local lab.
|
||||
- Extend it: when should the agent also require the SLH-DSA co-signature, given that heads before tree size 14 legitimately lack it?
|
||||
- Write a downstream Rust pseudo-code snippet that imports the generated capsule before enabling a code path.
|
||||
"""
|
||||
),
|
||||
|
|
@ -1770,7 +1819,7 @@ COURSE = {
|
|||
- State precisely which parts of the dogfood verifier are certificate-covered and which are its trusted base.
|
||||
- Extract a raw Ed25519 key from an OpenSSL PEM by hand (napkin) and mechanically (real).
|
||||
- Demonstrate backend dispatch and the fail-closed `--require-verified-verifier` policy.
|
||||
- Defend the hybrid post-quantum posture: one proven-classical signature plus one required-but-honest ML-DSA slot.
|
||||
- Defend the three-legged post-quantum posture: proven-classical Ed25519, the shipped SLH-DSA co-signature with its attested verify path, and a required-but-honest ML-DSA slot.
|
||||
"""
|
||||
),
|
||||
md(
|
||||
|
|
@ -1858,7 +1907,8 @@ COURSE = {
|
|||
leaf, against the very tree it is about to sign. The verdict is
|
||||
embedded in the signature block (`signing_provenance`: backend,
|
||||
library commit, leaf index, `self_inclusion: verified`,
|
||||
certificates 16/16). Lectures 6a/6b walk both sides of this.
|
||||
certificates 44/44 - the signer's source family was re-attested at 44
|
||||
certificates as leaf 13). Lectures 6a/6b walk both sides of this.
|
||||
Honesty note unchanged: the library's VERIFY path is
|
||||
certificate-covered; the signing path is declared trusted base -
|
||||
but it is the attested artifact, not an un-attested third
|
||||
|
|
@ -1866,12 +1916,13 @@ COURSE = {
|
|||
|
||||
## The post-quantum line, held honestly
|
||||
|
||||
The dogfood loop deliberately does NOT extend to ML-DSA. There is no formally verified ML-DSA implementation in this corpus, and pretending otherwise would poison the whole posture. The hybrid strategy is therefore asymmetric on purpose:
|
||||
The posture has three legs now, and each is exactly as strong as it claims:
|
||||
|
||||
- **Ed25519 (classical): proven path.** The signature everyone can check today runs on certificate-covered code.
|
||||
- **ML-DSA-65 (post-quantum): required, honest, unavailable-until-real.** The tree-head slot exists in every signed structure; `--require-signatures both` fails CLOSED on hosts without a real FIPS 204 backend; and when a real backend lands, the policy flips on without a schema change.
|
||||
- **Ed25519 (classical): proven verify path, dogfooded.** The signature everyone can check today runs on certificate-covered code.
|
||||
- **SLH-DSA-SHA2-128s (post-quantum): shipped and attested.** Since tree size 14 every live head carries a second, deterministic SLH-DSA co-signature. The estate proved the VERIFY path of a pinned Rust FIPS 205 implementation (eleven certificates) and appended that attestation as leaf 18 - so the co-signature uses exactly the parameter set the log itself attests. Consumers check it with `pacta receipt-verify ... --slhdsa-public-key provider.slhdsa.pub` or the mirror's `verify.py`. Signing remains unproven - verify paths only, always.
|
||||
- **ML-DSA-65 (lattice PQ): required, honest, unavailable-until-real.** The tree-head slot exists in every signed structure; `--require-signatures both` fails CLOSED on hosts without a real FIPS 204 backend; when a real backend lands, the policy flips on without a schema change.
|
||||
|
||||
A migration strategy that records "we cannot do this yet" as a deployment blocker is strictly stronger than one that ships a placeholder. Blockers get fixed; placeholders get trusted.
|
||||
A migration strategy that records "we cannot do this yet" as a deployment blocker is strictly stronger than one that ships a placeholder. Blockers get fixed; placeholders get trusted. And the SLH-DSA leg shows the endgame: a slot stops being aspirational the day its verify path enters the log.
|
||||
"""
|
||||
),
|
||||
code(
|
||||
|
|
@ -2310,6 +2361,23 @@ COURSE = {
|
|||
trusted for after you do.
|
||||
"""
|
||||
),
|
||||
md(
|
||||
"""
|
||||
## The human surface: see this wallet through the cockpit
|
||||
|
||||
Everything this notebook built programmatically has a read-only human console:
|
||||
|
||||
```
|
||||
pacta wallet cockpit --demo # throwaway demo wallet, zero setup
|
||||
pacta wallet cockpit --wallet DIR # the wallet you just sealed here
|
||||
```
|
||||
|
||||
Open `/deck` for all six role stations live in parallel (the quorum bench you built is the
|
||||
indigo pane; the ledger you hash-chained is re-verified on every page load), and `/manual`
|
||||
for the lab-manual sessions that teach each role — Session 4's tamper drill breaks a *copy*
|
||||
of a ledger exactly like this notebook's and watches two independent surfaces catch it.
|
||||
"""
|
||||
),
|
||||
]
|
||||
),
|
||||
"11_the_customers_eye_view.ipynb": notebook(
|
||||
|
|
|
|||
Loading…
Reference in a new issue