HushSpec/Docs
HUSHED · MMXXVI
Documentation v1.0
v1.0.0guidestable

Policy Signing#

The full normative specification is at spec/hushspec-signing.md. The 0.2 envelope and keyring schemas are schemas/hushspec-signature.v1.schema.json and schemas/hushspec-keyring.v1.schema.json.

A policy signature is a detached JSON envelope (policy.yaml.sig) carrying an Ed25519 signature over the canonical content hash of the resolved policy, not over the file bytes. Reformatting the YAML keeps the signature valid; changing a base policy reached through extends invalidates it.

The envelope's claims (format_version, algorithm, key_id, signed_at, optional expires_at, policy_version, policy_name, signer, and content_hash) are all inside the signed input, which is the RFC 8785 canonical form of the envelope without signature.

Keys are standard PEM (PKCS#8 private, SubjectPublicKeyInfo public); key_id is the SHA-256 of the SPKI DER, which verifiers recompute. Trust is a keyring file listing public keys with optional retirement (not_after) and revocation.

Verification is an ordered list of ten checks, each with a fixed reason code, ending in valid or the first failure. Enforcement points configured to require signatures verify on load and refuse to evaluate against an unverified policy.

Vectors: fixtures/signing/vectors.yaml lists eighteen cases signed with a published, test-only key.

Run the evidence lab#

Download verify.py and the quickstart policy into one directory. With Python 3.10+ and the v1 CLI installed:

Terminal
python3 verify.py --policy policy.yaml

The lab generates fresh disposable keys inside a private temporary directory. It signs and verifies a policy, checks a denied receipt, verifies a signed log, and creates and verifies a bundle. It then asserts these refusals:

AlterationExpected result
Change the policy namecontent_hash_mismatch
Verify using an unrelated public keyunknown_key_id
Advance the synthetic verifier clock beyond expiryexpired
Flip one signature byte, preserving base64url shapesignature_mismatch
Break the second log entry's predecessorLine 2 diagnostic naming prev_hash
Change a bundle's signed payloaddsse_signature_mismatch
Remove a log's final receiptRemaining prefix verifies; completeness is not established

The fixed/advanced clocks in examples test expiry. Production verification uses a trustworthy current clock, not a conveniently chosen historical time.

Load-time verification#

Configure a guard/resolver with signature requirements and an independently provisioned keyring. Verify every inheritance hop under that contract, not just the leaf after deployment. Built-in hops and matching explicit digest pins have their specified trust treatment; see the SDK resolution contract.

A missing or invalid required signature cannot become an unrestricted policy. Ordinary guards can represent verification refusal as a deny-all state; an ordinary failed reload retains the last good policy. The experimental invocation coordinator instead refuses subsequent calls after a rejected installation. Do not transfer one lifecycle's recovery rules to the other.

Rotation, expiry and rollback#

  1. Distribute a new trusted public key through an authenticated operator channel.
  2. Sign an approved policy with the new private key; verify with the receiving system's actual trust configuration before activating it.
  3. Retain old public keys for historical verification, with explicit retirement or revocation according to your incident and retention policy.
  4. Persist the last accepted integer policy_version per policy name and pass that lower bound to verification. A valid old signature is not rollback protection unless the relying party retains and checks that state.

expires_at, key retirement and revocation are different checks. An expired envelope is not repaired by allowing more clock skew; a revoked key is not restored by a new signature using that same key. The full reason-code table is in the registry reference.

Private-key handling#

Production private keys are operator inputs. Keep them out of policies, repositories, logs, screenshots and support tickets; restrict filesystem permissions and use your secret-management process. Do not adopt the published fixture key or a key generated by the evidence lab as a trust root.

For an operator-managed PEM key pair, these commands require a private working directory and restrictive umask:

Terminal
umask 077
openssl genpkey -algorithm ed25519 -out signing.key.pem
openssl pkey -in signing.key.pem -pubout -out signing.pub.pem

Loading documentation index…

↑↓ navigate↵ openesc close