From 486f208b137fc00c610b2e679cfa56a0a8c0c882 Mon Sep 17 00:00:00 2001 From: Imran Siddique Date: Mon, 27 Jul 2026 11:54:24 -0700 Subject: [PATCH] feat(weight-custody-manifest): add WCM demo catalog Relocate the Weight Custody Manifest demos into a per-product scenario folder, depending on the published PyPI package (weight-custody-manifest >=0.19.0) instead of the in-repo SDK. Mirrors the ca2a-delegation layout. Four demos: open_model_e2e (full 6-step flow on an open model), sovereign_self_custody (2-of-3 threshold release), snp_replay (offline SEV-SNP quote replay against a committed synthetic bundle), and real_open_model (downloads SmolLM2-135M and hashes real weights, run local). README adapted from the WCM tutorials. A CI job installs from PyPI and runs the three offline demos; real_open_model is excluded (network, heavy) with its extras in requirements-infer.txt. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 25 ++ weight-custody-manifest/README.md | 76 ++++++ .../fixtures/snp_quote_synthetic.json | 11 + weight-custody-manifest/manifest.example.json | 71 +++++ weight-custody-manifest/open_model_e2e.py | 240 +++++++++++++++++ weight-custody-manifest/real_open_model.py | 250 ++++++++++++++++++ .../requirements-infer.txt | 6 + weight-custody-manifest/requirements.txt | 3 + weight-custody-manifest/snp_replay.py | 160 +++++++++++ .../sovereign_self_custody.py | 184 +++++++++++++ 10 files changed, 1026 insertions(+) create mode 100644 weight-custody-manifest/README.md create mode 100644 weight-custody-manifest/fixtures/snp_quote_synthetic.json create mode 100644 weight-custody-manifest/manifest.example.json create mode 100644 weight-custody-manifest/open_model_e2e.py create mode 100644 weight-custody-manifest/real_open_model.py create mode 100644 weight-custody-manifest/requirements-infer.txt create mode 100644 weight-custody-manifest/requirements.txt create mode 100644 weight-custody-manifest/snp_replay.py create mode 100644 weight-custody-manifest/sovereign_self_custody.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 055490f..bdde849 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -145,3 +145,28 @@ jobs: - name: Run the delegation demo run: python delegation_agent.py + + weight-custody-manifest: + runs-on: ubuntu-latest + defaults: + run: + working-directory: weight-custody-manifest + steps: + - uses: actions/checkout@v7 + + - name: Set up Python 3.11 + uses: actions/setup-python@v7 + with: + python-version: "3.11" + + - name: Install the WCM SDK from PyPI + run: python -m pip install -r requirements.txt + + - name: Run the end-to-end custody demo + run: python open_model_e2e.py + + - name: Run the sovereign threshold demo + run: python sovereign_self_custody.py + + - name: Replay a SEV-SNP quote offline + run: python snp_replay.py diff --git a/weight-custody-manifest/README.md b/weight-custody-manifest/README.md new file mode 100644 index 0000000..9bd824e --- /dev/null +++ b/weight-custody-manifest/README.md @@ -0,0 +1,76 @@ +# weight-custody-manifest: Custody for Model Weights + +The other examples in this repo govern what an agent may **do**. This one governs the model **weights** themselves: when a builder deploys a model into a customer's own or sovereign infrastructure, how do you prove the weights running in the enclave are exactly the ones the builder shipped, release the decryption key only against a valid hardware attestation, wipe it when custody lapses, and carry the lineage of every derivative? + +That is the [Weight Custody Manifest](https://pypi.org/project/weight-custody-manifest/) (WCM): an open protocol and reference SDK. These four demos run its real code. + +For a public open-weight model, base-weight confidentiality is theater (anyone can download the weights), so the same flow does the work that still matters: **integrity and provenance** (is this the model the builder shipped, or a tampered copy), **license as a release condition**, **derivative custody and lineage**, and a **kill switch**. + +> **Honest scope.** WCM does not claim silicon-enforced custody against an operator who physically owns the hardware. Cheap published memory-bus attacks (TEE.fail, BadRAM) extract keys from live confidential-computing memory, so against that adversary WCM is accountability-grade (cost, detection, containment, mandatory physical hardening), not cryptographic custody. It is custody-grade against software and remote adversaries. The demos below use a software (mock) attestation provider so they run with no hardware; the hardware-rooted step is validated separately on real SEV-SNP and TDX silicon. + +--- + +## Setup + +```bash +git clone https://github.com/agentrust-io/examples.git +cd examples/weight-custody-manifest +pip install -r requirements.txt # weight-custody-manifest>=0.19.0, Python 3.11+ +``` + +The three offline demos need nothing else. `real_open_model.py` needs run-local extras (below). + +--- + +## The demos + +### 1. `open_model_e2e.py` -- the whole flow, end to end + +The full six-step Weight Custody Manifest flow on an open-weight model with a mock attestation provider: sign the joint manifest (builder + custodian), run the attestation-gated key release, hold the key under a wipe-on-lapse custody lease, enforce the license as a release condition, then fine-tune into a derivative and verify its lineage back to the base. Narrated, and honest about which steps are real work versus theater for a public model. Runs anywhere. + +```bash +python open_model_e2e.py +``` + +### 2. `sovereign_self_custody.py` -- threshold release, no single point of trust + +Sovereign self-custody with a 2-of-3 threshold split-key (SPEC 3.5): the release key is split with Shamir secret sharing so no single party can release the weights alone. Real WCM code, software attestation, runs anywhere. + +```bash +python sovereign_self_custody.py +``` + +### 3. `snp_replay.py` -- replay a real attestation, offline + +The CPU half of Layer 2's composite verification (SPEC 3.2), run offline against a recorded AMD SEV-SNP quote. It exercises exactly what a key broker runs before releasing a key: parse the SNP report, verify the VCEK to ASK to ARK certificate chain to a trusted AMD root, verify the report's own signature, and confirm REPORT_DATA binds the challenge nonce (anti-replay). A synthetic bundle is committed in `fixtures/` so it runs anywhere; pass a captured bundle to replay genuine silicon. + +```bash +python snp_replay.py # committed synthetic bundle +python snp_replay.py path/to/snp_quote.json # a bundle captured on a real Azure SEV-SNP CVM +``` + +### 4. `real_open_model.py` -- run-local, over real weights + +The same flow over a **real** open model's actual bytes: it downloads an open model (SmolLM2-135M by default, ~270MB), hashes its real safetensors into the manifest, includes a tamper demo (a one-byte-flipped fork no longer matches the manifest), and with `--infer` loads the model and generates so the certified serving stack is a real running model. Network and heavy, so it is run-local and excluded from CI. + +```bash +pip install -r requirements-infer.txt +python real_open_model.py # download, hash, full flow, tamper demo +python real_open_model.py --infer # also load the model and generate +python real_open_model.py --local path/to/model.safetensors # skip the download +``` + +--- + +## What runs in CI + +The three offline demos (`open_model_e2e.py`, `sovereign_self_custody.py`, `snp_replay.py`) run in CI against the published PyPI package and must exit 0. `real_open_model.py` is not in CI (it downloads a model). + +## Reference + +- Package: [weight-custody-manifest on PyPI](https://pypi.org/project/weight-custody-manifest/) +- `manifest.example.json` in this folder is a sample signed manifest for reference. + +## License + +Apache 2.0. See [LICENSE](../LICENSE) in the repo root. diff --git a/weight-custody-manifest/fixtures/snp_quote_synthetic.json b/weight-custody-manifest/fixtures/snp_quote_synthetic.json new file mode 100644 index 0000000..0b9b00e --- /dev/null +++ b/weight-custody-manifest/fixtures/snp_quote_synthetic.json @@ -0,0 +1,11 @@ +{ + "kind": "wcm-snp-quote-bundle/v1", + "source": "synthetic", + "note": "Self-consistent synthetic ARK->VCEK chain and report. NOT real hardware. Replace with tools/capture_snp_quote.py output for a genuine Azure SEV-SNP quote.", + "report_b64": "AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACaLbLiPxUEzQVmBlU6wEnF5xjo+c6SM4dt8aehghr4hQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOVfr/UlF6aVEeNEtcgt1yVfTcnaevR3bh8NkO8NBC9ugKwnqgg4seL0yJuRGoDOMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAugtnFRQ0+9Ir8kMPO+NbzMBsl+B3RvfrmVwsW4klzwgY61mK2LM16sbWbMWhqExDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "vcek_pem": "-----BEGIN CERTIFICATE-----\nMIIBkjCCARigAwIBAgIUB28IzgyENEygWjv7jqffhGZpw7swCgYIKoZIzj0EAwMw\nNDEyMDAGA1UEAwwpc3ludGhldGljLXNucC1yb290IChzdGFuZC1pbiBmb3IgQU1E\nIEFSSykwIBcNMjAwMTAxMDAwMDAwWhgPMjA5OTAxMDEwMDAwMDBaMB8xHTAbBgNV\nBAMMFFNFVi1WQ0VLIChzeW50aGV0aWMpMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE\nfNprLZF3rSMcTK0kLOZfBlfX7+KVQEbq9kmJOjKxDF2T1bKC0kF0jeFT6aq574wx\nTiIqGq7MMPWAdSQKHcQ6s8mcQZBZtODpJwDF683MnNc7hnHqN/AdLq8v77WTc5gp\nMAoGCCqGSM49BAMDA2gAMGUCMQDrVxfCf2knshEQvL+vyUuTU/nk/+QHpWDP+WAk\naMNdRUrzh3SGlZIAwgtqq+seorgCMF0X9n1VutxigXAXaWkHje6BpuAoyKFWYdmN\n1gePcPp9kUJZ/nSu9RIl7NvwMueinw==\n-----END CERTIFICATE-----\n", + "intermediates_pem": [], + "root_pem": "-----BEGIN CERTIFICATE-----\nMIIBvTCCAUKgAwIBAgIUIB3+SQg0A8tekhc1NbDQ9LSCE8IwCgYIKoZIzj0EAwMw\nNDEyMDAGA1UEAwwpc3ludGhldGljLXNucC1yb290IChzdGFuZC1pbiBmb3IgQU1E\nIEFSSykwIBcNMjAwMTAxMDAwMDAwWhgPMjA5OTAxMDEwMDAwMDBaMDQxMjAwBgNV\nBAMMKXN5bnRoZXRpYy1zbnAtcm9vdCAoc3RhbmQtaW4gZm9yIEFNRCBBUkspMHYw\nEAYHKoZIzj0CAQYFK4EEACIDYgAEuUSltY1Uj1sQzYTsecVdrNq9x0sgogawXEOl\nG/hcShU4GgPfFxUhryvWyeujHsaaz05LXPXVf/wJujBIrSxrAfxHdkTlfjuLzP5P\nEimoco+iYXuBjgo+Sz7nJaL36ZBaoxMwETAPBgNVHRMBAf8EBTADAQH/MAoGCCqG\nSM49BAMDA2kAMGYCMQDmJrgizN8uN1kQbxV8qW+1yac3XBpoDxwzMVYWD/U+yHbY\npAdF3OgGvlH5MWxTcM0CMQC4spaNY5CzLmMctCIyHz8Js9wbDpfGTAdbYB1rTwUr\ndF/mAzWU2hWC53hx5/T8oxw=\n-----END CERTIFICATE-----\n", + "expected_nonce": "abababababababababababababababababababababababababababababababab", + "expected_measurement": "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" +} diff --git a/weight-custody-manifest/manifest.example.json b/weight-custody-manifest/manifest.example.json new file mode 100644 index 0000000..75811db --- /dev/null +++ b/weight-custody-manifest/manifest.example.json @@ -0,0 +1,71 @@ +{ + "manifest_version": "0.1", + "weights_hash": "sha256:4a1c9b024a1c9b024a1c9b024a1c9b024a1c9b024a1c9b024a1c9b024a1c9b02", + "builder": { + "identity": "example-builder", + "signing_key": "ed25519:builder-key-placeholder" + }, + "release_terms": { + "license": "customer-deployment-agreement-ref:CDA-2026-0091", + "permitted_derivatives": "fine-tune-only, no re-export of base weights", + "permitted_environments": ["opaque-cmcp-attested-enclave"], + "jurisdiction_restriction": "US, EU" + }, + "release_policy": { + "required_assurance_tier": "hardware-attested", + "physical_hardening": "not-required", + "trusted_time_source": "secure-tsc", + "memory_fingerprint_challenge": "not-required", + "required_hw_platform": ["amd-sev-snp", "nvidia-cc-gpu"], + "required_gpu_measurement": { + "rim_pin": "nvidia-rim:driver+vbios golden measurement id", + "note": "GPU attestation report is a separate evidence chain from the CVM report; verify both plus the serving-image measurement, bound by a nonce echo (SPEC 3.2)" + }, + "tenancy": "shared", + "required_serving_image": { + "signer": "ed25519:builder-key-placeholder", + "release_rule": "prefer-current: refuse a retiring image when a current one is available; require signed/attested time for retire_after, never the host clock", + "accepted_measurements": [ + { + "measurement": "sha256:5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d5e2d", + "status": "current" + }, + { + "measurement": "sha256:6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e6f3e", + "status": "retiring", + "retire_after": "2026-07-16T00:00:00Z" + }, + { + "measurement": "sha256:70af70af70af70af70af70af70af70af70af70af70af70af70af70af70af70af", + "status": "revoked" + } + ], + "note": "key decrypts only under a builder-signed serving stack whose measurement is current or retiring; status:revoked hard-fails immediately" + }, + "key_release_mode": "attestation-gated", + "replay_protection": "kbs-nonce-required", + "attestation_revocation_check": "live-per-release, max-cache-age: short-window", + "revocation_authority": "builder-and-opaque-joint", + "sovereign_profile": { + "enabled": false, + "revocation_authority": "quorum", + "sovereign_signer": null, + "note": "when enabled, no single party (builder included) can revoke or dark the model unilaterally" + } + }, + "custody": { + "custodian": "opaque-systems", + "custodian_type": "opaque-hosted", + "kbs_image": { + "measurement": "sha256:abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234", + "signer": "ed25519:opaque-key-placeholder", + "note": "the key release service runs in an attested enclave in every profile, Opaque-hosted included" + }, + "enclave_id": "did:opaque:example-enclave-04", + "attestation_cadence": "24h", + "kbs_attestation_cadence": "24h" + }, + "base_confidentiality": "confidential", + "deployment_model": "builder-to-customer", + "signatures": [] +} diff --git a/weight-custody-manifest/open_model_e2e.py b/weight-custody-manifest/open_model_e2e.py new file mode 100644 index 0000000..af826f3 --- /dev/null +++ b/weight-custody-manifest/open_model_e2e.py @@ -0,0 +1,240 @@ +"""End-to-end Weight Custody Manifest flow on an OPEN-weight model. + +Run it: + + python open_model_e2e.py + +Everything here is real WCM code with a **software (mock) attestation provider**, +so it runs anywhere with no hardware. The point is to show how the whole flow +fits together, and to be honest about what each step is actually doing when the +base model is public. + +The reframe that drives this demo +--------------------------------- +For a closed frontier model the job is secrecy: don't let the weights leak. For +an OPEN-weight model (Llama, Mistral, SmolLM, ...) the base weights are already +downloadable, so encrypting them and gating decryption behind attestation +protects nothing - anyone can just download the same checkpoint. Saying that +plainly matters. What the same six-step machinery still does, and why you'd run +it anyway: + + * Integrity / provenance - prove you are running exactly the certified + checkpoint and serving stack, unmodified, not a silently tampered fork. + * License / field-of-use - make the model's license a technical release + condition, not just contract text. + * Derivative custody - the fine-tune trained on your proprietary data is real, + novel IP that never existed publicly. THIS is what you're protecting. + * A kill switch - now triggered by a safety recall or license violation, not + by "we detected theft". + +For a fully-public base, the base's own confidentiality is theater; this demo +labels it as such and puts the derivative at the center. +""" +from __future__ import annotations + +import hashlib + +from wcm import ( + EnclaveSession, + KeyBrokerService, + KeyWipedError, + SoftwareProvider, + VerificationContext, + WeightCustodyManifest, + generate_ed25519, + Ed25519Signer, + is_root, + verify_lineage, + verify_manifest, +) + + +def rule(title: str) -> None: + print(f"\n{'=' * 70}\n{title}\n{'=' * 70}") + + +def sha256(data: bytes) -> str: + return "sha256:" + hashlib.sha256(data).hexdigest() + + +def build_manifest( + *, + weights_hash: str, + license_text: str, + serving_measurement: str, + builder_id: str, + custodian_id: str, + derivatives: str, + derived_from: str | None = None, + rights_holder: dict | None = None, +) -> dict: + """Assemble a manifest document (the Layer 1 artifact).""" + m: dict = { + "manifest_version": "0.1", + "weights_hash": weights_hash, + "builder": {"identity": builder_id, "signing_key": "ed25519:demo"}, + "release_terms": { + "license": license_text, + "permitted_derivatives": "fine-tune-only", + "derivatives": derivatives, # machine-checkable (none|fine-tune-only|unrestricted) + "permitted_environments": ["enterprise-governed-enclave"], + }, + "release_policy": { + "required_assurance_tier": "hardware-attested", + "trusted_time_source": "secure-tsc", + "required_hw_platform": ["amd-sev-snp", "nvidia-cc-gpu"], + "required_gpu_measurement": {"rim_pin": "nvidia-rim:demo-golden"}, + "required_serving_image": { + "signer": "ed25519:demo", + "release_rule": "prefer-current", + "accepted_measurements": [ + {"measurement": serving_measurement, "status": "current"} + ], + }, + "attestation_revocation_check": "live-per-release, max-cache-age: short-window", + "revocation_authority": "builder-and-opaque-joint", + }, + "custody": { + "custodian": custodian_id, + "custodian_type": "customer-self-custody", + "kbs_image": {"measurement": sha256(b"reference-kbs-image"), "signer": "ed25519:demo"}, + "enclave_id": "did:example:enterprise-enclave-01", + "attestation_cadence": "1h", + }, + # State the two things this whole demo turns on, rather than leave them + # implicit: the base is public, and one org holds both roles. + "base_confidentiality": "open", + "deployment_model": "byom-symmetric", + } + if derived_from is not None: + m["derived_from"] = derived_from + if rights_holder is not None: + m["rights_holder"] = rights_holder + return m + + +def sign(manifest: WeightCustodyManifest, keypair, role: str, signer: str) -> dict: + return Ed25519Signer(keypair).sign(manifest.unsigned_dict(), role=role, signer=signer) + + +def main() -> None: + # In the open-weight case the "builder" and "custodian" collapse into the + # enterprise's own model-governance function (the symmetric BYOM case, + # design principle 4). It certifies which checkpoint + serving stack are + # approved internally, and it runs them. We model that with two keys held by + # the same governance org. + gov_builder = generate_ed25519() + gov_custodian = generate_ed25519() + + rule("Open-weight model: base weights are PUBLIC") + # Pretend this blob is a downloaded checkpoint (in reality: your + # Llama-3.1 / Mistral / SmolLM safetensors). We hash the real bytes. + checkpoint = b"" + base_hash = sha256(checkpoint) + serving = sha256(b"vllm-0.6.3 + policy-bundle-v2 (the certified serving stack)") + print("base weights_hash :", base_hash) + print("license : Llama-3.1-Community (usage + scale restrictions)") + print("NOTE: encrypting a *public* base protects nothing. The mechanism below") + print(" does INTEGRITY + LICENSE work here, not secrecy.") + + # ---- Step 0: certify the base checkpoint (integrity + license) ---------- + rule("Step 0 - Certify the base: manifest (integrity + license), jointly signed") + base_doc = build_manifest( + weights_hash=base_hash, + license_text="Llama-3.1-Community", + serving_measurement=serving, + builder_id="acme-model-governance", + custodian_id="acme-model-governance", + derivatives="fine-tune-only", + ) + base = WeightCustodyManifest.model_validate(base_doc) + base = base.with_signatures([ + sign(base, gov_builder, "builder", "acme-model-governance"), + sign(base, gov_custodian, "custodian", "acme-model-governance"), + ]) + print("manifest binds: weights_hash, the certified serving image, and the license.") + print("signed jointly by the governance function as builder + custodian.") + + # ---- Step 1: verify the manifest (provenance) --------------------------- + rule("Step 1 - Verify the manifest (is this the certified checkpoint?)") + ctx = VerificationContext() + ctx.add_key(gov_builder.public_bytes) + ctx.add_key(gov_custodian.public_bytes) + result = verify_manifest(base, ctx) + print("manifest signature valid:", result.ok, " (integrity, not secrecy)") + # The verifier does not block on base_confidentiality; it tells you, in + # words, what this manifest is and is not protecting. For an open base that + # is exactly the disclaimer you want on the record. + for note in result.notes: + print(" note:", note) + + # ---- Step 2: attestation-gated load ------------------------------------- + rule("Step 2 - Attestation gate: only load the CERTIFIED serving stack") + kbs = KeyBrokerService({base.weights_hash: b"loading-key-protects-no-secret-here"}) + challenge = kbs.issue_challenge() + evidence = SoftwareProvider().produce( # a REAL enclave would produce a hardware quote + challenge, + serving_image_measurement=serving, + gpu_measurement="nvidia-rim:demo-golden", + ) + decision = kbs.verify_and_release(base, evidence) + print("gate released:", decision.released) + print("what the gate enforced: genuine attestation nonce, the approved platform,") + print("and a serving-image measurement matching what governance signed - so a") + print("silently modified fork of the public weights would NOT load.") + + # ---- Step 3: runtime custody (the kill switch) -------------------------- + rule("Step 3 - Wipe-on-lapse custody (kill switch: recall / license violation)") + session = EnclaveSession.from_release(base, decision) + session.use_key() + print("serving under a 1h cadence; time_floor =", session.time_floor.value) + print("kill-switch reason has shifted: not 'theft detected' but a safety recall,") + print("a license breach, or governance pulling a misbehaving checkpoint.") + + # ---- Step 4: license / field-of-use (technical checkpoint) -------------- + rule("Step 4 - License is a technical release condition, not just contract text") + print("release_terms.license =", base.release_terms.license) + print("the environment is only marked releasable under the disclosed,") + print("license-conforming configuration bound into the signed manifest.") + + # ---- Step 5: fine-tune -> the DERIVATIVE is the real asset --------------- + rule("Step 5 - Fine-tune on proprietary data: the derivative is novel IP") + derivative_weights = checkpoint + b"<+ acme proprietary trading-desk fine-tune>" + deriv_hash = sha256(derivative_weights) + deriv_doc = build_manifest( + weights_hash=deriv_hash, + license_text="Llama-3.1-Community + Acme-proprietary-derivative", + serving_measurement=serving, + builder_id="acme-model-governance", + custodian_id="acme-model-governance", + derivatives="none", # Acme does not permit derivatives OF its derivative + derived_from=base.weights_hash, + rights_holder={"base": "meta", "derivative": "acme"}, + ) + deriv = WeightCustodyManifest.model_validate(deriv_doc) + deriv = deriv.with_signatures([ + sign(deriv, gov_builder, "builder", "acme-model-governance"), + sign(deriv, gov_custodian, "custodian", "acme-model-governance"), + ]) + print("derivative weights_hash:", deriv_hash) + print("derived_from :", deriv.derived_from) + print("rights_holder :", {"base": deriv.rights_holder.base, "derivative": deriv.rights_holder.derivative}) + print("THIS never existed publicly. It is the whole reason to run the stack.") + + # ---- Step 6: lineage + revocation on the derivative --------------------- + rule("Step 6 - Verify lineage (derivative -> public base root)") + manifests = {base.weights_hash: base, deriv.weights_hash: deriv} + lineage = verify_lineage(manifests, deriv.weights_hash) + print("lineage ok :", lineage.ok) + print("chain :", " -> ".join(h.split(':')[1][:12] + '...' for h in lineage.chain)) + print("depth :", lineage.depth, " root is a base manifest:", is_root(base)) + + rule("What was doing real work") + print("moot for a public base : encrypt-at-rest secrecy of the base weights") + print("real work : integrity/provenance (0,1,2), license (0,4),") + print(" derivative custody + lineage (5,6), kill switch (3)") + print("\nSame six steps as the closed-model flow; the purpose of half of them flipped.") + + +if __name__ == "__main__": + main() diff --git a/weight-custody-manifest/real_open_model.py b/weight-custody-manifest/real_open_model.py new file mode 100644 index 0000000..40de66b --- /dev/null +++ b/weight-custody-manifest/real_open_model.py @@ -0,0 +1,250 @@ +"""End-to-end Weight Custody Manifest flow on a REAL open-weight model, locally. + +Downloads a real open model, hashes its ACTUAL weights, and runs the whole WCM +flow over those bytes. The ONLY mocked part is the hardware attestation +(SoftwareProvider): a laptop has no SEV-SNP/TDX/H100, and that hardware-rooted +step is the one we validate separately on real cloud silicon. Everything else is +real WCM code over real weights: + + integrity/provenance -> hash the actual safetensors, bind it in the manifest + joint signing -> real Ed25519 builder + custodian signatures + release gate -> the KBS composite-verify logic (software evidence) + wipe-on-lapse -> the kill switch + license-as-condition -> the model's license bound into the signed manifest + derivative custody -> a fine-tune's weights get their own manifest + lineage + +Usage: + pip install huggingface_hub safetensors + python real_open_model.py # SmolLM2-135M (~270MB) + python real_open_model.py --model --license "" + python real_open_model.py --local path/to/model.safetensors +""" +from __future__ import annotations + +import argparse +import hashlib +import pathlib + +from wcm import ( + Ed25519Signer, + EnclaveSession, + KeyBrokerService, + SoftwareProvider, + VerificationContext, + WeightCustodyManifest, + combine_shares, # noqa: F401 - kept importable for the curious + generate_ed25519, + is_root, + verify_lineage, + verify_manifest, +) + + +def rule(title: str) -> None: + print(f"\n{'=' * 72}\n{title}\n{'=' * 72}") + + +def sha256_file(path: pathlib.Path, *, flip_first_byte: bool = False) -> str: + """Hash a file. With flip_first_byte, hash it as if one byte were modified (a + silently tampered fork) without writing a copy.""" + h = hashlib.sha256() + flipped = False + with open(path, "rb") as fh: + for chunk in iter(lambda: fh.read(1 << 20), b""): + if flip_first_byte and not flipped and chunk: + b = bytearray(chunk) + b[0] ^= 0xFF + chunk = bytes(b) + flipped = True + h.update(chunk) + return "sha256:" + h.hexdigest() + + +def run_inference(model_id: str) -> None: + """Optionally load the model and generate, so the certified serving stack is + a real running model, not a placeholder. Lazy imports; skips if unavailable.""" + try: + from transformers import AutoModelForCausalLM, AutoTokenizer + except ImportError: + print(" transformers not installed; `pip install transformers` to enable --infer") + return + print(" loading the model and generating (real serving stack)...") + tok = AutoTokenizer.from_pretrained(model_id) + model = AutoModelForCausalLM.from_pretrained(model_id) + ids = tok("Confidential computing protects", return_tensors="pt") + out = model.generate(**ids, max_new_tokens=12, do_sample=False) + print(" model output:", repr(tok.decode(out[0], skip_special_tokens=True))) + + +def resolve_weights(args: argparse.Namespace) -> tuple[pathlib.Path, str]: + if args.local: + p = pathlib.Path(args.local) + if not p.exists(): + raise SystemExit(f"--local path not found: {p}") + return p, f"local:{p.name}" + try: + from huggingface_hub import hf_hub_download + except ImportError: + raise SystemExit("pip install huggingface_hub to download a model, or pass --local") + print(f"downloading {args.model} model.safetensors (first run only, cached after)...") + path = pathlib.Path(hf_hub_download(repo_id=args.model, filename="model.safetensors")) + return path, args.model + + +def build_manifest(*, weights_hash, license_text, serving, builder_id, custodian_id, + derivatives, derived_from=None, rights_holder=None) -> dict: + m: dict = { + "manifest_version": "0.1", + "weights_hash": weights_hash, + "builder": {"identity": builder_id, "signing_key": "ed25519:demo"}, + "release_terms": { + "license": license_text, + "permitted_derivatives": "fine-tune-only", + "derivatives": derivatives, + "permitted_environments": ["enterprise-governed-enclave"], + }, + "release_policy": { + "required_assurance_tier": "hardware-attested", + "trusted_time_source": "secure-tsc", + "required_hw_platform": ["amd-sev-snp", "nvidia-cc-gpu"], + "required_gpu_measurement": {"rim_pin": "nvidia-rim:demo-golden"}, + "required_serving_image": { + "signer": "ed25519:demo", + "release_rule": "prefer-current", + "accepted_measurements": [{"measurement": serving, "status": "current"}], + }, + "attestation_revocation_check": "live-per-release, max-cache-age: short-window", + "revocation_authority": "builder-and-opaque-joint", + }, + "custody": { + "custodian": custodian_id, + "custodian_type": "customer-self-custody", + "kbs_image": {"measurement": "sha256:" + "ab" * 32, "signer": "ed25519:demo"}, + "enclave_id": "did:example:enterprise-enclave-01", + "attestation_cadence": "1h", + }, + # The open-weight reframe: base is public, one org holds both roles. + "base_confidentiality": "open", + "deployment_model": "byom-symmetric", + } + if derived_from is not None: + m["derived_from"] = derived_from + if rights_holder is not None: + m["rights_holder"] = rights_holder + return m + + +def sign(manifest, kp, role, signer): + return Ed25519Signer(kp).sign(manifest.unsigned_dict(), role=role, signer=signer) + + +def main() -> int: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--model", default="HuggingFaceTB/SmolLM2-135M") + ap.add_argument("--local", default=None, help="path to a local .safetensors instead of downloading") + ap.add_argument("--license", default="Apache-2.0") + ap.add_argument("--infer", action="store_true", + help="also load the model and generate (real serving stack; needs transformers)") + args = ap.parse_args() + + weights_path, model_id = resolve_weights(args) + size_mb = weights_path.stat().st_size / 1e6 + + rule(f"Real open model: {model_id} ({size_mb:.1f} MB of real weights)") + base_hash = sha256_file(weights_path) + print("weights file :", weights_path) + print("REAL weights_hash :", base_hash) + print("license :", args.license) + print("NOTE: the base is public, so this protects integrity + license +") + print(" derivative custody, not secrecy. Attestation below is the software") + print(" mock (no TEE on this machine); the hardware path is validated in cloud.") + + gov_builder, gov_custodian = generate_ed25519(), generate_ed25519() + serving = hashlib.sha256(b"vllm + policy-bundle (the certified serving stack)").hexdigest() + serving = "sha256:" + serving + + # ---- Step 0-1: certify + verify the base (integrity + license) ---------- + rule("Step 1 - Certify + verify the base manifest (integrity, provenance)") + base = WeightCustodyManifest.model_validate(build_manifest( + weights_hash=base_hash, license_text=args.license, serving=serving, + builder_id="acme-model-governance", custodian_id="acme-model-governance", + derivatives="fine-tune-only")) + base = base.with_signatures([ + sign(base, gov_builder, "builder", "acme-model-governance"), + sign(base, gov_custodian, "custodian", "acme-model-governance")]) + ctx = VerificationContext() + ctx.add_key(gov_builder.public_bytes) + ctx.add_key(gov_custodian.public_bytes) + result = verify_manifest(base, ctx) + print("manifest signature valid:", result.ok) + for note in result.notes: + print(" note:", note) + + # ---- Step 2: attestation-gated load (only the certified stack) ---------- + rule("Step 2 - Attestation gate (load only the certified serving stack)") + kbs = KeyBrokerService({base.weights_hash: b"loading-key-not-a-secret-for-open-weights"}) + challenge = kbs.issue_challenge() + evidence = SoftwareProvider().produce( + challenge, serving_image_measurement=serving, gpu_measurement="nvidia-rim:demo-golden") + decision = kbs.verify_and_release(base, evidence) + print("gate released :", decision.released) + print("a silently modified fork of these public weights would NOT load here.") + + # ---- Step 3: wipe-on-lapse custody (kill switch) ------------------------ + rule("Step 3 - Wipe-on-lapse custody (kill switch: recall / license breach)") + session = EnclaveSession.from_release(base, decision) + session.use_key() + print("serving under a 1h cadence; time_floor =", session.time_floor.value) + + # ---- Step 4: license as a technical release condition ------------------- + rule("Step 4 - License is a technical release condition") + print("release_terms.license =", base.release_terms.license) + if args.infer: + if args.local: + print(" (--infer needs a --model repo id for the tokenizer; skipping with --local)") + else: + run_inference(model_id) + + # ---- Step 5: fine-tune -> the DERIVATIVE is the real asset --------------- + rule("Step 5 - Fine-tune: the derivative weights get their own custody") + # A real fine-tune would retrain; here we stand in for the *resulting bytes* + # (base weights + your proprietary delta) and hash them, which is what a + # derivative manifest actually binds. + deriv_hash = hashlib.sha256( + base_hash.encode() + b"<+ acme proprietary fine-tune delta>").hexdigest() + deriv_hash = "sha256:" + deriv_hash + deriv = WeightCustodyManifest.model_validate(build_manifest( + weights_hash=deriv_hash, license_text=args.license + " + Acme-derivative", + serving=serving, builder_id="acme-model-governance", + custodian_id="acme-model-governance", derivatives="none", + derived_from=base.weights_hash, rights_holder={"base": model_id, "derivative": "acme"})) + deriv = deriv.with_signatures([ + sign(deriv, gov_builder, "builder", "acme-model-governance"), + sign(deriv, gov_custodian, "custodian", "acme-model-governance")]) + print("derivative weights_hash:", deriv_hash) + print("derived_from :", deriv.derived_from) + + # ---- Step 6: lineage from derivative back to the real base -------------- + rule("Step 6 - Verify lineage (derivative -> real open base)") + lineage = verify_lineage({base.weights_hash: base, deriv.weights_hash: deriv}, deriv.weights_hash) + print("lineage ok :", lineage.ok, " depth:", lineage.depth, " base is root:", is_root(base)) + + # ---- Step 7: integrity, made concrete ----------------------------------- + rule("Step 7 - A silently tampered fork is caught by the hash") + tampered = sha256_file(weights_path, flip_first_byte=True) + print("certified weights_hash :", base_hash) + print("tampered-fork hash :", tampered) + print("tampered matches manifest? :", tampered == base.weights_hash) + print("the enclave binds the manifest's weights_hash, so weights that do not hash") + print("to it (a poisoned or backdoored fork of these public weights) never load.") + + rule("What was real vs mocked here") + print("REAL : the weights_hash over", model_id, "actual bytes; joint signatures;") + print(" the release-gate logic; wipe-on-lapse; derivative manifest + lineage.") + print("MOCKED : the attestation evidence (no TEE on this machine). The hardware") + print(" root of trust is validated separately (SEV-SNP + TDX, in cloud).") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/weight-custody-manifest/requirements-infer.txt b/weight-custody-manifest/requirements-infer.txt new file mode 100644 index 0000000..2204c40 --- /dev/null +++ b/weight-custody-manifest/requirements-infer.txt @@ -0,0 +1,6 @@ +# Run-local extras for real_open_model.py only. These download a real open model +# and, with --infer, load it and generate. Not needed for the three offline demos +# and deliberately not installed in CI (network + heavy). +huggingface_hub +safetensors +transformers diff --git a/weight-custody-manifest/requirements.txt b/weight-custody-manifest/requirements.txt new file mode 100644 index 0000000..a2be462 --- /dev/null +++ b/weight-custody-manifest/requirements.txt @@ -0,0 +1,3 @@ +# The Weight Custody Manifest reference SDK, from PyPI. +# Covers the three offline demos (open_model_e2e, sovereign_self_custody, snp_replay). +weight-custody-manifest>=0.19.0 diff --git a/weight-custody-manifest/snp_replay.py b/weight-custody-manifest/snp_replay.py new file mode 100644 index 0000000..f96f669 --- /dev/null +++ b/weight-custody-manifest/snp_replay.py @@ -0,0 +1,160 @@ +"""Replay a captured AMD SEV-SNP quote through the full WCM verification path. + + python snp_replay.py # the committed synthetic bundle + python snp_replay.py path/to/snp_quote.json # a real captured bundle + +This is the CPU half of Layer 2's composite verification (SPEC 3.2), run OFFLINE +against a recorded quote. It exercises exactly the code a KBS runs before +releasing a key: parse the SNP report, verify the VCEK -> ASK -> ARK certificate +chain to a trusted AMD root, verify the report's own signature under the VCEK, +and confirm REPORT_DATA binds the challenge nonce (anti-replay). + +A "bundle" is the evidence a KBS would receive, captured to a JSON file so the +verification is reproducible with no hardware: + + { "kind": "wcm-snp-quote-bundle/v1", "source": "...", + "report_b64": "...", # raw SNP report bytes, base64 + "vcek_pem": "...", # leaf cert that signed the report + "intermediates_pem": ["..."], # ASK (empty for the synthetic root) + "root_pem": "...", # trusted root (real AMD ARK, or synthetic) + "expected_nonce": "hex|null", # guest-controlled REPORT_DATA nonce, or null + "expected_measurement": "hex" } # optional launch measurement to assert + +Two freshness topologies, and the demo handles both honestly: + + * Guest-controlled REPORT_DATA (bare-metal /dev/sev-guest, and the synthetic + bundle): the guest writes sha256(nonce) into REPORT_DATA, so the KBS's nonce + gate applies directly. `expected_nonce` is set. + * Azure CVM vTPM path: REPORT_DATA is paravisor-bound to the vTPM attestation + key, NOT to our KBS nonce, so freshness comes from a separate vTPM quote over + the AK (one layer up). `expected_nonce` is null; the demo verifies the chain + and report signature (which ARE genuine on Azure) and reports the AK binding + rather than pretending the SNP report itself binds our nonce. + +The committed default bundle is `source: synthetic` (a self-consistent stand-in +chain, NOT real silicon) so the demo runs anywhere. Capture a genuine one on an +Azure SEV-SNP CVM with tools/capture_snp_quote.py. +""" +from __future__ import annotations + +import base64 +import datetime +import json +import pathlib +import sys + +from cryptography import x509 + +from wcm import ( + QuoteVerifier, + SnpQuoteParser, + TrustStore, + parse_snp_report, + verify_cert_chain, + verify_snp_report_signature, +) + +DEFAULT_BUNDLE = pathlib.Path(__file__).resolve().parent / "fixtures" / "snp_quote_synthetic.json" + + +def rule(title: str) -> None: + print(f"\n{'=' * 70}\n{title}\n{'=' * 70}") + + +def load_bundle(path: pathlib.Path) -> dict: + bundle = json.loads(path.read_text(encoding="utf-8")) + if bundle.get("kind") != "wcm-snp-quote-bundle/v1": + raise SystemExit(f"{path}: not a wcm-snp-quote-bundle/v1") + return bundle + + +def main() -> int: + path = pathlib.Path(sys.argv[1]) if len(sys.argv) > 1 else DEFAULT_BUNDLE + bundle = load_bundle(path) + + rule(f"SEV-SNP quote replay (source: {bundle['source']})") + print("bundle :", path) + if bundle["source"] == "synthetic": + print("WARNING : synthetic bundle, NOT real hardware. This proves the") + print(" verification path, not a genuine attestation. Capture a real") + print(" one with tools/capture_snp_quote.py on an Azure SEV-SNP CVM.") + else: + print("this is a genuine hardware quote captured from real SEV-SNP silicon.") + + vcek = x509.load_pem_x509_certificate(bundle["vcek_pem"].encode()) + intermediates = [x509.load_pem_x509_certificate(p.encode()) for p in bundle["intermediates_pem"]] + root = x509.load_pem_x509_certificate(bundle["root_pem"].encode()) + report_b64 = bundle["report_b64"] + nonce = bundle["expected_nonce"] + + trust = TrustStore() + trust.add_root(root) + report = base64.b64decode(report_b64) + + if nonce: + # Guest-controlled REPORT_DATA: the KBS's full gate applies, nonce and all. + rule("Step 1 - The gate the KBS runs (parse + chain + signature + nonce)") + result = QuoteVerifier(SnpQuoteParser(vcek, intermediates), trust).verify( + report_b64, expected_nonce=nonce + ) + print("verified :", result.verified) + if not result.verified: + print("reason :", result.reason) + print("\nThe KBS would REFUSE to release the key.") + return 1 + print("leaf (VCEK) subject :", result.leaf_subject) + print("checks passed : VCEK chains to the trusted root; report signature") + print(" verifies under the VCEK; REPORT_DATA binds the nonce") + print(f" {nonce[:16]}... (a captured quote for a different") + print(" nonce would be rejected as a replay).") + else: + # Azure vTPM path: REPORT_DATA binds the vTPM AK, not our nonce. Verify the + # parts that are genuine on this platform (chain + report signature) and be + # explicit that freshness lives one layer up, in the vTPM quote over the AK. + rule("Step 1 - Chain + report signature (Azure vTPM freshness topology)") + chain_error = verify_cert_chain(vcek, intermediates, trust, datetime.datetime.now(datetime.timezone.utc)) + print("VCEK chains to root :", chain_error is None, "" if chain_error is None else f"({chain_error})") + sig_ok = verify_snp_report_signature(report, vcek) + print("report signature ok :", sig_ok) + if chain_error is not None or not sig_ok: + print("\nThe KBS would REFUSE to release the key.") + return 1 + rd = parse_snp_report(report).report_data + print("REPORT_DATA :", rd.hex()[:32], "...") + print("note : on Azure CVMs REPORT_DATA is paravisor-bound to the") + print(" vTPM AK, not our KBS nonce. Freshness comes from a") + print(" separate vTPM quote over that AK (SPEC 3.2). This") + print(" demo replays the SNP report; the vTPM-quote layer is") + print(" not part of the recorded bundle.") + + # Surface the measured fields the manifest's policy would pin. + rule("Step 2 - Measured fields the manifest pins") + parsed = parse_snp_report(report) + print("SNP report version :", parsed.version) + print("launch measurement :", parsed.measurement.hex()) + print("chip id (VCEK) :", parsed.chip_id.hex()[:32], "...") + expected_m = bundle.get("expected_measurement") + if expected_m: + ok = parsed.measurement.hex() == expected_m + print("measurement matches manifest expectation:", ok) + if not ok: + print(" -> a KBS pinning required launch measurement would refuse here") + return 1 + + rule("What this did and did not prove") + if nonce: + print("proved : the SEV-SNP CPU quote is genuine, unmodified, bound to our") + print(" nonce, and rooted in the trusted AMD chain (Layer 2 CPU half).") + else: + print("proved : the SEV-SNP CPU quote is genuine and unmodified, signed by a") + print(" VCEK rooted in the trusted AMD chain, on real silicon (Layer 2") + print(" CPU half). Freshness is the vTPM-quote layer's job, not shown here.") + print("not here : the separate GPU (NVIDIA CC) report and the CPU-GPU nonce") + print(" binding (SPEC 3.2 composite verification); GPU needs H100 quota.") + if bundle["source"] == "synthetic": + print("not here : real silicon. Swap in a captured bundle for that.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/weight-custody-manifest/sovereign_self_custody.py b/weight-custody-manifest/sovereign_self_custody.py new file mode 100644 index 0000000..d5f1329 --- /dev/null +++ b/weight-custody-manifest/sovereign_self_custody.py @@ -0,0 +1,184 @@ +"""Sovereign self-custody with threshold split-key release (SPEC 3.5, decision 15). + + python sovereign_self_custody.py + +Everything here is real WCM code with a software (mock) attestation provider, so +it runs anywhere with no hardware. + +The problem this solves +----------------------- +A sovereign runs the model in its own jurisdiction, on hardware it owns, and +operates the key release service (KBS) itself. That is the hostile-owner +posture: the owner can forge its own KBS attestation quote (open question 8.8, +key-extraction half). Single-key self-custody is therefore unsound for a +sovereign, because one forged quote would self-release the key. + +Decision 15's answer is threshold split-key release: the key is split so that no +single party, the sovereign included, holds it. The attested KBS enclave +reconstructs the key only from a quorum of shares contributed by independent +parties. One forged quote is then not enough, because it still yields only one +share. This demo shows that end to end with a 2-of-3 split across the builder, +the sovereign, and the custodian. + +What is real vs modelled here: the split, the reconstruction, the attestation +gate, and the manifest's sovereign quorum are all real SDK code. The share +contribution "into the attested enclave" is narrated, not sandboxed; a real +deployment runs the combine inside the measured KBS enclave so no party sees the +assembled key. +""" +from __future__ import annotations + +import hashlib + +from wcm import ( + Ed25519Signer, + KeyBrokerService, + SoftwareProvider, + VerificationContext, + WeightCustodyManifest, + combine_shares, + generate_ed25519, + split_secret, + verify_manifest, +) + + +def rule(title: str) -> None: + print(f"\n{'=' * 70}\n{title}\n{'=' * 70}") + + +def sha256(data: bytes) -> str: + return "sha256:" + hashlib.sha256(data).hexdigest() + + +def main() -> int: + # Three independent parties. In a sovereign deployment none of them may hold + # the whole key on its own. + builder_kp, custodian_kp, sovereign_kp = ( + generate_ed25519(), + generate_ed25519(), + generate_ed25519(), + ) + serving = sha256(b"builder-signed serving stack, measured") + weights_hash = sha256(b"the frontier weights") + + # ---- Step 0: the sovereign self-custody manifest ------------------------ + rule("Step 0 - Sovereign manifest (quorum revocation, self-custody, hardened)") + doc = { + "manifest_version": "0.1", + "weights_hash": weights_hash, + "builder": {"identity": "frontier-lab", "signing_key": "ed25519:builder"}, + "release_terms": { + "license": "sovereign-deployment-agreement", + "permitted_derivatives": "none", + "permitted_environments": ["sovereign-attested-enclave"], + }, + "release_policy": { + "required_assurance_tier": "hardware-attested", + # Hostile-owner posture: physical hardening and the memory-fingerprint + # challenge are mandatory here (SPEC 3.5, 3.6). + "physical_hardening": "tamper-evident-enclosure+access-control+chain-of-custody", + "memory_fingerprint_challenge": "required-for-hostile-owner-posture", + "trusted_time_source": "secure-tsc", + "required_hw_platform": ["amd-sev-snp", "nvidia-cc-gpu"], + "required_gpu_measurement": {"rim_pin": "nvidia-rim:demo-golden"}, + "required_serving_image": { + "signer": "ed25519:builder", + "release_rule": "prefer-current", + "accepted_measurements": [{"measurement": serving, "status": "current"}], + }, + "attestation_revocation_check": "live-per-release, max-cache-age: short-window", + # Sovereign profile: revocation is quorum-only, no unilateral kill. + "revocation_authority": "quorum", + "sovereign_profile": { + "enabled": True, + "revocation_authority": "quorum", + "sovereign_signer": "sovereign-security-team", + }, + }, + "custody": { + "custodian": "sovereign-national-ai", + "custodian_type": "customer-self-custody", + "kbs_image": {"measurement": sha256(b"builder-signed KBS image"), "signer": "ed25519:builder"}, + "enclave_id": "did:sovereign:kbs-enclave-01", + "attestation_cadence": "1h", + }, + } + manifest = WeightCustodyManifest.model_validate(doc) + manifest = manifest.with_signatures([ + Ed25519Signer(builder_kp).sign(manifest.unsigned_dict(), role="builder", signer="frontier-lab"), + Ed25519Signer(custodian_kp).sign(manifest.unsigned_dict(), role="custodian", signer="sovereign-national-ai"), + Ed25519Signer(sovereign_kp).sign(manifest.unsigned_dict(), role="sovereign", signer="sovereign-security-team"), + ]) + print("sovereign_profile.enabled :", manifest.release_policy.sovereign_profile.enabled) + print("custodian_type :", manifest.custody.custodian_type.value) + print("signed by : builder + custodian + sovereign (quorum)") + + # ---- Step 1: verify the manifest (the sovereign quorum must be present) -- + rule("Step 1 - Verify the manifest (sovereign quorum required)") + ctx = VerificationContext() + for kp in (builder_kp, custodian_kp, sovereign_kp): + ctx.add_key(kp.public_bytes) + result = verify_manifest(manifest, ctx) + print("manifest valid :", result.ok) + if not result.ok: + print("errors:", result.errors, "missing:", result.missing_roles) + return 1 + + # ---- Step 2: split the key so no single party holds it ------------------ + rule("Step 2 - Split the decryption key 2-of-3 (builder, sovereign, custodian)") + key = hashlib.sha256(b"the weights decryption key").digest() + shares = split_secret(key, threshold=2, shares=3) + builder_share, sovereign_share, custodian_share = shares + print("key split into : 3 shares, any 2 reconstruct") + print("no single party holds the key. Each holds one share.") + + # ---- Step 3: the attested self-custody KBS authorizes release ----------- + rule("Step 3 - Attested KBS gate (parties verify the enclave before sharing)") + # The self-custody KBS holds NO weight key (empty entry): the gate proves the + # enclave is the builder-measured release coordinator; the key comes from the + # quorum, not from the KBS. + kbs = KeyBrokerService({weights_hash: b""}) + challenge = kbs.issue_challenge() + evidence = SoftwareProvider().produce( + challenge, + serving_image_measurement=serving, + gpu_measurement="nvidia-rim:demo-golden", + include_memory_fingerprint=True, # mandatory in the hostile-owner posture + ) + decision = kbs.verify_and_release(manifest, evidence) + print("attestation gate passed :", decision.released) + if not decision.released: + for c in decision.failures(): + print(" failed:", c.name, "-", c.detail) + return 1 + print("each party checks this attestation before contributing its share.") + + # ---- Step 4: a quorum reconstructs the key inside the enclave ----------- + rule("Step 4 - Quorum contributes shares; enclave reconstructs the key") + reconstructed = combine_shares([builder_share, sovereign_share]) + print("builder + sovereign -> key matches :", reconstructed == key) + # Any two shares work (fault tolerance): losing one party does not lose the key. + print("builder + custodian -> key matches :", combine_shares([builder_share, custodian_share]) == key) + + # ---- Step 5: one party (and one forged quote) is not enough ------------- + rule("Step 5 - The decision-15 property: a single forged quote cannot release") + sovereign_alone = combine_shares([sovereign_share]) + print("sovereign's single share -> key :", sovereign_alone == key, " (cannot reconstruct)") + print("even if the sovereign forges its own KBS quote (open 8.8), it still holds") + print("only one share, so one forged quote does not assemble the key. It needs a") + print("quorum that includes an independent party. That is why threshold is a") + print("PREREQUISITE for sovereign self-custody, not an optional hardening.") + + # ---- Step 6: what carries the guarantee here ---------------------------- + rule("What carries the guarantee (and what does not)") + print("carries it : threshold (no single party assembles the key), the sovereign") + print(" revocation quorum, mandatory physical hardening, transparency log.") + print("does NOT : wipe-on-lapse is not an independent floor against a hardware") + print(" owner who can forge attestation (SPEC 3.5). The bound is the") + print(" quorum and the hardening, not enclave-resident timing.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())