diff --git a/.claude/commands/check-ci.md b/.claude/commands/check-ci.md index 8ae436d0eb..f6eea788d7 100644 --- a/.claude/commands/check-ci.md +++ b/.claude/commands/check-ci.md @@ -10,4 +10,7 @@ Run all CI checks locally, in order. Stop and report if any step fails. 8. `cargo run --manifest-path crates/trusted-server-cli/Cargo.toml --target "$(rustc -vV | sed -n 's/host: //p')" --bin ts -- dev lint domains --changed-vs origin/main` (use the PR's base branch in place of `main` when it targets another branch) 9. `node --test scripts/check-ci-node-pin.test.mjs && node scripts/check-ci-node-pin.mjs` +10. Publisher attestation fixtures (run `cd docs && npm ci` first): `node scripts/publisher-attestation/generate.mjs --check && node --test scripts/publisher-attestation/*.test.mjs` +11. `docs/node_modules/.bin/eslint --config scripts/publisher-attestation/eslint.config.mjs "scripts/publisher-attestation/*.mjs"` and `docs/node_modules/.bin/prettier --config docs/.prettierrc --check "scripts/publisher-attestation/*.mjs"` + Report a summary of all results when done. diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 299768ffda..c5d3185451 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -202,6 +202,14 @@ jobs: docs/node_modules/.bin/prettier --config docs/.prettierrc --check "*.md" ".claude/**/*.md" ".github/**/*.md" "crates/**/*.md" "scripts/**/*.md" "tinybird/**/*.md" + - name: Check publisher attestation fixtures + working-directory: . + run: | + node scripts/publisher-attestation/generate.mjs --check + node --test scripts/publisher-attestation/*.test.mjs + docs/node_modules/.bin/eslint --config scripts/publisher-attestation/eslint.config.mjs "scripts/publisher-attestation/*.mjs" + docs/node_modules/.bin/prettier --config docs/.prettierrc --check "scripts/publisher-attestation/*.mjs" + - name: Build with VitePress (fails on dead links) run: npm run build diff --git a/AGENTS.md b/AGENTS.md index bf31f54858..9dc7ef463b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -375,9 +375,10 @@ Every PR must pass: 11. Documentation proposal scripts: `shellcheck scripts/docs-proposal/*.sh && node --test scripts/docs-proposal/hunks.test.mjs && scripts/docs-proposal/test.sh` 12. URL-host linter on lines the PR adds (`lint-domains` job, PRs only); run it locally with `cargo run --manifest-path crates/trusted-server-cli/Cargo.toml --target --bin ts -- dev lint domains --changed-vs origin/main`, using the PR's base branch in place of `main` when it targets another branch 13. CI Node pin guard: `node --test scripts/check-ci-node-pin.test.mjs && node scripts/check-ci-node-pin.mjs` with the `.tool-versions` Node (every CI job that compiles Rust sets up the `.tool-versions` Node before its first cargo step) -14. Fastly absolute-deadline network fixtures under Viceroy (the harness builds the adapter test binary itself): `node crates/trusted-server-adapter-fastly/tests/deadline-origin.mjs "$(command -v viceroy)"` -15. Worker auction-deadline runtime fixtures (`worker-auction-deadlines.yml`): build and lint the runtime fixture, then run it under the local runtime. For Cloudflare, `cargo build --locked -p trusted-server-adapter-cloudflare --example deadline-runtime --target wasm32-unknown-unknown --features cloudflare` and `node scripts/test-worker-auction-deadlines.mjs cloudflare --runtime "$(command -v wrangler)"` (requires wrangler and the `Cargo.lock` wasm-bindgen CLI). For Spin, `cargo build --locked -p trusted-server-adapter-spin --example deadline-runtime --target wasm32-wasip1 --features spin` and `node scripts/test-worker-auction-deadlines.mjs spin --runtime "$(command -v spin)"`. CI lints each fixture with the same `cargo clippy` arguments plus `-- -D warnings` -16. Release producer checks: `node --test scripts/release/phase1.test.mjs && shellcheck scripts/release/phase1-build.sh` +14. Publisher attestation fixtures (requires `cd docs && npm ci` for Ajv, ESLint and Prettier): `node scripts/publisher-attestation/generate.mjs --check && node --test scripts/publisher-attestation/*.test.mjs`; lint with `docs/node_modules/.bin/eslint --config scripts/publisher-attestation/eslint.config.mjs "scripts/publisher-attestation/*.mjs"` and format with `docs/node_modules/.bin/prettier --config docs/.prettierrc --check "scripts/publisher-attestation/*.mjs"` +15. Fastly absolute-deadline network fixtures under Viceroy (the harness builds the adapter test binary itself): `node crates/trusted-server-adapter-fastly/tests/deadline-origin.mjs "$(command -v viceroy)"` +16. Worker auction-deadline runtime fixtures (`worker-auction-deadlines.yml`): build and lint the runtime fixture, then run it under the local runtime. For Cloudflare, `cargo build --locked -p trusted-server-adapter-cloudflare --example deadline-runtime --target wasm32-unknown-unknown --features cloudflare` and `node scripts/test-worker-auction-deadlines.mjs cloudflare --runtime "$(command -v wrangler)"` (requires wrangler and the `Cargo.lock` wasm-bindgen CLI). For Spin, `cargo build --locked -p trusted-server-adapter-spin --example deadline-runtime --target wasm32-wasip1 --features spin` and `node scripts/test-worker-auction-deadlines.mjs spin --runtime "$(command -v spin)"`. CI lints each fixture with the same `cargo clippy` arguments plus `-- -D warnings` +17. Release producer checks: `node --test scripts/release/phase1.test.mjs && shellcheck scripts/release/phase1-build.sh` CI also runs these HTML EOF checks. Run them locally only when changing EOF finalization, publisher or HTML-processor code: diff --git a/docs/package-lock.json b/docs/package-lock.json index 89cebb3727..2a25cacf30 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -11,6 +11,7 @@ "devDependencies": { "@eslint/js": "^10", "@types/node": "^24.10", + "ajv": "6.14.0", "eslint": "^10", "mermaid": "^11.12.3", "prettier": "^3.4.2", diff --git a/docs/package.json b/docs/package.json index c70fc831d0..31279b5fb9 100644 --- a/docs/package.json +++ b/docs/package.json @@ -19,6 +19,7 @@ "devDependencies": { "@eslint/js": "^10", "@types/node": "^24.10", + "ajv": "6.14.0", "eslint": "^10", "mermaid": "^11.12.3", "prettier": "^3.4.2", diff --git a/docs/superpowers/specs/2026-01-15-attestation-design.md b/docs/superpowers/specs/2026-01-15-attestation-design.md index d836c4e671..7bdaba1814 100644 --- a/docs/superpowers/specs/2026-01-15-attestation-design.md +++ b/docs/superpowers/specs/2026-01-15-attestation-design.md @@ -4,6 +4,8 @@ > **Author:** Architecture Team > **Date:** January 2026 +> **Superseded for publisher/runtime attestation:** [Publisher attestation](2026-11-04-publisher-attestation-design.md) is the current contract. The proposal metadata above is historical; this document's full-configuration and embedded-checksum model is not a current requirement. Binary release evidence follows its separate release specification. + ## Problem Statement The Trusted Server is a WASM-based edge computing system that handles ad serving with pluggable **integrations** (Prebid, Lockr, Didomi, etc.). Publishers configure these integrations via `trusted-server.toml`. The challenge: diff --git a/docs/superpowers/specs/2026-11-04-publisher-attestation-design.md b/docs/superpowers/specs/2026-11-04-publisher-attestation-design.md new file mode 100644 index 0000000000..5e28e90edf --- /dev/null +++ b/docs/superpowers/specs/2026-11-04-publisher-attestation-design.md @@ -0,0 +1,728 @@ +# Publisher attestation + +**Date:** 2026-11-04. **Status:** Proposed contract for maintainer and user review; no implementation or provider qualification is claimed. + +**Issues:** [#161](https://github.com/IABTechLab/trusted-server/issues/161), [#1427](https://github.com/IABTechLab/trusted-server/issues/1427). **Predecessor:** Layer 1 (authenticated release-artifact evidence), tracked as binary attestation [#1235](https://github.com/IABTechLab/trusted-server/issues/1235), [#1435](https://github.com/IABTechLab/trusted-server/pull/1435), [#1449](https://github.com/IABTechLab/trusted-server/pull/1449), and subsequent release-evidence work. + +**Repository baseline:** `origin/main` at `51730bdaf841e4d7bb96c2258e5e98169bdff9a7`. Release-branch observations are dependencies, not functionality assumed to exist on this baseline. This specification introduces no code, credentials, enrollment, remote writes, or release authorization. + +## Read this first + +Publisher attestation answers: **who is responsible for this website's deployment, what software was independently checked, and how recently?** The publisher keeps its key. Its server answers a fresh random challenge with a signed identity statement. A separately chosen verifier checks host/key authorization and, when a qualified Fastly method is available, compares the deployed executable with an authenticated official release. It then signs a short-lived assessment. Consumers choose which verifiers and policies they trust. + +The signed response alone is a declaration. A registry assessment can add independent evidence, but a package fingerprint match cannot be promoted into proof of installed official code. Fastly supplies production declarations; Axum remains an isolated development adapter. Independently checked executable deployment starts with Fastly and remains qualification-gated. + +V1 consumers normally rely on assessment expiry, at most 15 minutes from the oldest observation. Optional live status can detect a reported withdrawal sooner but adds a registry availability and privacy dependency. The signed identity projection describes five public identity values; registries do not inspect a publisher's whole business configuration for that check. Auction/bid attestation comes later. + +| Read next | Purpose | +| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| This document | Publisher endpoint, common wire rules, runtime integration and acceptance boundaries | +| [Registry protocol and operations](2026-11-04-publisher-attestation-registry-design.md) | Enrollment, assessments, consumer policy, optional status, governance and registry-side operation | +| [Fastly qualification](2026-11-04-publisher-attestation-fastly-qualification-design.md) | Evidence needed before anyone may assert an installed official executable | + +```mermaid +flowchart LR + L[Authenticated official release bytes] --> V[Independent verifier] + P[Publisher signed challenge response] --> V + F[Qualified Fastly deployment evidence] --> V + V --> A[Short-lived signed assessment] + A --> C[Consumer applies its own trust policy] + P --> C +``` + +## 1. Decision and assurance boundary + +Publisher attestation connects a publisher host, its operator, a specific deployment, and an authorized public key to signed evidence and independent, time-bounded verifier assessments. It answers who made the declaration, what an independent party checked, how, and when. It does not establish continuous measured execution. + +The selected architecture is publisher-produced evidence plus portable assessments from zero, one, or multiple independent verifiers. Publishers retain their keys. Consumers independently configure trusted verifier keys and acceptance policies. A registry is an optional directory and presentation service; it is not a mandatory administrator or key custodian. + +The complete work includes independent deployment appraisal. An evidence-only milestone is useful, but MUST be labelled self-declared and MUST NOT be presented as an independently verified official deployment. + +The intended strong result is: + +> An identified verifier checked authorization for a publisher host and key, observed a specified production service/version, authenticated its selected executable against an eligible official release, and issued an assessment with an explicit observation time and expiry. + +That result is available only after the selected provider method passes qualification. A provider package fingerprint match alone does not currently establish that result. The companion [Fastly binding qualification specification](2026-11-04-publisher-attestation-fastly-qualification-design.md) owns that gate. + +### 1.1 Alternatives considered + +| Approach | Benefit | Cost or limitation | Decision | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| Publisher evidence plus independent, portable assessments | Allows custom builds, multiple verifiers, and consumer choice; separates declarations from observation | Requires enrollment, provider qualification, and explicit freshness policy | Selected | +| Operator signatures alone | Small endpoint; useful identity and challenge evidence | Cannot independently establish installed official bytes or honest configuration | Permitted delivery milestone, not the complete assurance claim | +| One administrator generates publisher keys and approves every deployment | One operational authority and policy | Concentrates impersonation capability and governance; unnecessary for interoperability | Rejected | +| Hardware/provider-enforced measured execution and signing | Could strengthen code-to-key binding | No qualified capability established here | Future separately specified assurance method | + +### 1.2 Scope + +In scope: host and operator authorization; deployment identity; publisher evidence; runtime key custody and rotation; independently verified release inputs; Fastly deployment observations; custom-build treatment; configuration claim boundaries; verifier assessment and withdrawal contracts; reference verification; safe networking; privacy; compatibility; acceptance tests; and operational responsibilities. + +Out of scope: auction/bid signing, message-body integrity, auction execution proofs, bid replay caches, auction key delegation, transaction schemas, inventory truth, consent-input truth, legal-entity certification, broad configuration compliance certification, a hosted commercial registry, a registry dashboard, or hardware attestation. This work reserves stable deployment/key/assessment identifiers for future consumers; it does not design the next roadmap layer. + +Fastly is the first production target for declarations and independently checked executable deployment. Axum supplies isolated development fixtures only; this design does not promote it to a production or staging deployment target. Cloudflare, Spin, and reusable Fastly require their own runtime qualification for the applicable profile. No new official adapter distribution is implied. + +### 1.3 Normative language and ownership + +MUST, MUST NOT, SHOULD, and MAY express requirements in the sense of BCP 14. Stable PA identifiers identify requirement groups. These wire/schema changes are revisions of an unshipped proposal and require coordinated implementation; they do not claim compatibility with a deployed earlier draft. The wire version is `1`; schema names and message types below are proposed private protocol identifiers, not claims of IANA registration. + +| Owner | Responsibility | +| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| Release producer and Layer 1 verifier | Authenticate executable bytes, source/workflow identity, official publication, and applicable release eligibility | +| Publisher/operator | Authorize enrollment, control DNS and deployment, provision runtime keys, maintain configuration and permissions | +| Attester in Trusted Server | Produce typed, fresh evidence from one actual request-used snapshot | +| Independent verifier | Authenticate enrollment, collect provider observations, apply a named policy, sign assessments and status | +| Consumer/relying party | Supply independent trust roots, check bindings/freshness/status, and decide acceptance | +| Registry/directory | Distribute signed objects and clearly label their assurance; optional | + +### 1.4 RFC 9334 roles + +| RATS role | Concrete participant and scope | +| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Attester | The runtime serving publisher evidence for the requested host | +| Verifier | The independent component appraising that evidence and qualified provider observations | +| Relying Party | The consumer deciding whether the signed assessment meets its policy | +| Reference Value Provider | The release authority publishing authenticated official executable identities; #1435/#1449 contribute that Layer 1 work, rather than being participants themselves | +| Endorser | The authority supplying authenticated statements about artifact origin or approved reference values; release provenance does not endorse a publisher key or prove remote execution | +| Verifier Owner / Relying Party Owner | The parties configuring appraisal policy and consumer acceptance respectively | + +These roles may be performed by different organizations or combined. RFC 9334 does not require one administrator or prescribe this private wire format. + +### 1.5 Platform feasibility and supported claims + +| Deployment | Target v1 evidence/profile | Required qualification or limitation | +| ---------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| Fastly production/staging | Publisher declaration; official-deployment profile only for production | Endpoint/runtime verification; strong executable binding still requires the companion gate | +| Fastly staging isolation | Separate deployment identity, keys and physical store links | A separate service or a distinct version with isolated links can supply isolation; shared production links cannot | +| Several hosts on one service | One attested host per deployment configuration | Other names return 421 on the reserved paths until configuration-by-host support exists | +| Axum loopback development | Isolated declaration fixtures | Separate test trust; cannot use development authorization in production | +| Reusable Fastly | Same intended profiles after lifecycle qualification | Actual retained snapshot, key expiry and refresh/recycling must be demonstrated | +| Cloudflare / Spin | Future separately qualified endpoint support | No implicit provider measurement or production-assessment support from this spec | + +This is a target support table, not a statement that endpoints are implemented today. + +## 2. Terminology and threat model + +### PA-IDENTITY: keep the identities distinct + +**Publisher host** is the public DNS name being checked, such as `news.example.com`. **Backend origin** is the upstream content source and is not publisher identity. **Publisher ID** is the canonical owner DNS domain, such as `owner.example.com`, independently authorized by the verifier for an authenticated publisher account. A **site** is an exact enrolled host belonging to that publisher; one publisher may own many sites on unrelated domains. The signed identifier means that this verifier authorized that site for that publisher, not that it certified legal identity or a sellers.json account. Backend routing, cookie scope and the site domain do not establish publisher authority. Registry PA-PUBLISHER defines the proof and membership record. **Operator ID** is a stable HTTPS identifier for a separately claimed operator, or null when the publisher acts for itself. A non-null identifier requires independent technical authorization; it is not legal-entity certification or trusted merely because it is a URL. Verifiers MUST NOT fetch arbitrary operator-ID URLs. An operator identifier's optional path is descriptive scope only: its host's DNS controller can authorize every path beneath that host. Different paths do not prove independently controlled operators. + +**Deployment ID** is a publisher-assigned UUID stable for one intended logical deployment. Separate production/staging deployments MUST use different deployment IDs and keys. On Fastly, the runtime opens stores through logical IDs; isolation comes from the physical resources linked to the selected service version (`RuntimeStoreConfig::logical`). Distinct production and staging versions of the same service can link different physical configuration and secret stores; a separate service is another isolation option. Activating the same version on both networks retains that version's links and does not isolate its settings or key. Production and staging MUST NOT share attestation identity configuration or private keys, and any supported staging path must demonstrate its version-specific store isolation. PA-BINDING defines the fail-closed staging/environment consistency check; sharing a service ID alone is not a reason to reject an otherwise qualified isolated deployment. Cloning a service does not authorize reuse of an enrollment. **Binding ID** is the digest of the exact version/configuration/key descriptor defined below. A new service version, attestation key, or covered configuration value changes the binding ID. Assessment renewal does not change it. + +Consumers MUST compare the full binding, not assume identifier uniqueness makes assertions true. Enrollment authorizes the tuple `(host, publisher_id, operator_id, deployment_id, provider, service_id, environment, attestation_key_thumbprint)` for each authorization generation; a routine key rotation is a new generation of the same enrollment (registry PA-ENROLLMENT), while versions and binding IDs are separately checked at assessment time. + +### 2.1 Attacker capabilities and residual trust + +Assume an attacker can copy endpoints and assessments, replay or relay challenges, manipulate unsigned discovery, control a custom binary and its reported metadata, change publisher configuration or service versions, upload unusual packages, obtain a publisher runtime key through deployment control, submit malicious enrollment/package URLs, and operate an untrusted verifier. Assume network outages and partial provider observations. + +| Threat | Required control | Remaining limitation | +| --------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| Forged publisher or verifier signature | Pinned keys, strict typed signatures, key-purpose separation | A stolen trusted key can sign | +| Evidence copied to another host | Expected host, independent authorization, fresh challenge, authenticated HTTPS | A live same-host relay is not measured execution proof | +| A clean attestation path hides different traffic handling | Explicit endpoint-only claim; provider service/version observations | No proof that all site traffic follows this path | +| Custom code reports an official commit | Independently authenticated executable binding | Self-reported headers/build facts are not measurements | +| Alternate code uses an endorsed key | Monitoring, short-lived assessments, withdrawal, isolated key scope | Operator-controlled software keys are not exclusive to approved code | +| Ambiguous package produces matching fingerprint | Provider binding qualification, structural evidence, stronger selected-module identity | Local package inspection alone cannot constrain a malicious remote upload | +| Configuration changes without activation | Actual snapshot binding and coverage declaration | Uncovered/dynamic settings and propagation remain outside the claim | +| SSRF or credential leakage | Fixed provider authority, constrained enrollment targets, bounded fetches, redaction | Trust in configured resolver/provider remains | +| Compromised verifier or misleading directory | Consumer-pinned trust roots/policies; signed provenance of assessments | An accepted verifier can lie | + +Consumers still rely on Fastly reporting its state truthfully, on verifier collection and signing being honest, on the release authority, and on the declared clock/status assumptions. No result promises absence of vulnerabilities, continuous execution, honest publisher inputs, or secret non-extractability. + +Monitoring reduces exposure; it does not close the code-to-key gap. Binding version 42 does not invalidate its signed assessment when version 43 activates. A consumer rejects the mismatch only after trustworthy newer evidence/status reaches it, or the old assessment expires. No finite probe set proves universal routing or parser behavior. + +### What this does not prove + +- A nonce/signature proves a fresh answer from a key holder; it cannot prevent a live relay or prove exclusive use of that key by official code. +- An installed executable observation is time-bounded and trusts the provider/verifier. It does not establish uninterrupted execution or simultaneous state at every POP. +- Official release bytes do not certify configuration, business rules, consent inputs, page content, inventory truth, or absence of vulnerabilities. +- V1 does not verify that Trusted Server is the outermost layer for all site traffic. An endpoint probe and domain mapping are insufficient to establish that property. +- Expiry-only acceptance cannot promise immediate withdrawal. Known withdrawal is honored, but an uninformed consumer can accept until the existing assessment expires. +- Protocol identity/DNS control does not certify a legal entity, contractual relationship, or legal compliance. Governance/liability choices belong to registry agreements and task-force approval. + +## 3. Protocol artifacts and common encoding + +### PA-WIRE: one strict signed envelope + +Evidence, assessments, and status use compact JWS. The response body is the compact ASCII serialization, not a JSON object with an unsigned readable evidence copy. The protected header contains exactly `alg`, `kid`, and `typ`: + +| Field | Version 1 rule | +| ----- | ---------------------------------------------------------------------------------------------------------------- | +| `alg` | Exactly `Ed25519` as registered by RFC 9864; no polymorphic algorithm identifier | +| `kid` | Base64url, unpadded SHA-256 JWK thumbprint of the signing public key | +| `typ` | Exactly `ts-publisher-evidence+jws`, `ts-publisher-assessment+jws`, or `ts-publisher-status+jws`, as appropriate | + +1. All header fields are protected. + +2. Reject unprotected headers, detached payloads, `none`, HMAC, legacy `EdDSA`, alternate curves, embedded trust keys, key URLs, certificate URLs, `crit`, compression, and unencoded payload extensions. + +3. Signature length is exactly 64 bytes. + +4. Public keys are JWK objects containing exactly `kty: "OKP"`, `crv: "Ed25519"`, and a canonical unpadded base64url `x` encoding 32 bytes. + +5. Private JWK material MUST NOT appear on a public surface. + +6. The transport uses JWS rules; implementations MUST NOT introduce the historical prototype's separate context-prefix format (section 14) under the same message type. + +7. Ed25519 verification is strict, so independent implementations agree on edge cases. + +8. RFC 8032 sections 5.1.3 and 5.1.7 already require rejecting a signature whose scalar `S` is not canonical (`S >= L`) and a non-canonical encoding of the point `R` or the public key `A`; many libraries skip some of these checks, so implementations enforce them explicitly. + +9. This profile additionally rejects a small-order point in either `R` or the public key `A`. + +10. Apply the same public-key checks when accepting a JWK at enrollment, in discovery and in key metadata. + +11. Use the cofactorless verification equation. + +12. Library defaults are not assumed to be strict: the existing request-signing verifier calls `VerifyingKey::verify`, so attestation code must use a strict entry point such as `verify_strict` instead of reusing that call unchanged. + +13. Time comparisons use exclusive expiry (`now < expires_at`) and inclusive not-before (`now >= not_before`). Clock skew applies only to future issuance, never to key validity or expiry. Canonical public-key point encoding must also be checked explicitly by decoding and re-encoding the point and requiring the original bytes; a library's strict signature entry point is not assumed to perform every JWK admission check. With ed25519-dalek, `to_bytes()` returns the supplied encoding; compare against `to_edwards().compress().to_bytes()` instead. + +14. Sign and verify the original JWS segments; never reserialize a decoded payload to check its signature. + +15. Parse UTF-8 JSON with duplicate-member rejection, including nested objects, bounded depth 16, and no trailing data. + +16. Unknown members anywhere in a version 1 payload are rejected. + +17. The closed schemas below use an explicit `extensions` object, initially required empty; a nonempty extension requires a later supported profile/version. + +18. Arrays are limited to 16 entries unless a smaller limit is stated. + +19. Claim strings have a 256-byte UTF-8 limit unless specified otherwise. + +20. The claim-string bound does not apply to compact JWS serialization or its encoded segments. + +21. An assessment-list item is an explicitly exempt compact-JWS string of at most 32,768 ASCII bytes; the list remains bounded to 16 items and 524,288 total response bytes. + +22. The 16-item and 524,288-byte list bounds also apply to unsigned assessment-list and key-history responses; single discovery and issuer metadata responses use the PA-NETWORK caps. + +23. Their decoded claim strings retain the ordinary bound. + +24. Structural JSON Schema `maxLength` counts Unicode code points, so receivers must independently enforce the UTF-8 byte and raw-response bounds before accepting data. + +25. Integers are nonnegative JSON integers no greater than `9007199254740991`. + +26. Time fields are UTC Unix seconds, not milliseconds. + +27. Use SHA-256, lowercase 64-character hex, for document/content identifiers, except `kid` as above and Fastly's explicitly labelled SHA-512 fingerprint. `digest` means `{ "algorithm": "sha256", "value": }`. + +28. Canonical descriptor/projection hashes use JCS over the defined public JSON value. + +29. JWS signatures themselves do not require JCS. + +30. Object digests of signed objects hash the exact compact JWS bytes; do not hash a readable rendering. + +31. The JOSE and canonicalization rules are based on RFC 7515, RFC 8037 as updated by RFC 9864, RFC 7638, RFC 8725, and RFC 8785. + +32. RFC 9864 replaces the deprecated polymorphic `EdDSA` identifier with `Ed25519` for this new protocol; a library that cannot enforce that protected identifier is not conforming and must not silently substitute it. + +33. Some deployed JOSE libraries may support only the older `EdDSA` name; integrators must qualify or update those libraries before enabling v1. + +34. Renaming an incoming protected header breaks its signature and is forbidden. + +35. These standards define encoding and validation primitives; the publisher claim semantics here are project requirements. + +36. The accompanying [wire schema](schemas/publisher-attestation-v1.schema.json) uses JSON Schema draft-07 with local references and no remote schema fetch. + +37. The wire schema omits a `$schema` URI, so validators must select draft-07 explicitly. + +38. Its definitions cover protected headers, signed payloads, discovery/list envelopes, enrollment challenges, and stored public metadata. + +39. The [public verification vectors](fixtures/publisher-attestation-v1-vectors.json) retain exact signed bytes, public keys, expected context, and positive/negative outcomes; public test-only seeds and a deterministic generator are committed under `scripts/publisher-attestation/`; these seeds MUST NOT be used outside tests. + +40. They are protocol fixtures, not provider observations or official-deployment qualification. + +41. Schema validation is necessary but insufficient. + +42. Duplicate JSON members, canonical base64url, actual UTF-8 byte lengths, canonical host handling, JWK thumbprints, JCS/digest equality, signature/type/key agreement, time intervals, operator authorization, policy eligibility, status, and cross-object equality require the separate semantic checks specified here. + +43. Consumers reject a schema-valid object that fails any applicable semantic check. + +44. A valid signature on an unknown/legacy algorithm identifier or wrong message type is still rejected. + +45. Published vectors must be checked through two independent cryptographic implementations and include changed-signature, changed-context, stale, and mismatched-binding cases before wire freeze. + +### PA-BINDING: exact deployment descriptor + +The `binding` object is closed and has these required fields: + +| Field | Type and meaning | +| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `schema` | Literal `ts-publisher-binding-v1` | +| `host` | Canonical authorized publisher host | +| `publisher_id` | Canonical owner DNS domain authorized under registry PA-PUBLISHER | +| `operator_id` | Canonical HTTPS identifier (PA-IDENTIFIER), or null for publisher self-operation | +| `deployment_id` | Canonical lowercase UUID | +| `environment` | `production`, `staging`, or `development` | +| `platform` | Object: `provider` (`fastly` or `axum`), `service_id` (string; null only for Axum), `service_version` (positive integer; null only for Axum), `staging` (boolean or null) | +| `attestation_key` | Object: `kid`, public `jwk`, `not_before`, `not_after` | +| `configuration` | Object: `schema` literal `ts-publisher-identity-v1`, `digest`, and `projection` defined in section 7 | +| `extensions` | Empty object | + +`binding_id` is the lowercase 64-character hexadecimal string SHA-256 of JCS(`binding`), not a nested digest object. `attestation_key.kid` MUST equal its JWK thumbprint and the evidence signing header `kid`. Its validity interval MUST be nonempty and contain the entire evidence lifetime (`not_before <= issued_at` and `expires_at <= not_after`); consumers reject a violation at stage 4 as `key_unauthorized`. Fastly service identity is adapter-observed runtime identity, still a declaration to an external verifier until corroborated. Axum declaration fixtures require `environment: development` and null service ID, version, and staging fields. The declared environment is an operator setting, not a provider measurement. Every Fastly binding carries non-null service ID and version; when the adapter cannot observe them the endpoint returns 503 and signs nothing. Fastly official-deployment bindings additionally require that identity to be independently corroborated. Axum can never satisfy the Fastly official-deployment profile. Unknown staging MUST be null, never silently false. Every Fastly binding MUST satisfy this invariant: `staging: true` permits only `staging` or `development`; `staging: false` permits only `production` or `development`. Unknown staging remains null and cannot support provider fingerprint or official assurance. Verifiers and consumers reject a contradictory binding as `environment_mismatch` before ordinary schema evaluation; `development` cannot pass production policy. When the adapter observes Fastly staging (`FASTLY_IS_STAGING=1`) but the configured environment is `production`, or observes production while the configured environment is `staging`, the endpoint returns 503 and signs nothing; it never emits a binding whose staging flag contradicts its environment. + +The descriptor MUST NOT include verifier assessment IDs, verifier list order, latest store state fetched after the request, private configuration hashes, or a claimed self-measurement of the binary. This avoids circular identity and changing the binding whenever an assessment renews. + +### PA-EVIDENCE: publisher payload + +| Required field | Type and rule | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `schema` | `ts-publisher-evidence-v1` | +| `binding` | Exact descriptor above | +| `binding_id` | Matching descriptor digest | +| `nonce` | Canonical base64url encoding of exactly 32 random bytes supplied by the caller | +| `issued_at`, `expires_at` | `issued_at < expires_at <= issued_at + 60`, within signing-key validity | +| `build_report` | Object with `declared_class` (`custom` or `unspecified`) and nullable `source_commit` (40 lowercase hex), `package_version` (string), `target` (string), and `variant` (string) | +| `extensions` | Empty object | + +All five `build_report` members are required; only the four build-fact members may be null when unavailable. `declared_class` is an operator declaration; `unspecified` does not mean official. These are declarations, not an installed-artifact digest. Because `build_report` is outside the binding, an assessment does not appraise the `build_report` of later fresh evidence. Consumers take build provenance and assurance only from the assessment's `build` object and never present a fresh evidence `build_report` as verifier-checked. Use existing immutable owning-product build facts when present; do not add operator/tag/time inputs to Layer 1 builds. An external tag belongs in verified release evidence, not a new compiled publisher input. + +The caller supplies expected host, nonce, environment, and trust policy out of band. No payload-selected verifier URL or public key can establish trust. A verifier MUST correlate the nonce to one outstanding collection operation, expire it after 60 seconds, and consume it once. A valid signature does not establish authorization before enrollment checks. + +### Reader example: declarations and assessments are different + +The following is an annotated **excerpt of a decoded publisher payload**, not a complete valid wire object or a signature to copy: + +```json +{ + "schema": "ts-publisher-evidence-v1", + "build_report": { + "declared_class": "custom", + "source_commit": null, + "package_version": "0.1.0", + "target": "aarch64-unknown-linux-gnu", + "variant": "default" + } +} +``` + +`custom` is the publisher's own declaration. A verifier's separate `build.assurance: declared` keeps that visible. `fingerprint_only` means a narrower provider hash comparison; it still cannot carry `provenance: official`. Only independently bound selected executable bytes plus eligible authenticated release evidence can support `artifact_bound` and `official`. Complete signed examples and rejection cases are in the linked public vectors; a structurally valid object still needs semantic, policy and cryptographic checks. + +### PA-ASSESSMENT and PA-STATUS: registry-issued objects + +The normative assessment/status fields, policy outcomes and optional status mode are defined in the [registry protocol](2026-11-04-publisher-attestation-registry-design.md#pa-assessment-verifier-payload). The shared wire schema and typed JWS rules remain common to both documents. Publisher evidence never signs a registry verdict into its own descriptor. + +## 4. Enrollment and independent trust bootstrap + +[Registry enrollment and consumer trust](2026-11-04-publisher-attestation-registry-design.md#pa-enrollment-authorize-before-endorsing) define publisher DNS/key proof, optional separate operator proof, finite renewal, issuer trust and exit. Production Fastly declarations require real host/key proof; Axum loopback fixtures use separate development trust only. The publisher advertises untrusted issuer hints in discovery, while consumers configure actual trust independently. + +## 5. Publisher HTTP surface and runtime behavior + +### PA-HTTP: fixed, small endpoint + +| Request | Response | +| ------------------------------------------------------------------------------------ | ------------------------------------------------------ | +| `GET /_ts/attestation.jws?nonce=[&kid=]` | `200 application/jose` with evidence compact JWS | +| `GET /_ts/attestation/keys.json` | `200 application/json` with versioned public discovery | +| Missing/invalid nonce or kid, repeated parameter, unknown query member, request body | `400` with a PA-HTTP error body | +| Disabled attestation on either exact reserved path | `404`; no origin fallback and no signing | +| Unsupported method on an enabled reserved path | `405`, `Allow: GET, HEAD`; no signing | +| Enabled but unavailable key/identity/clock or snapshot mismatch | `503`; no unsigned success payload | +| Invalid or mismatched host selection | `421`; no signing or origin fallback | +| Optional configured platform admission limiter denies the request | MAY return `429`; no signed payload | + +1. Both endpoints support GET and HEAD. + +2. HEAD applies the same validation, status and headers as GET, suppresses the response body, and performs no signature operation; it does not itself prove key possession. + +3. Other methods, including OPTIONS, return 405 with `Allow: GET, HEAD` when enabled; disabled paths return 404 for every method. + +4. Browser clients need no custom request headers. + +5. Only the two exact paths in the table are reserved; neighboring paths retain existing routing and may reach the publisher fallback. + +6. No whole-prefix takeover or configurable endpoint is introduced in version 1. + +7. The evidence endpoint requires exactly one `nonce` parameter with the canonical encoding above. + +8. It accepts at most one optional `kid` parameter naming the JWK thumbprint of a key listed in `discovery_keys`; without it the endpoint signs with `active_key`. + +9. A syntactically valid `kid` that does not match the configured inventory returns 400 `unknown_kid`. + +10. An unresolved or ambiguous inventory, expired/not-yet-valid selected key, or metadata/seed mismatch returns 503 `unavailable`. + +11. Selection never falls back to another key. + +12. The `kid` parameter exists so a verifier can prove possession of a staged successor key before it becomes active (PA-KEYS); consumers never need it. + +13. A repeated parameter in one request is malformed; the stateless edge does not maintain a global used-nonce cache. + +14. Single-use outstanding challenges are tracked by the caller/verifier that generated them. + +15. The keys endpoint accepts no query parameters. + +16. Request bodies are rejected with 400. + +17. Nonce-free evidence is not a conforming freshness check. + +18. Error responses on both paths carry `Content-Type: application/json` and a closed body `{"schema":"ts-publisher-error-v1","error":}` of at most 256 bytes, where `` is one of `invalid_request` (400), `unknown_kid` (400), `not_found` (404), `method_not_allowed` (405), `misdirected_request` (421), `rate_limited` (429) or `unavailable` (503). + +19. The body never names the failing setting, key or store. + +20. When several conditions apply, the endpoint evaluates them in this order and returns the first failure: exact reserved path (otherwise ordinary routing), attestation enabled for this deployment (404), method (neither GET nor HEAD is 405), trusted host selection (421), query/body syntax and bounds, including canonical `kid` encoding (400), optional configured platform admission limiter (429 when present), key-inventory resolution (400 `unknown_kid` or 503 `unavailable`), then selected-key validity, identity, clock, snapshot and staging/environment consistency (503). + +21. Inventory resolution and secret reads occur after admission; a well-formed unknown `kid` is therefore not looked up when admission already denies the request. + +22. All responses on these paths, including errors and public discovery, MUST have `Cache-Control: no-store, private`, `Surrogate-Control: no-store`, and `X-Content-Type-Options: nosniff`. + +23. No ETag, Last-Modified, Set-Cookie, authentication redirect, or shared-cache lifetime is emitted. + +24. Conditional requests do not produce 304. + +25. Responses are independent of reader cookies and contain no user IDs or client IP. + +26. Wildcard CORS is permitted for these public GET resources with no credentials; credentialed CORS is prohibited. + +27. A human-readable status page is not required. + +28. Any later UI must explain declarations versus verifier results and cannot present its own rendering as verification. + +29. Discovery is `{ "schema": "ts-publisher-key-discovery-v1", "host": , "keys": [...], "verifiers": [...] }`. + +30. `verifiers` is a unique list of at most 16 canonical HTTPS issuer identifiers (PA-IDENTIFIER) configured by the publisher, empty by default. + +31. An entry advertises a possible source of assessments, not proof that the issuer endorses this host. + +32. Consumers match it to independently configured issuer/trust information before any lookup; it never supplies a trust key or authority to fetch arbitrary URLs. + +33. `keys` holds one to four entries, and each contains only `kid`, public `jwk`, `not_before`, and `not_after`. + +34. Include active and overlap keys; no private key, secret reference, provider token, or mutable URL-as-key-authority. + +35. Discovery is untrusted until matched to enrollment/assessment, and cannot replace retained historical key records. + +36. It contains no verifier trust roots. + +### PA-ROUTING: trusted host selection before evidence + +Version 1 protocol boundaries accept canonical lowercase ASCII DNS names only. Enrollment/configuration/signed claims and lookup paths reject Unicode; optional operator UI tooling may convert an internationalized name before submission, show the resulting ASCII name for confirmation, and must not change it again inside the protocol. No Unicode database version or IDNA mapping library is a runtime protocol dependency. Names beginning `xn--` are treated as opaque ASCII DNS labels; this profile neither converts them nor certifies their Unicode spelling. Identity comparisons use the exact same ASCII name across publisher authority, enrollment DNS, TLS/SNI, and registry lookup. + +For trusted ingress authority only, lowercase ASCII and remove one final ASCII dot before comparing with enrolled canonical host. Reject a second terminal dot, empty labels, userinfo, percent escapes, wildcards, IP literals, invalid ports, and ambiguous/conflicting authority sources. Reject a final host label consisting entirely of decimal digits or `0x` followed by zero or more hexadecimal digits before URL parsing; this includes shortened and hexadecimal IPv4 forms. Labels are 1–63 octets and the host at most 253 ASCII octets, with letters/digits/hyphens and no leading/trailing hyphen. Prefixed DNS challenge owners must separately fit DNS limits. Production uses HTTPS port 443; another port is not silently treated as the same origin. Host and URI authority, when both present, must agree after this normalization. Client-supplied forwarded headers never select signed host identity. Retain the original trusted ingress host before module header rewriting. International-name conversion rules belong to operator tooling and its compatibility tests, not a second implicit identity equivalence rule. + +The baseline has one settings snapshot per service and no host-based configuration selector. + +1. Use that verified snapshot and require its configured attestation host to match the original trusted ingress host exactly; otherwise return 421. Do not borrow another host's settings. +2. `publisher.domain` currently feeds site fields in the runtime; `publisher.cookie_domain` is a separate setting. Neither setting establishes owner identity. The attestation host is independently enrolled as the site, with no parent/subdomain restriction relative to either setting. +3. Version 1 configuration remains a single `[attestation]` table and one attested host per deployment. Other aliases return 421 on reserved paths. Consumers check the exact `site.domain` or page host; an alias may be checked under its canonical host only after independently establishing the site's redirect and recording that comparison target. Never substitute `site.publisher.domain`, which describes the seller. +4. Each site has its own enrollment, deployment configuration and attestation key. Cross-site key reuse is prohibited in v1 to limit a compromise to one site; separate production/staging keys remain mandatory. A site stores one to four seeds during rotation, so provisioning must account for per-site secret quota and repeated staged-key/DNS proofs. This is a deliberate operational cost, not a protocol replay limitation. +5. Configuration-by-host, a list-shaped multi-site configuration, separately enrolled aliases sharing one deployment, and authorized publisher-wide keys are named follow-ups. They require their own configuration migration and compromise/rotation design; this v1 does not claim them. + +### PA-IDENTIFIER: canonical HTTPS identifiers + +`operator_id`, verifier `issuer` identifiers and publication bases are compared as exact bytes after canonical validation, never with URL normalization. The host's last label MUST NOT consist entirely of decimal digits or `0x` followed by zero or more hexadecimal digits; reject these WHATWG numeric-host forms before URL parsing, including hexadecimal and shortened IPv4 representations. A canonical identifier is `https://`, then a host that satisfies the PA-ROUTING host rules (lowercase ASCII, no trailing dot, no port, no IP literal), then an optional path of one or more `/`-separated segments built only from ASCII letters, digits, `.`, `_`, `~` and `-`. Reject uppercase scheme or host, any port (including `:443`), userinfo, query, fragment, percent-encoding, empty segments, a trailing `/`, and `.` or `..` segments. Receivers reject non-canonical identifiers rather than rewriting them, so `https://Operator.example.com/x` and `https://operator.example.com/x/` are both invalid, not aliases of `https://operator.example.com/x`. + +Dispatch evidence after ingress sanitization and host/snapshot resolution, but before Edge Cookie creation, consent processing, device classification, bot challenges, request filters, publisher backend calls, and auctions. An explicit attestation exception to generic authentication is needed so a verifier can reach the public endpoint; it applies only to these exact read-only paths and is covered by route tests. Normal routes retain existing authentication and filtering. + +This needs an explicit startup seam as well as a route exception. The baseline constructs auction/integration state and resolves ordinary application secrets before normal dispatch. Implementation must acquire the verified, identically normalized nonsecret configuration snapshot without `resolve_secret_references`, then load attestation identity independently of those unrelated initialization steps, then dispatch the evidence request without creating the auction application. + +When ordinary application construction succeeds, both paths must use the same loader normalization and snapshot provenance; retained sandboxes must not mix snapshots. An unrelated bidder/secret initialization failure is not an attestation-key failure and must not force the proof path through that failing component. Evidence describes the attestation request's actual snapshot, not a guarantee that ordinary site traffic is healthy. Invalid own host/identity configuration still fails safely. + +On Fastly, the evidence path performs at most one secret-store read (the selected seed); public discovery performs zero. The [current provider documentation](https://www.fastly.com/documentation/guides/compute/edge-data-storage/working-with-secret-stores/) lists five secret reads per Compute request and a paid-account minimum of ten secrets across stores, with additional secrets purchasable; these are account-plan constraints to recheck when provisioning, not a universal ten-secret hard cap. Neither path resolves ordinary application secrets. These counts, including unrelated missing bidder secrets and staged-key selection, are T5/T7 acceptance criteria. + +Use the existing terminal `/_ts/trace` dispatcher as the routing/finalization precedent, with an attestation-specific terminal response marker and no trace-session/auth dependency. Attestation paths bypass ordinary filters, Edge Cookie finalization, telemetry, geo headers, and configured `response_headers`, as well as version/environment diagnostic headers. Enforce the attestation status, headers and HEAD body suppression at the final adapter boundary. Ordinary finalizers must not append client-derived location or identity data after this terminal response. A diagnostic endpoint is not a path to execute arbitrary modules or sign arbitrary bytes. Provider identity and trusted host come from adapter-owned data, not custom integration headers. + +### PA-CONFIG: proposed operator configuration + +```toml +[attestation] +enabled = true +host = "news.example.com" +publisher_id = "owner.example.com" +operator_id = "https://operator.example.com/publisher-attestation" +deployment_id = "22222222-2222-4222-8222-222222222222" +environment = "production" +active_key = "publisher-evidence-2026-11" +discovery_keys = ["publisher-evidence-2026-11"] +verifiers = [] +build_class = "unspecified" +``` + +1. This is a proposed configuration contract, not a claim that these fields already load. +2. Omitted `operator_id` normalizes to null for self-operation; the wire descriptor/projection/challenge still includes the explicit null member so its digest is unambiguous. +3. Default is absent/disabled; `build_class` defaults to `unspecified` and accepts only `unspecified` or `custom`. +4. Unknown keys are rejected. +5. `verifiers` defaults to an empty list and contains at most 16 unique HTTPS issuer identifiers; it is public discovery metadata only and never changes signature trust. +6. Host, IDs, environment, and the selected key metadata must validate when enabled. +7. The selected name is an opaque local key reference, never a private key. +8. `discovery_keys` defaults to a list containing `active_key`; an explicit list must contain 1–4 unique valid local key names and include `active_key`. +9. The discovery endpoint performs bounded metadata reads of those exact names, ordered by thumbprint in its public response; it never enumerates a provider store. +10. Missing, malformed, duplicate-thumbprint, or inconsistent metadata in that list returns discovery 503 rather than publishing a partial set. +11. Without `kid`, evidence signing reads only active-key metadata and its secret and may still succeed when an unrelated overlap entry is unavailable. +12. With `kid`, the endpoint reads at most the four explicitly configured metadata entries, validates their thumbprints, and resolves one unambiguous matching entry before reading only that entry's secret. +13. A missing, malformed or duplicate-thumbprint entry that prevents unambiguous selection fails that explicit selection with 503; it does not change the default active-key path. +14. No secret is fetched merely to search for a matching thumbprint, no store is enumerated, and an unknown selection never falls back to the active key. +15. Disabled mode ignores the list without store reads. +16. Key metadata and private key bytes live in dedicated runtime stores described next. +17. It MUST NOT permit overriding observed provider/service/version, choosing a signature algorithm, choosing arbitrary signed claims, or supplying a purported official checksum. + +Disabled attestation reads no attestation stores. Invalid structural configuration fails ordinary configuration validation with redacted errors; a temporarily missing/malformed key at runtime fails only the attestation endpoint. The config loader MUST NOT eagerly resolve the endpoint's optional private key as a required site-wide secret. Endpoint admission validates the effective metadata and secret together, so a partially propagated rotation returns 503 rather than mislabelling the signing key. + +## 6. Key custody, provisioning, and recovery + +### PA-KEYS: dedicated publisher keys + +Publisher evidence keys are distinct from Layer 1 release signatures, existing request-signing keys, verifier assessment keys, TLS keys, and future auction keys. No modification to today's request-signing protocol or rotation endpoints is authorized by this spec. + +Provision a publisher-generated Ed25519 key through an authenticated operator tool or existing provider secret mechanism. The private seed is exactly 32 bytes encoded as canonical standard base64 in a dedicated secret-store entry. Metadata contains the public JWK, thumbprint, validity interval, and secret entry reference. The runtime derives the public key from the loaded seed and compares it with metadata before signing. A metadata/key mismatch is 503. Secrets never enter source, build inputs, artifact metadata, shared CI caches, app-config JSON, generated documentation, or verifier enrollment. A provisioning tool MUST NOT print or return private key bytes in ordinary output. + +Version 1 preserves the existing portable store inventories and defaults unchanged. Store nonsecret metadata in the existing logical config store `trusted_server_config` at `ts-publisher-key-`, and private seeds in the existing logical secret store `trusted_server_secrets` at `ts-publisher-`. A local name is 1–48 lowercase ASCII letters, digits, or hyphens, starts with a letter, and is unique within that deployment. The prefixed secret name therefore remains at most 61 characters. Metadata has the closed schema `schema: ts-publisher-key-metadata-v1`, `kid`, `jwk`, `not_before`, `not_after`, and `secret_name` (exactly the prefixed secret name). Verify that metadata and the derived public key agree. The ordinary app-config secret resolver MUST NOT eagerly resolve these endpoint-only entries. + +These prefixes are reserved for attestation provisioning. Before writing, the operator tool checks existing entries and ordinary app-config secret references, including each adapter's normalized key/environment mapping, and refuses collisions; it must not overwrite an unrelated value. Rotation replaces only entries explicitly owned by that deployment/key. Physical IDs and resource-link mappings remain operator deployment context, not public claims or new build inputs. Fastly reads by the existing logical links. The running Trusted Server Axum adapter reads metadata through `TRUSTED_SERVER_CONFIG_{STORE}_{KEY}` and seed values through `TRUSTED_SERVER_SECRET_{STORE}_{KEY}`, using its existing uppercase/underscore normalization. EdgeZero CLI local-config files and its separate secret environment convention are not automatically consumed by that runtime. P1a provisioning must use the Trusted Server mapping explicitly, or supply and verify a bridge, including entry-ownership/collision checks and a running-adapter readback test. Cloudflare/Spin flat secret namespaces remain unchanged and production attestation there remains unsupported until separately qualified. + +Do not add a second logical secret store, a second config store, or unconditional attestation resource links to `edgezero.toml`/adapter store metadata. Pinned EdgeZero v0.0.11 treats Axum, Cloudflare, and Spin as single-secret-store adapters; expanding the shared inventory can reject strict validation, config push, and provision for those targeted adapters before feature enablement is considered. This is not a blanket prohibition on additional Fastly physical stores: request signing already opens dedicated `jwks_store` and `signing_keys` outside the manifest, and management uses `api-keys`. A Fastly-only out-of-manifest store is a real alternative, but needs separate linkage/provisioning and gives no portable runtime mapping. V1 chooses existing logical stores to preserve managed deployment and adapter inventories; the cost is that every reader of application secrets can also read publisher seeds, and each seed consumes account-wide secret quota. Enabling up to four keys requires a current provider quota/access check; no fixed account quota is assumed here. Existing-store namespaces avoid that regression and consume no additional physical-store quota or empty-store linkage requirement on disabled deployments. Fastly requires an initial config entry before association under its documented provisioning path; existing application-config provisioning already supplies it. Enabling attestation adds validated entries only after existing link/access/quota checks succeed. No empty new store, placeholder private key, or sentinel is needed. Disabled mode performs zero attestation-entry reads/writes and requires no new resources. + +A future operator may isolate keys in additional physical stores only through an adapter-scoped, separately reviewed capability/provisioning change; this spec does not authorize changing the shared inventory to do so. Store sharing does not mean key reuse: cryptographic purposes, metadata, prefixes, and enrollment are separate, but application code with access to the shared secret store can read those keys. Verifier credentials remain read-only; absence of physical isolation is explicit in the residual trust model. + +Sharing the public release executable does not imply sharing keys between publishers. Existing store linkage is observed provider context, not proof of non-extractability or per-key access isolation. Verifiers receive no write permission to these stores and do not generate publisher keys. Axum fixtures use the existing environment-backed read adapter and a test-only mapping; adding portable logical names does not imply provider namespace isolation or write support on Cloudflare/Spin, whose production paths remain unsupported. + +Key validity is at most 90 days. Metadata may publish a successor before activation. Pre-assessing a successor can avoid the key-only endorsement gap when the other binding fields remain unchanged and the required metadata/configuration has propagated. It does not promise uninterrupted service or a gap-free simultaneous version change. The sequence is: + +1. Stage the successor metadata and seed, with `not_before` no later than now, and add it to `discovery_keys` while `active_key` still names the current key. +2. Request a key transition from the verifiers needed by the intended consumer trust policy through their authenticated enrollment workflow (PA-ENROLLMENT key transition); the provisional API sketch is not a required transport. The verifier issues a fresh DNS challenge naming the successor thumbprint and proves possession by fetching `GET /_ts/attestation.jws?nonce=…&kid=`. +3. The verifier records the successor in the same enrollment under a new authorization generation and issues an assessment for the successor binding before it is active. +4. Change `active_key`. Fresh evidence now matches the successor assessment that already exists. +5. Keep the old key in `discovery_keys` until its last assessment expires; the verifier marks it `retired`. + +An operator who switches `active_key` before step 3 completes has an unendorsed interval until the successor assessment is issued; normal site traffic continues either way. Keep the old public key verifiable for at least the maximum assessment lifetime plus status/cache allowance. Never sign after key expiry. No "last key signs forever" fallback. There is no promise of an atomic distributed store/config transaction: readers observing inconsistent metadata fail the endpoint, and rotation tests must cover ordering and propagation. + +The endpoint checks key validity and current time on every response even if parsed key bytes are sandbox-cached. `clock_unavailable` means the platform time operation fails, not a claim that the clock can detect its own drift. Evidence sets `expires_at = min(issued_at + 60, selected_key.not_after)`; at `now >= not_after`, or when no positive lifetime remains, return 503. No build-time clock floor or publisher-specific build input is added. A reusable sandbox cannot extend an expired key or assessment. Cached metadata has the same documented snapshot lifetime as its bound settings; changing a store does not retroactively update a running snapshot. Operators must plan instance recycling or bounded refresh where required by a qualified adapter. + +On compromise, stop positive renewal, mark affected enrollments/keys compromised, provision a fresh key through trusted operator recovery, re-prove host control, and issue new assessments. Old-key signatures cannot authorize the replacement alone. Routine retirement does not imply historical signatures were forged; compromise and observation unavailability are separate reasons. Historical evidence remains verifiable as historical data, not current authorization. + +## 7. Configuration claims and actual snapshot identity + +### PA-SNAPSHOT: commit only to a defined public projection + +Version 1 claims a deliberately narrow identity projection: + +```json +{ + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" +} +``` + +The object is closed. It is built from effective normalized settings after defaults and permitted overrides, alongside that request's settings snapshot. `configuration.digest` is SHA-256 of its JCS representation; descriptor identity fields MUST equal their projection counterparts. The key and provider version are already separately bound in the descriptor. + +This digest says nothing about auction rules, consent policies, bidder lists, integration safety, source contents, mutable dictionaries, runtime data, resolved secret values, or the full settings document. Neither built-in policy may claim those properties. A broader policy projection is a follow-up requiring named fields, normalization, privacy review, and independent check methods; merely hashing more JSON is not approval of behavior. + +Capture verified envelope identity and the actual store/key/link provenance privately (native adapters may also have environment-based store/key selection; Fastly has no host-based selector) before `BlobEnvelope::into_data()` and secret resolution discard them, and carry that provenance beside the same immutable settings snapshot. Do not publicly expose an arbitrary full-config hash: low-entropy sensitive settings can be guessed. An opaque internal snapshot ID and a public projection digest serve different purposes. A timestamp or envelope checksum is not proof of publisher authorization. + +For both built-in v1 policies, the configuration check validates the signed public projection's schema, digest, equality with the descriptor, and consistency with the enrolled host/operator/deployment/environment tuple. Its method is `public-identity-projection-v1`; a pass means that those declared values are structurally consistent and authorized, not independently observed runtime configuration. The registry does not read the whole app-config envelope merely to compare these five identity values. Even an official executable assessment MUST state this declared-configuration scope; official bytes do not certify settings or behavior. + +The runtime still binds evidence to the snapshot actually used by its handler. The verifier compares fresh evidence bindings during collection and repeats provider active-version/host observations where the selected policy requires them. Covered declared identity or provider version changes cause a bounded retry and then an indeterminate result. Matching observations cannot rule out change-and-restore or global propagation differences. Independent business/consent/integration policy appraisal requires a separate privacy-reviewed method and consumer opt-in. + +Default Fastly, reusable Fastly, Axum, Cloudflare, and Spin can have different settings lifetimes. Evidence MUST describe the actual retained snapshot, not the latest config store fetched later. When fresh evidence and a retained assessment disagree on covered identity/key/version, a consumer rejects the combination and requests reassessment. It never relabels a stale binding as current. + +## 8. Assessment lifecycle + +The [registry lifecycle contract](2026-11-04-publisher-attestation-registry-design.md#pa-collection-deterministic-independent-assessment) defines collection and timing. V1 uses expiry-only consumer acceptance by default: assessments last at most 15 minutes from the oldest observation and no longer than their authorizations. Live status is optional consumer policy with its own availability/privacy cost. Independent configuration reads are excluded from both built-in profiles; projection consistency remains a signed publisher declaration. + +## 9. Consumer verification and networking + +### PA-CONSUMER: consumer verification + +The [consumer acceptance algorithm](2026-11-04-publisher-attestation-registry-design.md#pa-consumer-acceptance-algorithm) compares a fresh publisher response with assessments from independently trusted issuers. It validates exact bindings, signatures, policy and expiry, and checks live status only when expressly configured. A matching package fingerprint cannot satisfy official-executable policy. + +### PA-NETWORK: constrain all external fetches + +Publisher checks require TLS certificate and hostname validation with SNI and authority set to the expected enrolled host. No redirects, downgrade to HTTP, userinfo, URL fragments, caller-selected port, or alternate host supplied by a response. Fixed endpoint paths prevent following a page's attacker-selected verify link. Development HTTP is allowed only for explicitly configured loopback fixtures under the declaration profile, never production. + +Reference-verifier enrollment and publisher fetching MUST defend against SSRF: reject loopback, private, link-local, multicast, unspecified, reserved/nonpublic, and provider metadata destinations; resolve and validate all connection candidates, pin a validated address for the connection, and revalidate on every new connection. Do not merely validate a hostname before a client re-resolves it. Resolver trust and allowed enterprise exceptions require explicit operator configuration and cannot produce the public production profile. Proxy environment variables are disabled by default in the reference collector; explicitly configured egress proxies are trusted components recorded in its policy. + +Provider API traffic uses an independently configured fixed Fastly authority, not report-selected URLs. Provider credentials are never forwarded to publisher, release, discovery, redirect, or status endpoints. Release evidence acquisition follows Layer 1's trusted repository/workflow/publication policy. Verifier status URLs are constructed from configured verifier base and validated UUID, not fetched from an arbitrary assessment member. + +Each network call has a five-second deadline; transient idempotent reads may retry once within the collection deadline, respecting bounded Retry-After. HTTP error bodies are capped at 4 KiB and redacted. Evidence is capped at 16 KiB, discovery at 16 KiB, issuer metadata at 16 KiB, assessment at 32 KiB, and status at 8 KiB, measured after any HTTP decoding. Do not accept unlimited decompression or chunked growth. Provider/package limits belong to the qualification spec. Endpoint rate limiting is optional and owned by the configured operator/platform admission layer; the endpoint MAY return 429 when that layer denies admission. No mandatory Fastly ERL entitlement, global counter, cross-instance quota, or Axum limiter is introduced. Mandatory per-request protection comes from the strict input/body/depth bounds, bounded metadata inventory, at most one selected secret read and one signature, and fixed response/network limits; tests cover bounded work per request without assuming a global counter across WASM instances. + +### PA-ADOPTION: how a consumer uses attestation alongside bid traffic + +Version 1 attestation is a host-level check performed out of band, not a per-bid signal. A consumer such as an SSP, DSP or verification vendor evaluates a host when it starts trading with it and re-evaluates on its own schedule. It caches the result per `(host, environment)` and never relies on a positive result past the `valid_until` defined in the registry's consumer result (PA-CONSUMER), which includes assessment expiry and, only in the opt-in live-status mode, the required statuses' expiry/freshness deadlines. Known withdrawal, compromise or a changed consumer trust policy invalidates a cached result before that deadline. Each evaluation costs one fresh publisher fetch plus, unless a retained assessment is still valid, one registry lookup; no per-bid registry or publisher lookup is specified or expected. + +What a version 1 consumer can conclude about bid traffic is deliberately limited: + +- An accepted result says that, at evaluation time, an independently enrolled key for the host answered a fresh challenge and described the stated deployment. +- Compare the attested site with `site.domain` or the independently selected page host, never `site.publisher.domain` (the seller domain). Such a match is still a sender claim, not a cryptographic link between the bid request and this result. +- Trusted Server's existing request signing (`ext.trusted_server.signature` with a `kid` published at `/.well-known/trusted-server.json`) proves only that a key served from that host's HTTPS origin signed the request. Version 1 keeps request-signing keys separate from attestation keys and does not attest them. + +A separately versioned request-signing bridge is an optional future candidate, not a selected next profile or a version 1 deliverable. It could connect request-signing keys to an assessed deployment, but its claims, encoding, rotation and reassessment behavior require the later auction/bid design. This specification leaves stable deployment, key and assessment identifiers for that work; it does not prescribe an extension shape, freeze the later wire format, or pre-approve a cryptographic link. Signing auction or bid payloads with the attestation key remains out of scope. + +### PA-REFERENCE: delivery boundary + +This repository delivers the publisher endpoint, portable types/pure verification, and a bounded host-target reference verifier. Registry publication, administration, signer recovery, governance, retention and the demo are specified in the [separate registry contract](2026-11-04-publisher-attestation-registry-design.md). Production checkpoint/fencing backend engineering is a registry deployment obligation, not code required inside the edge publisher binary. Moving it to that contract does not waive the no-stale-issuance invariant. + +## 10. Failure model, diagnostics, and privacy + +### PA-FAILURE: distinguish rejection from uncertainty + +| Reason code | Meaning | Default effect | +| ---------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------- | +| `malformed` | Invalid syntax, schema, limits, or inconsistent hashes | Reject | +| `signature_invalid` | Wrong key, message type, or signature | Reject | +| `publisher_unauthorized` | Publisher/account/site authorization absent, out of scope, expired or withdrawn | Reject enrollment/issuance | +| `operator_unauthorized` | Claimed operator identifier lacks valid independent account association/delegation | Reject enrollment/issuance | +| `state_recovery_required` | Durable authorization history or fencing cannot be established | Stop signing/current status | +| `host_mismatch` | Expected host or enrolled authorization differs | Reject | +| `environment_mismatch` | Wrong staging/production or unavailable identity | Reject production | +| `key_unauthorized` | Publisher key not valid for the evidence lifetime, or reported retired by status | Reject current acceptance | +| `key_compromised` | Confirmed compromise recorded by trusted authority | Reject; rotate/re-enroll | +| `nonce_mismatch` | Missing, stale, repeated, or wrong challenge | Reject freshness | +| `stale` | Evidence, observations, assessment, enrollment, or status too old | Reject current acceptance | +| `binding_mismatch` | Version/key/projection/deployment tuple differs | Reject combination; reassess | +| `release_ineligible` | Authentic release not accepted under configured policy | Reject official profile | +| `artifact_mismatch` | Qualified observation binds a different executable | Fail official check | +| `artifact_binding_unproven` | Only fingerprint/declaration or unqualified method available | Indeterminate official check | +| `configuration_unobservable` | Inputs to an explicitly selected independent configuration method are unavailable | Indeterminate selected optional method | +| `provider_unavailable` | Outage, timeout, permission, incomplete state | Indeterminate; stop positive renewal | +| `observation_race` | Observed state changed during collection | Retry bounded operation; then indeterminate | +| `withdrawn` | Assessment or enrollment withdrawn | Reject current acceptance | +| `status_unavailable` | Explicitly required live status cannot be validated | Reject current official assurance | +| `policy_untrusted` | Unknown issuer/policy/method or threshold not met | Reject endorsement | +| `clock_unavailable` | Platform time operation fails; arbitrary clock drift is not detectable | Stop current assurance | + +No API response should reduce these distinctions to a universal `trusted: true`. Public verdicts include expected host, issuer/policy, observation interval, expiry, build class/assurance, coverage, and reason codes. They also state the residual that applies to every profile, including the official one: the publisher key is held by the operator and is not bound to the code that uses it. A declaration can satisfy the declaration profile without satisfying official-deployment policy. A custom build is not intrinsically a failure; it fails a policy that requires official bytes. + +Attestation failure does not shut down normal publisher traffic by default. It removes the attestation claim or returns an endpoint error. Any site-wide blocking policy belongs to explicit operator configuration outside this feature. Ordinary request-signing behavior remains unchanged. + +### PA-PRIVACY: minimize disclosures and retained evidence + +Public objects disclose host, IDs, key, provider service/version, public identity projection, build declarations, appraisal scope/times, and potentially official release identity. Operators explicitly opt in to this disclosure. No browser identities, IPs, cookies, consent strings, bidder credentials, backend URLs, full configuration, private keys, or provider tokens appear in public evidence. + +Log operation IDs, outcome codes, durations, and shortened safe object IDs through `log`; do not log nonce query strings, bodies, raw config, secret references, provider authorization headers, or sensitive provider error payloads. Public errors use generic codes; privileged diagnostics are separately access controlled. Do not publish arbitrary low-entropy private digests. + +The reference verifier retains exact public signed objects, sanitized observation records, policy/release evidence identities, and enrollment/recovery audit events for 90 days by default in restricted storage. Public historical objects may be archived longer under a stated operator policy. Raw credential-bearing responses and signing secrets are excluded from evidence archives; retention and credential backup have separate access controls. Restrict state directories and files to the owning account. Disable unrelated third-party analytics on a reference verification page, if one is added later. + +Fastly read-only credentials may still expose account-wide information: service restrictions on global read scopes do not imply isolation of non-service endpoints. Before enrollment, document actual endpoint permissions, resource exposure, rotation/expiry, and separation from unrelated account assets. Prefer a dedicated deployment account where required by the observed permission model. No verifier requires provider write/deploy rights. + +## 11. Repository integration and compatibility + +### PA-INTEGRATION: fit existing seams + +| Existing surface | Proposed work and constraint | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | +| `crates/trusted-server-core/src/config.rs` and `settings.rs` | Typed opt-in attestation settings, strict validation, narrow projection | +| `config_payload.rs` and `settings_data.rs` | Retain provenance beside the actual settings snapshot before secret resolution; do not change existing secret semantics | +| `platform/` runtime services | Adapter-owned identity and dedicated store access; no publisher-controlled identity override | +| `trusted-server-adapter-fastly/src/app.rs` and `main.rs` | Exact early route, request-local identity/snapshot, default and reusable admission semantics | +| `trusted-server-adapter-axum/` | Isolated development fixtures only; no production/staging support or measured provider identity | +| Cloudflare and Spin adapters | Preserve existing routes/builds; unsupported production attestation remains explicit; qualify separately before adding claims | +| `request_signing/` | Reuse reviewed crypto primitives only where useful; retain separate keys, entry namespaces, types, and endpoints | +| Host CLI/reference verifier | Enrollment input, collection, offline verification, public assessment/status generation; target-isolated dependencies | +| Layer 1 release evidence | Authenticate retained raw executable bytes and eligibility; no runtime GitHub verification or new publisher-specific release build | +| JS bundles and publisher HTML | No new JS injection, user flow, or auction modification | + +Core protocol types and validation should have narrow modules and strong domain types for host, deployment ID, binding ID, key thumbprint, and times. Use `error-stack` and existing runtime store/HTTP abstractions. Runtime clock access must use the repository's WASM-safe clock conventions. No dependency is selected solely by this design; verify support across targeted builds before implementation. + +The endpoint and its required protocol/key-reading capability MUST be compiled into the authenticated release executable used for an official deployment claim, then enabled at runtime with publisher config/keys. Adding an operator-specific module or recompiling in a key changes the artifact and cannot inherit that official release identity. Core versus linked module placement remains an implementation choice only if the official release composition includes it. Older releases lacking the endpoint are not made capable by configuration alone. + +Do not depend on the historical prototype's full module/source/filter stack (section 14) to ship this feature. An existing or merged module endpoint seam may host it if it can enforce the routing/key/snapshot contract; otherwise add the smallest explicit adapter route. The architecture is independent of code placement. Do not add a broad generic attestation plugin framework. + +Existing deployments remain disabled and preserve normal behavior. Existing `/.well-known/trusted-server.json` key discovery and `/verify-signature` retain their current purposes; they do not become publisher-attestation authorities. No automatic migration of their keys is allowed. The historical prototype's bespoke ES256 envelope and compiled-key schedule are not wire-compatible with this version. Operators must enroll new runtime keys; do not silently accept legacy objects under new assurance labels. + +The [superseded January proposal](2026-01-15-attestation-design.md) is background only. Its full-config commitments and embedded final-binary checksum must not become requirements by implication. The release specification/guide on #1435 distinguishes raw executable identity, source facts, and deployment observations; consume verified inputs without assuming a later release index is shipped. A candidate/unsigned index cannot authorize an official assessment. + +## 12. Delivery boundaries and qualification gates + +This is the cross-component architecture contract. It is not an implementation plan or permission to activate live credentials/services. Each delivery milestone gets its own bounded implementation plan and any necessary phase-specific spec amendment; provider experiments require explicit authorization of named disposable resources. + +| Milestone | Deliverable | Exit gate | +| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| P0: provider feasibility | Retained Fastly source/provider evidence and supported method decision | Companion qualification review; unresolved official binding remains closed | +| P1a: publisher evidence | Runtime keys, strict schemas, host/snapshot binding, endpoint, pure verification library, fixtures/docs; Axum development fixtures only | Fastly evidence verifies independently; evidence remains self-declared without an assessment | +| P1b: declaration appraisal | Separate reference verifier, authenticated declaration enrollment and signed assessments | Enter after task-force question 2 settles verifier operation/trust; declaration end-to-end proof, no installed-code claim | +| P2: independent deployment appraisal | Provider-backed enrollment/appraisal, qualified provider/release collection with declared identity consistency, signed assessments/status | Strong profile enabled only for methods passing P0 and runtime tests | +| P3: operational adoption | Operator-run renewal/status service and recovery runbooks | Chosen consumer-mode availability/privacy SLOs, credential exposure review, rollback and incident rehearsal | +| P4: demo registry | Stacked illustrative registry with enrollment/status display and consumer verification walkthrough | Fictional/local fixtures, clear declared/fingerprint/official labels; no implicit public accreditation or live provider qualification | + +Neither P1a nor P1b closes the full independent-verification scope of #161. If P0 cannot justify a strong binding, maintainers may ship the declaration profile and fingerprint observations, but must record the unsupported official-deployment claim and revise acceptance rather than quietly mark it achieved. + +The declaration profile has standalone value if the official profile never qualifies. It gives consumers a domain-bound publisher key that an independent verifier enrolled, proof that the host answered a fresh challenge, a stable deployment identity, bounded assessment lifetimes and withdrawal. It does not give them proof of installed code. Should no Fastly method qualify, the P0 report records the missing provider capability (for example, authenticated selected-module digest or readback) as an external dependency, and the official profile stays disabled until a provider capability or a separately specified assurance method closes it. P4 is a named stacked demo-registry follow-up after the protocol/reference verifier; its UI and hosting are not core-feature acceptance dependencies. Future auction/bid work is not an acceptance dependency. + +No calendar commitment is made here. Endpoint work, provider feasibility, and a continuously operated verifier are different effort categories. Estimates must follow the qualified method, phase scope, dependencies, and reviewer capacity; signature code size is not a measure of complete assurance-system effort. + +## 13. Verification and acceptance matrix + +### PA-TESTS: evidence before assurance + +Tests use fictional hosts/data and generated test-only keys, never operator credentials or production signing keys. A fixture's declared expected result cannot stand in for provider qualification. CI tests must include independent verification of produced JWS bytes and malicious negative fixtures, not only round-trip tests through the same serializer. + +| Test family | Required cases | Requirements | +| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | +| T1 encoding/crypto | Independent JWS verification; altered segments; wrong key/type/curve/algorithm; non-canonical `S`, non-canonical points and small-order `R`/public keys; duplicate members; limits; canonical thumbprints/hashes; exclusive expiry boundary; structurally valid semantic contradictions | PA-WIRE, PA-BINDING | +| T2 endpoint freshness | Exact nonce; repeated parameters and invalid encoding; `kid` selection of a listed staged key, unlisted or expired `kid`; future/expired/key-boundary times; unavailable clock | PA-EVIDENCE, PA-HTTP | +| T3 host routing | Exact host; parent/suffix/lookalike rejection; canonical ASCII/A-label/trailing dot/port/authority conflicts; forwarded-host spoofing; second name on the same service returns 421; non-canonical HTTPS identifiers and numeric/hexadecimal IPv4 forms rejected | PA-IDENTITY, PA-ROUTING, PA-IDENTIFIER | +| T4 routes/privacy | No bot/auth/EC/consent/auction dependency; disabled reserved paths; unsupported methods; unchanged neighbors; no backend calls; conditional requests; final no-store; CORS; no user identifiers; closed error body and evaluation order when several conditions apply | PA-HTTP, PA-ROUTING, PA-PRIVACY | +| T5 runtime keys/stores | No key; corrupt seed; public-key mismatch; rotation propagation; staged-key proof before activation; bounded discovery list; missing overlap metadata; no enumeration; evidence at most one secret read, discovery zero, unrelated app-secret failures isolated; expiry in cached sandbox; Fastly staging flag contradicting the configured environment returns 503; no secret disclosure; namespace collisions; strict disabled validation/provision/config push without inventory expansion; absent optional entries | PA-CONFIG, PA-KEYS | +| T6 runtime snapshots | Actual retained versus latest config; projected versus uncovered changes; declared scope; multi-host mismatch; cached sandbox; version/config/key changes | PA-SNAPSHOT | +| T7 adapter integration | Running Fastly evidence independently verified; evidence reads at most one selected seed and discovery reads zero, even with missing ordinary bidder secrets; isolated Axum development declarations; canonical host consistent across ingress/TLS; ordinary deploy emits new version binding and rejects old assessment combination | PA-REFERENCE, PA-BINDING | + +Registry enrollment, appraisal, consumer acceptance, provider collection and durable recovery tests belong to the [registry verification matrix](2026-11-04-publisher-attestation-registry-design.md#registry-verification-matrix). Provider executable-binding experiments belong to the Fastly qualification companion. These separate acceptance boundaries do not require auction/bid implementation. + +Before publisher-evidence handoff, run changed core/adapter tests with target-matched aliases, Fastly reusable tests if that path is touched, host verifier/CLI tests, strict applicable clippy, formatting, domain lint, documentation checks, and an actual running-adapter fixture. Use native tests for protocol logic and Viceroy for Fastly endpoint behavior. Cloudflare/Spin compilation/parity checks demonstrate compatibility only, not production provider qualification. No HTML EOF harness is required unless implementation changes publisher/HTML processing. Before an implementation PR handoff, run all repository-mandated CI gates, documenting any unavailable external runtime checks without calling them passed. + +Acceptance of this design requires review of the contracts and explicit unresolved-provider gates, not a claim that implementation tests have run. Acceptance of the full feature requires: + +- **AC1:** Authorized publisher evidence verifies independently; malformed, forged, stale, cross-host, cross-environment, and unauthorized-key objects fail. +- **AC2:** Keys are runtime-only and separate from release/request signing; rotation/expiry/compromise behavior is demonstrated. +- **AC3:** The exact request-used identity projection and service/version/key binding survive the real runtime transport. +- **AC4:** A qualified independent method authenticates the installed selected executable before official provenance is asserted. If that gate remains unresolved, only the narrower milestone is accepted; the full independent-verification feature remains incomplete and the strong profile stays unsupported. Any reduced-scope closure requires an explicit maintainer disposition. +- **AC5:** Declaration, custom, fingerprint-only, official, unavailable, failed, and withdrawn outcomes cannot be confused by the reference consumer or documentation. +- **AC6:** Signed assessments, default expiry-only acceptance, optional live status, monitoring outages, sequence recovery where applicable, and multiple-verifier trust policy are exercised end to end. +- **AC7:** No auction/bid behavior changes, new user-data collection, publisher-specific official build inputs, or mandatory central administrator are introduced. + +## 14. Prior work and references + +The role separation follows RFC 9334: attester, verifier, relying party, reference-value provider, and endorser are roles, not a requirement for one administrator. This design borrows the operator endpoint's narrow declarations and exact-byte verification lessons while requiring independent evidence for stronger appraisal. + +| Work | Learning and treatment | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [jwrosewell/trusted-server#7](https://github.com/jwrosewell/trusted-server/pull/7), inspected at `59e09b6cd9632b8198deaa92b38ed7d5b841e06e` | Historical closed/unmerged attestation prototype: bounded nonce, explicit limits, endpoint-local errors, independent OpenSSL harness. Reject compiled private keys and endless last-key signing; it supplies no installed-artifact measurement | +| [IABTechLab/trusted-server#1297](https://github.com/IABTechLab/trusted-server/pull/1297) and [#1353](https://github.com/IABTechLab/trusted-server/pull/1353) | Related upstream stack history; attestation was removed from #1353, which is endpoint-seam work, not an implemented assurance dependency | +| [jwrosewell/trusted-server#34](https://github.com/jwrosewell/trusted-server/pull/34), inspected at `12b1f467aeab9cfb5650e7fbc2b32f654d323ebd` | Distinguish publisher identity from backend source and outbound Host; source refactor is not required here | +| [jwrosewell/trusted-server#47](https://github.com/jwrosewell/trusted-server/pull/47), inspected at `86f77a124d49bcfe27d00be2b397b481786340fb` | Request-cycle parity and terminal privacy protections matter; no independent executable verification supplied | +| [#846](https://github.com/IABTechLab/trusted-server/issues/846) | Secret migration background. Current baseline already resolves typed secret references; open issue status is not proof of current plaintext blob behavior | + +Primary provider sources: [Fastly package API](https://www.fastly.com/documentation/reference/api/services/package/), [token scopes](https://www.fastly.com/documentation/reference/api/auth-tokens/user/), [edge data stores](https://www.fastly.com/documentation/guides/compute/edge-data-storage/about-edge-data-stores/), [secret-store API](https://www.fastly.com/documentation/reference/api/services/resources/secret-store/), and [audit events](https://www.fastly.com/documentation/reference/api/account/events/). These support provider semantics, not an inference of exclusive runtime key access or globally consistent observation. + +Normative encoding references: RFC 7515 (JWS), RFC 8037 as updated by RFC 9864 (fully specified Ed25519 JOSE), RFC 7638 (JWK thumbprints), RFC 8725 (strict validation and message separation), and RFC 8785 (JCS). Verify interoperability and target support during implementation qualification; no external standard certifies this project's claim accuracy. + +The v1 host contract is canonical ASCII DNS syntax; Unicode/IDNA conversion, if offered by operator tooling, is explicit preprocessing outside protocol identity checks. Using JWS does not claim EAT conformance; RFC 9711 EAT alignment or registered public media types may be considered later without weakening this private profile's validation. + +## Task-force decisions and open questions + +The proposed v1 defaults now explicitly select expiry-only acceptance, optional live status, publisher-declared identity consistency, isolated Axum development fixtures, runtime-enabled capability in the authenticated official release, registry-side endorsement storage with read-only provider access, and provider polling as the monitoring baseline. Fresh DNS challenges are retained with automation support. Outermost traffic placement is not a verified v1 claim. + +Before implementation/production adoption, the task force must decide: + +1. Which Fastly selected-executable binding method passes qualification? If none does, is a clearly narrower declaration/fingerprint milestone useful without closing the strong feature? +2. Who will operate verifiers, which policies/keys consumers should trust, and whether any consumer requires the optional live-status mode? +3. Which publisher/site and operator DNS/account-delegation methods are accepted, and how is DNS renewal automated for a multi-site publisher? The v1 baseline costs about 400 site DNS updates per 30 days for 200 sites and two verifiers, plus owner/operator proofs. Persistent per-site delegation with publisher-level fresh renewal is the named alternative and requires explicit revocation rules. +4. Which governance/appeal procedures and registry service agreements apply, including allocation of legal responsibilities? Cryptographic validity does not settle those terms. +5. What deployment and acceptance scope should the stacked demo registry have? It cannot present fictional evidence as production qualification. +6. Is independently checked full configuration or outermost traffic placement required in a later profile? Each needs a new evidence/privacy contract and qualification; neither may be inferred from official bytes. +7. Should the request-signing bridge profile (PA-ADOPTION) be the next profile, so bid recipients can tie signed bid requests to an attested deployment? Which consumers would adopt it, and how should request-signing rotation interact with reassessment? +8. What maximum validity should verifier signing keys have, and should publishers be able to restrict which verifiers may enroll their host (an issuer-allowlist DNS record, similar in spirit to CAA)? Version 1 requires finite, published verifier key validity but sets no maximum and defines no allowlist record. + +These are explicit approval/qualification gates, not implementation TODOs or permission to invent missing provider capabilities. + +## Review log + +- **Round 1 (Codex, independent security and runtime review):** Corrected policy-conditional inputs, exact-path routing, store wiring and rotation. +- **Round 2 (Codex, independent security and runtime review):** Corrected overlap-key discovery without store enumeration. +- **Round 3 (Codex, independent security and runtime review):** No blocking contract issue remained. +- **Follow-up review (Codex, independent security, feasibility and standards review):** Corrected existing-store compatibility, operator authorization, JOSE algorithm identifier and transport limits; added schemas, vectors and recovery fencing. +- **Second detailed pass (Codex, independent security and feasibility review):** Corrected authorization-expiry caps; no blocking contract issue remained after correction. +- **Split-contract review (Codex, independent security and contract review):** Aligned the signed configuration-method identifier; no blocking contract issue remained after correction. Document and fixture checks do not qualify runtime behavior or a Fastly method. +- **Cleanup review (Codex, independent contract review):** Checked contextual fingerprint evidence, deployment gaps, operator lifetime, rejection semantics and test ownership; corrected declaration-profile wording to permit optional provider observations. +- **Cleanup follow-up (Codex, independent contract review):** No remaining blocking consistency findings. Structural and signature fixtures pass; runtime and provider qualification remain unexecuted. +- **Final detailed review (Codex, independent security, runtime-feasibility and protocol reviewers):** No blocking findings. Clarified Axum provisioning mapping, optional provider observations, and P1 declaration enrollment/assessment ownership. Runtime implementation and provider qualification remain unexecuted. +- **Gap review (Claude Code, independent security, consistency, implementability, adoption and lifecycle review):** Identified the risk of sharing production stores with staging and added a staging/environment conflict check; the later correction below distinguishes version-specific physical links from service identity. Added strict Ed25519 verification, exclusive expiry, canonical HTTPS identifiers, the optional `kid` parameter for staged-key proof before rotation, a closed error body with evaluation order, the one-host-per-deployment rule, the consumer adoption section with the request-signing bridge as a later profile, the standalone value of the declaration profile, and two task-force questions. Registry and vector changes are recorded in the registry review log. +- **Self-review (Claude Code, independent contract and fixture review):** No blocking findings. Renamed the evidence path to `/_ts/attestation.jws` to match its `application/jose` body, clarified the discovery `keys` bound, and fixed live-status sequence ordering in the registry contract. The schema keeps draft-07 in its `$comment` and in the wire-schema text rather than a `$schema` URI, because the repository URL-host lint rejects that host and JSON cannot carry a per-line suppression; validators must select draft-07 explicitly. Fixture signatures, digests and schema results were rechecked independently. +- **Correction review (Codex, independent contract and security reviewers):** Corrected cache deadline dependencies, version-scoped Fastly isolation, staged-key lookup, status-generation semantics and strict small-order signature-point rejection. Kept the enrollment API provisional and the request-signing bridge an optional later candidate; corrected targeted fixture inputs and outcomes. +- **Correction follow-up (Codex, both independent reviewers):** No remaining blocking findings in the design/fixture scope. Forty focused cases cover expiry-only/live cache behavior, original timestamp deadlines, sequence invalidation and strict signature boundaries. This does not qualify runtime behavior or the Fastly method. + +- **Maintainer-feedback correction (Codex):** Corrected numeric-host exclusions, nonce/key endorsement, policy inputs, runtime seams and support scope; added reproducible fixture tooling. Historical clean review entries describe their examined revisions, not current approval. Runtime implementation and provider qualification remain outstanding. + +- **Round-2 correction (Codex, internal author check):** Separated owner identity from site/cookie configuration, defined verified publisher membership and explicit per-site key/renewal costs, and named alias/multi-site follow-ups. Updated the structural publisher identifier and synthetic cases before wire freeze; implementation still requires maintainer spec approval. diff --git a/docs/superpowers/specs/2026-11-04-publisher-attestation-fastly-qualification-design.md b/docs/superpowers/specs/2026-11-04-publisher-attestation-fastly-qualification-design.md new file mode 100644 index 0000000000..64d16ad94f --- /dev/null +++ b/docs/superpowers/specs/2026-11-04-publisher-attestation-fastly-qualification-design.md @@ -0,0 +1,145 @@ +# Publisher attestation: Fastly executable-binding qualification + +**Date:** 2026-11-04. **Status:** Proposed read-only investigation contract and conditional experiment design; no production provider qualification or live mutation authorized. + +**Parent contract:** [Publisher attestation](2026-11-04-publisher-attestation-design.md). **Registry protocol:** [assessments and operations](2026-11-04-publisher-attestation-registry-design.md). **Scope:** authenticate the join from an enrolled publisher host and service/version to an installed selected executable, then to independently verified Layer 1 bytes. This is a feasibility/assurance gate, not auction/bid attestation or a new release producer. + +## 1. Question and non-assumptions + +Can an independent verifier, with read-only Fastly credentials, establish that the executable selected for an enrolled host's active production service/version is byte-for-byte an eligible official Trusted Server WASM? + +The verifier starts with authenticated official raw WASM bytes, not merely a checksum string, plus existing enrolled service/host context. It must not infer execution from source headers, service environment variables, upload success, CLI exit zero, publisher-supplied receipts, or the attestation endpoint's own report. No public API for retrieving deployed executable bytes has been established by this investigation. + +The strong publisher profile remains disabled until this question has a defensible method. A provider fingerprint observation can still be recorded at lower assurance. A matching fingerprint must not be labelled an official installed binary merely because it appears plausible. + +## 2. Confirmed client algorithm + +The repository pins Fastly CLI 16.0.0, whose annotated tag resolves to commit `34cef1b0bdbc35d0f1af883a1955d7fe9b3a5ad5`. At [hashfiles.go](https://github.com/fastly/cli/blob/34cef1b0bdbc35d0f1af883a1955d7fe9b3a5ad5/pkg/commands/compute/hashfiles.go), `getFilesHash`: + +1. Extracts nondirectory package entries through `packageFiles`. +2. Stores each entry's contents by `NameInArchive` in a map; a repeated exact name replaces the earlier value. +3. Sorts the map's names lexicographically. +4. Concatenates contents in that order into SHA-512. Names and lengths are not included in the hashed stream. + +In symbols, for sorted unique entry names `n1 ... nk`: + +```text +files_hash = hex(SHA-512(contents[n1] || ... || contents[nk])) +``` + +For a package with precisely one `bin/main.wasm` and one `fastly.toml` under the same conventional root, their lexical order yields `SHA-512(wasm_bytes || manifest_bytes)`. Additional files also contribute. Tar headers, gzip metadata, and directory entries are not part of that client stream. + +At [validate.go](https://github.com/fastly/cli/blob/34cef1b0bdbc35d0f1af883a1955d7fe9b3a5ad5/pkg/commands/compute/validate.go), `packageFiles` skips directories and delegates archive extraction; this code is not a security specification for unique paths/member types. Package validation checks expected basenames and must not substitute for a strict verifier grammar. + +The [provider API](https://www.fastly.com/documentation/reference/api/services/package/) describes `metadata.files_hash` as a hash of files within the package and exposes metadata GET and package PUT. It does not by itself establish server acceptance/selection grammar, identical hash calculation, or selected-module readback. The deprecated `metadata.hashsum` field is documented as the hash of the Compute package, deprecated in favor of `files_hash`; it is a different field and must not be substituted. + +## 3. Security consequence + +SHA-512 collision resistance does not make this concatenation an injective package encoding. Different names or divisions between file contents can represent the same hashed byte stream without any SHA-512 collision. Duplicate names and parser differences add ambiguity. Whether an attacker can exploit those properties while satisfying Fastly's actual package/WASM selection rules is not established here. + +Strictly inspecting a publisher-supplied canonical archive only proves properties of that supplied archive. An attacker controlling upload can submit a different provider-accepted archive. A locally enforced two-file grammar does not constrain Fastly's remote accepted set. Nor does a matching provider hash prove the publisher supplied the same archive that was uploaded. + +Strong qualification therefore requires one of: + +| Method | Evidence necessary | +| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Provider-authenticated selected-module digest | Documented/authenticated provider measurement naming exact service/version and selected raw module bytes, with semantics and algorithm qualified | +| Authenticated selected-byte readback | Retrieve provider-authenticated deployed package/module for exact service/version, independently parse selection, hash exact module bytes, and verify its association with the active host | +| Qualified package-hash binding | Documented provider acceptance/selection and hashing contract, corroborated by experiments, plus a security argument that no accepted alternate package with a different selected module can produce the same expected stream under that contract | + +Bounded positive and negative experiments cannot by themselves prove the final method safe for all accepted packages. If provider contract or selected-byte evidence is missing, retain `fingerprint_only` assurance. Operator-controlled upload tooling, a package comment, or a receipt signed by the publisher is not an independent provider invariant. + +## 4. Required observation chain + +Each link needs a concrete authenticated evidence source, its retrieval time, version scope, coverage, and failure behavior: + +```text +explicitly enrolled publisher host + → provider host/service mapping and TLS/routing context + → production active service version + → package identity for that exact version + → selected executable bytes or qualified equivalent binding + → authenticated eligible official release bytes +``` + +Investigate both versioned and versionless domain mechanisms supported by the actual account, staging, attached resources, resource linkage where needed by the qualified method, and edge propagation. V1 does not require app-config payload reads for identity consistency; the signed public projection remains a publisher declaration. Resolve the production environment explicitly through supported active/environment associations, not latest version or a legacy staging boolean. The [version API](https://www.fastly.com/documentation/reference/api/services/version/) documents some historical booleans as unused; distinguish those management fields from runtime staging identity. Provider API reads may describe desired/control-plane state rather than execution at every POP. An HTTPS challenge observes one serving path; a browser-visible page or response header cannot authenticate the management-plane mapping alone. + +Read active provider version/host mapping before and after the challenge, and compare fresh signed public identity bindings when needed. Do not treat those declared values as independently observed configuration. Reject inconsistent intervals. Record that matching observations do not exclude a change-and-restore or prove simultaneous global convergence. State whether a supported profile describes control-plane configured deployment, sampled response, or both. + +## 5. Strict local package inspection + +For any method consuming an archive, the verifier must use a safe streaming/parser strategy without extracting attacker-selected paths to the filesystem. Enforce a qualified allowlisted archive layout, one selected executable, exact member names/root, unique canonical paths, allowed regular-file/directory types, and exact manifest bytes. Reject absolute paths, traversal, backslash aliases, normalized duplicate names, links, special files, nested archives, multiple executable candidates, unexpected files, malformed headers, truncated streams, and unsupported extensions. If PAX/GNU metadata is necessary, specify its allowed interpretation and reject contradictory names/sizes. + +Initial parser safety limits are 100 MB compressed, 256 MiB total expanded regular contents, 128 MiB per member, 16 entries including directories, depth 4, 512-byte names, and a 30-second inspection deadline. These are verifier bounds, not claims of identical provider limits. Confirm actual official artifact fit before adoption; a larger official artifact requires an explicit reviewed profile limit increase, not silent expansion. Hash while reading; do not hold several attacker-controlled expanded copies unnecessarily. + +Retain the exact inspected file inventory, content digests/sizes, selected-member identity, and archive digest privately. The archive SHA-256 identifies the submitted envelope; it is not the canonical release executable identity. Publisher `fastly.toml` can disclose private deployment metadata and must not be published in full as attestation evidence. Never execute its scripts or invoke a build-capable command to inspect it. + +## 6. Investigation and controlled experiments + +### Q1: source and provider contract review, read-only + +- Pin the examined CLI version and source commit; inspect `getFilesHash`, archive parser, package validation, upload, skip-same-hash, and activation behavior. +- Identify actual provider metadata and active-version APIs, host mapping APIs, resource links, selector observation, and read permissions. +- Verify the parent contract's existing-store namespaces against the pinned EdgeZero capability model. Do not add a second portable logical secret store or require empty metadata-store association; disabled attestation must preserve strict validation/config-push/provision behavior and existing store quotas. Check exact entry ownership/collision and physical link resolution before enabling keys. +- Ask whether authenticated selected-module digest/readback exists and under what access/retention conditions. Record documentation or provider confirmation; do not invent an API. +- Document what each field measures and whether it is reported, derived, mutable, versionless, or sampled. +- Separate supported facts from hypotheses in a method report. Runtime reports and client source do not establish server semantics. + +### Q2: local fixtures, provider-free + +- Compute client fingerprints from canonical official-byte packages with independently retained manifests and compare against pinned CLI output. +- Vary compression, tar timestamps/order/root prefixes, manifest whitespace, contents, extra files, duplicate/alias paths, links, and headers. Record fingerprint and local-parser outcomes separately. +- Demonstrate generic concatenation/name ambiguity with harmless fixture bytes. Do not claim a successful provider exploit from a local hash equality alone. +- Validate safe archive rejection and resource bounds with malicious small fixtures. No real credentials or publisher domains enter fixtures. + +### Q3: disposable provider observations, requires explicit resource authorization + +Before any upload, activation, token creation, or domain/resource change, obtain authorization naming the disposable account/service, allowed actions, cost bounds, and cleanup owner. Use fictional fixture data; do not modify an existing publisher service. Test instrumentation and a reviewable experiment plan are prepared before requesting that authorization. + +- Upload canonical packages containing retained official or harmless test module bytes; independently read provider metadata for exact allocated versions. +- Compare local/client/provider hashes; vary only one property per trial. Distinguish successful upload, selected module, activation, and observed response. +- Exercise provider-accepted naming/root/member rules, duplicate/alias handling, extra entries, and safe selected-module distinguishers. A provider rejection is evidence about that fixture only. +- Check staging versus production, new-version activation, same-hash upload skipping, domain mappings, and before/after read races. Verify the physical links for the selected version: the same version on both networks retains its links, whereas distinct staging/production versions can link isolated physical stores within one service. Demonstrate separate attestation identity/key material for each environment and the endpoint's 503 refusal when the observed staging flag contradicts the configured environment. A separate service is an isolation option, not a provider-imposed prerequisite (parent PA-IDENTITY). +- Change a declared public identity value without a new service version and verify the endpoint's loaded snapshot/renewal mismatch behavior separately; no claim of independent full-config appraisal is made. +- Exercise credential restrictions/revocation and audit-event coverage for relevant changes. Missing events or inaccessible resources must remain explicit gaps. +- Check structural/enrollment consistency of the signed public identity projection without reading the full app-config blob for that purpose, while treating `ts-publisher-key-` metadata entries and `ts-publisher-` secret entries as separate namespaced records. Do not publish their private linkage or infer code-exclusive access from a shared store. +- Retain sanitized observations and raw private evidence under restricted access. Clean up only resources the experiment owns; report uncertain outcomes without guessing or retrying mutations blindly. + +### Q4: method security review and disposition + +The report must contain source/API identity, provider acceptance/selection contract, exact join algorithm, hostile-operator threat argument, retained positive/negative evidence, release verification/eligibility path, host/version/config coverage, permissions, time/propagation limits, and unsupported states. Distinguish absence of an observed counterexample from a justified invariant. + +Two independent reviews are required before enabling a strong method: package/provider security review and runtime/operational integration review. Reviewers must assess alternate accepted archives and control-plane versus serving-state limitations, not just canonical equality. + +## 7. Gate outcomes and versioning + +| Outcome | Permitted publisher result | +| -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| Authenticated selected executable and all parent-profile checks established | `artifact_bound`; `official` only after eligible release-byte verification | +| Provider hash matches a locally reconstructed candidate, but selected module is not securely bound | `fingerprint_only`, null installed executable digest and official release; official profile indeterminate | +| Only publisher build report available | `declared`; no independent installed-artifact claim | +| Observation fails or races | `unavailable`, `fail`, or `indeterminate` as appropriate; no positive official renewal | + +An approved method gets a stable versioned identifier naming its provider evidence, semantics, and limits. Consumers allowlist that identifier. CLI/provider algorithm changes, archive parser changes, provider acceptance-rule changes, new package layouts, or broadened resource selectors require requalification; they cannot silently inherit the method's approval. + +The parent contract defines policy failure and expiry. This investigation must not weaken them to fit an available API. If only lower assurance is feasible, maintainers explicitly decide whether that narrower feature meets their objective; the original strong claim stays unresolved. + +## 8. Acceptance + +- **Q-AC1:** A reviewed evidence graph distinguishes official raw WASM, publisher archive, provider fingerprint, selected executable, active service/version, host, and the publisher-declared actual request-used identity snapshot. +- **Q-AC2:** The observed client hash algorithm and its ambiguity are documented with exact source and harmless fixtures. +- **Q-AC3:** Provider contract and observations justify the selected method, or an explicit lower-assurance disposition prevents official claims. +- **Q-AC4:** Hostile packages, parser/resource limits, permissions, staging, version/config changes, and failures have retained evidence at the applicable scope. +- **Q-AC5:** No unapproved live provider changes, invented readback capability, or inference from canonical success to universal safety is accepted. +- **Q-AC6:** The qualified path uses the compatible existing-store layout, with provider-valid initialization, collision checks, and no disabled-deployment resource/inventory regression. + +No controlled provider experiment has been executed as part of writing this specification. + +## Review log + +- **Round 1 (Codex, independent security and runtime review):** Checked package ambiguity and remote-provider invariants; no blocking companion issue found. +- **Round 2 (Codex, independent security and runtime review):** Confirmed the selected-executable qualification gate; no blocking companion issue found. +- **Round 3 (Codex, independent security and runtime review):** No blocking companion issue remained. +- **Follow-up correction pass (Codex, independent security and feasibility review):** Added existing-store provisioning qualification and Q-AC6; no blocking companion issue remained. Controlled provider experiments and strong-method qualification remain unexecuted. +- **Gap review (Claude Code, independent security and lifecycle review):** Added staging/environment consistency qualification; the correction below refines isolation to the selected version's physical resource links rather than a blanket separate-service requirement. Fingerprint-only results now carry `provider_package_fingerprint` coverage in the registry contract. +- **Isolation correction (Codex, source and contract review):** Refined staging qualification to version-specific physical links and distinct identity/key material; a separate service is an option rather than a provider-imposed prerequisite. No live provider experiment was performed. diff --git a/docs/superpowers/specs/2026-11-04-publisher-attestation-registry-design.md b/docs/superpowers/specs/2026-11-04-publisher-attestation-registry-design.md new file mode 100644 index 0000000000..e26f1bcd8b --- /dev/null +++ b/docs/superpowers/specs/2026-11-04-publisher-attestation-registry-design.md @@ -0,0 +1,485 @@ +# Publisher attestation: registry protocol and operations + +**Date:** 2026-11-04. **Status:** Proposed registry-side contract; no deployed registry or qualified Fastly method is claimed. + +**Parent:** [Publisher endpoint and common wire contract](2026-11-04-publisher-attestation-design.md). **Provider gate:** [Fastly executable binding](2026-11-04-publisher-attestation-fastly-qualification-design.md). + +This companion specifies what an independent verifier signs and what a consumer checks. Registry hosting, administration, signer recovery and governance belong to that registry's operator; they do not run inside the edge publisher binary. The repository supplies a bounded reference verifier and a separately planned stacked demo, not an automatically accredited multi-tenant public service. + +V1 defaults to expiry-only consumer acceptance. An assessment lives for at most 15 minutes from its oldest observation and no longer than applicable authorizations. Live status is optional and introduces availability, privacy and load costs. Known compromise/withdrawal is never ignored, but a consumer without a live update can accept until its assessment expires. Consumer trust roots are independently configured. + +```mermaid +sequenceDiagram + participant O as Publisher/operator + participant D as Publisher DNS + participant V as Independent verifier + participant P as Publisher endpoint + participant F as Fastly and release evidence + O->>V: Request scoped enrollment + V-->>O: Fresh DNS challenge + O->>D: Publish challenge response + V->>D: Verify host and optional operator proof + V->>P: Fresh nonce challenge over HTTPS + P-->>V: Signed declared identity and build facts + opt Qualified Fastly official-deployment policy + V->>F: Independently collect active version and executable binding + F-->>V: Authenticated observation and release evidence + end + V-->>O: Short-lived signed assessment +``` + +## Signed registry objects + +### PA-ASSESSMENT: verifier payload + +| Required field | Type and rule | +| --------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `schema` | `ts-publisher-assessment-v1` | +| `issuer` | Consumer-configured verifier identifier, canonical per parent PA-IDENTIFIER | +| `assessment_id`, `enrollment_id` | Verifier-assigned canonical UUIDs | +| `binding`, `binding_id` | Full endorsed descriptor and its digest | +| `evidence_digest` | SHA-256 of collected evidence compact JWS | +| `observed_from`, `observed_until` | Ordered observation interval covering all successful checks | +| `issued_at`, `expires_at` | Ordered times subject to PA-TIME | +| `policy` | Object: `id` string, `version` positive integer, `digest` of the verifier's retained policy document | +| `result` | `satisfied`, `not_satisfied`, or `indeterminate` under that policy | +| `checks` | Object described immediately below | +| `build` | Build appraisal object described immediately below | +| `reason_codes` | Unique sorted array of codes from the parent PA-FAILURE, empty allowed | +| `extensions` | Empty object | + +`checks` contains exactly `host_authorization`, `key_possession`, `host_service_binding`, `active_version`, and `configuration`. Each is an object with `outcome` (`pass`, `fail`, `unavailable`, `not_assessed`), `method` (nonempty profile identifier string or null), `observed_at` (integer or null), and `evidence_digest` (digest or null). A pass requires a non-null method, non-null observation time, and retained evidence digest. `not_assessed` requires all three other fields null; a fail/unavailable outcome names the attempted method and may have null observation/evidence when collection did not complete. An unavailable/not-assessed outcome MUST NOT satisfy a required check. + +Each check's `evidence_digest`, when not null, identifies what that check examined: + +| Check | `evidence_digest` value | Consumer use | +| ---------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------- | +| `key_possession` | Equal to the assessment's `evidence_digest` (SHA-256 of the evidence compact JWS) | Checkable: must match | +| `configuration` | Equal to `binding.configuration.digest` | Checkable: must match | +| `host_authorization` | SHA-256 of the verifier's retained authorization record for the current generation | Opaque; supports audit and dispute | +| `host_service_binding`, `active_version` | SHA-256 of the verifier's retained, sanitized provider observation | Opaque; supports audit and dispute | + +A passing `host_authorization` under `dns-key-control-v1` requires both exact site DNS/key enrollment and active PA-PUBLISHER account/site membership. `host_authorization.observed_at` is when the verifier rechecked both records during this collection, not when DNS control was last proved. The age of DNS proof is bounded by the 30-day publisher-record, site-enrollment and operator-authorization lifetimes, which also cap assessment expiry; version 1 gives consumers no field for a stricter DNS-freshness policy. + +`build` contains exactly `provenance` (`official`, `custom`, `unknown`), `assurance` (`artifact_bound`, `fingerprint_only`, `declared`), `method` (string), `artifact_sha256` (digest or null), `provider_files_hash` (128 lowercase hex or null), `release` (object or null), and `coverage`. `coverage` is determined by `assurance`: `artifact_bound` requires `selected_executable_only`, `fingerprint_only` requires `provider_package_fingerprint` (the provider's hash of the whole uploaded package, which does not single out the executable that runs), and `declared` requires `declaration_only`. A non-null `release` contains `repository`, `tag`, `source_commit`, `asset_name`, `artifact_sha256`, and `verification_evidence_digest`; it names authenticated Layer 1 evidence, not runtime strings. `repository` is a nonempty `owner/name` string matched to the configured Layer 1 source; `tag` is a nonempty tag string validated under that release policy; `source_commit` is 40 lowercase hex characters; `asset_name` is one basename without slash, backslash, or dot-segment components; both digest members are SHA-256 digest objects. None of these members may be null inside a non-null release. + +`provenance: official` requires `assurance: artifact_bound`, a non-null executable digest, and a verified eligible release with the same digest. `custom` may be independently observed or declared; the assurance field is mandatory and MUST be displayed alongside it. A publisher saying it customized code does not prove those bytes are installed. A custom classification requires the signed custom declaration or a retained custom-build appraisal; absence from the verifier's known release list alone means unknown, not custom. `fingerprint_only` MUST have null executable digest/release and MUST NOT be rendered as official. It records a provider fingerprint comparison, not a stronger claim. An artifact-bound custom build has a non-null independently bound digest and null official release. All other declared builds have null artifact digest/release and `coverage: declaration_only`. + +`fingerprint_only` requires a non-null `provider_files_hash`; without one the assessment is `malformed`. A non-null `provider_files_hash` requires `checks.host_service_binding.outcome: pass` and `checks.active_version.outcome: pass`, plus a Fastly binding with non-null service ID, version and staging flag. Retained provider evidence MUST attribute the hash to that exact host-authorized service/version/environment, observed within the assessment interval. A publisher-supplied hash or a hash from an unattributed package cannot satisfy this rule. These checks establish context, not selected-executable binding or official provenance. Consumers reject a violated cross-field invariant as `malformed`. + +Two built-in appraisal contracts are defined: + +| Policy ID | Version 1 acceptance condition | +| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `publisher-declaration-v1` | Authorized enrollment; fresh evidence; exact authorized key/binding; valid configuration identity projection; explicit build declaration. Host-service and installed-artifact checks may be not assessed; the result must remain visibly self-declared | +| `fastly-official-deployment-v1` | Qualified Fastly active production service/version and host binding; artifact-bound official eligible executable; authorized host/key and structural projection-consistency checks pass. Configuration remains publisher-declared as scoped in the [parent PA-SNAPSHOT](2026-11-04-publisher-attestation-design.md#pa-snapshot-commit-only-to-a-defined-public-projection) | + +Each built-in policy version has one canonical policy document, a closed JSON object with `schema: ts-publisher-policy-v1`. The assessment's `policy.digest` is the SHA-256 of the JCS form of that exact document, so every conforming verifier emits the same digest for the same built-in policy and consumers can pin `id`, `version` and `digest` together. A verifier MUST NOT change the semantics behind a built-in `id`/`version`; a change needs a new version and a new document. The normative [`publisher-declaration-v1` version 1 document](policies/publisher-declaration-v1.json) requires: + +| Check or field | Requirement for `result: satisfied` | Version 1 method identifier | +| ---------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| `host_authorization` | `pass` | `dns-key-control-v1` | +| `key_possession` | `pass` | `publisher-evidence-jws-v1` | +| `configuration` | `pass` | `public-identity-projection-v1` | +| `host_service_binding` | `not_assessed`, or `pass` with a method the consumer allowlists | Assigned when a provider observation method qualifies | +| `active_version` | `not_assessed`, or `pass` with a method the consumer allowlists | Assigned when a provider observation method qualifies | +| `build.assurance` | `declared`, or `fingerprint_only` with a method the consumer allowlists; never `artifact_bound` | `publisher-custom-declaration-v1` (custom) or `publisher-unspecified-declaration-v1` (unknown) | + +Under any built-in policy, a `fail` or `unavailable` outcome on any check means the result is not `satisfied`. `fastly-official-deployment-v1` has no canonical document yet: its provider methods are assigned only when the Fastly qualification passes, and no conforming verifier issues it before then. + +The declaration policy JCS SHA-256 digest is `18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3`. The [fictional official-shaped policy](policies/fictional-official-profile-v1.json) is a normative definition for synthetic cases only; it authorizes no real official method. Vectors copy these files, and regeneration reads them without redefining policy semantics. + +Policy documents are normative evaluation inputs, not digest labels. Consumer administrators obtain built-in documents from the normative policy files linked here and custom documents from the policy owner out of band, validate them and pin their exact `id`, `version` and JCS SHA-256 digest in independent local configuration before enabling that policy. Issuer metadata advertises descriptors only; it is not a policy retrieval or trust update channel. Missing or mismatched documents fail `policy_untrusted`; no automatic fetch from an assessment-selected URL is allowed. + +| Policy field | Normative evaluation | +| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `checks..requirement: pass` | The named check must pass. | +| `pass_or_not_assessed` | The check may pass or be not assessed; fail/unavailable never satisfies it. | +| `checks..methods: null` | A passing check must use a method in the consumer's independent allowlist. | +| `methods: [...]` | A passing check must satisfy both this list and the consumer allowlist. An empty list permits no passing method; a not-assessed check needs no method. | +| `build.assurance` | The assessment assurance must be a member of this list. | +| `declared_methods`, `fingerprint_methods`, `artifact_methods` | The list corresponding to the actual assurance restricts `build.method`, in addition to the independent consumer allowlist; null delegates to that allowlist, and an empty list permits none. | +| `max_assessment_lifetime_seconds` | Require `expires_at <= observed_from + limit`, in addition to PA-TIME and any stricter consumer bound. It is never measured from issuance. | + +Consumers evaluate the complete pinned document at stage 8; a local `required_checks` override cannot weaken it. Independent consumer configuration may impose stricter requirements. Method trust is checked at stage 5, while a `satisfied` result that contradicts the known document's required outcomes is `malformed` at stage 8. + +A verifier may add its own named policy, with retained policy digest, explicit check requirements, and consumer opt-in. A custom policy is not interchangeable with either built-in policy. No policy here certifies consent behavior, module safety, the truth of the full configuration, or outermost traffic placement. Additional profiles require their own reviewed specification, qualification, and negative tests before consumers enable them. + +### PA-STATUS: signed withdrawal and key status + +A verifier publishes a status object for a requested assessment. Required fields are `schema: ts-publisher-status-v1`, `issuer`, `enrollment_id`, `assessment_id`, `binding_id`, `publisher_key_kid`, `sequence`, `assessment_status`, `enrollment_status`, `publisher_key_status`, `reason_codes`, `issued_at`, `expires_at`, and empty `extensions`. + +1. Statuses are `current`/`withdrawn` for the assessment, `active`/`withdrawn` for enrollment, and `active`/`compromised`/`retired` for the publisher key. `sequence` is a positive integer per enrollment, durably persisted by the verifier. + +2. It advances only when the enrollment's recorded status state changes: an assessment or enrollment withdrawal, a publisher-key status change, or an authorization-generation change. + +3. A status reissued or reused while that state is unchanged carries the current sequence, so concurrently current assessments in one enrollment, such as both versions during a rolling deployment, share it. + +4. Withdrawal and compromise are terminal for their identifier. + +5. Re-enrollment uses new identifiers; an expired assessment is not extended by a status response. + +6. A current status means no withdrawal is recorded, not that a fresh provider observation occurred. + +7. Before each current-status issuance or reuse, recheck publisher authorization/site membership, any applicable separate operator authorization, enrollment, publisher key, assessment, and verifier signing-key validity at the current time. + +8. Its expiry is no later than the earliest of those authorization/assessment expiries and issued_at+60; a still-valid signature cannot extend an expired operator delegation. + +9. On publisher-record or operator-record expiry/withdrawal, stop current-status issuance and positive assessment publication, mark affected enrollment authorization withdrawn, and advance durable generation/sequence. + +10. Time passing across an expiry is a denial condition even if no event has yet updated the generation. + +11. A publisher key rotation is a key transition within the enrollment (PA-ENROLLMENT): it advances the authorization generation and the sequence, the new key becomes `active`, and the old key becomes `retired` once its last assessment expires. + +12. Superseding a service version does not withdraw its assessment: that assessment stays `current` until expiry and is not renewed (PA-TIME). + +13. Recording a different accepted binding advances the authorization generation and therefore the public sequence under PA-RECOVERY. + +14. An unexpired old-binding assessment can obtain a fresh `current` status at that new sequence; after seeing a higher sequence, consumers reject older statuses, not the still-authorized assessment merely because its binding is older. + +15. Status MUST satisfy `issued_at < expires_at <= issued_at + 60`. + +16. Consumers persist the highest seen sequence per verifier/enrollment, accept an equal sequence, and reject lower sequences. + +17. A lower sequence means the status predates a recorded state change, so a superseded `current` status for another assessment in the enrollment is correctly rejected. + +18. A fresh consumer may still accept an older signed status within its bounded lifetime; no instantaneous revocation is promised. + +19. Compromised-key status affects all assessments for that key in that enrollment; known cross-enrollment reuse triggers investigation and withdrawal of each affected enrollment. + +20. Status history and assessment expiry remain independent. + +## Enrollment and independent trust bootstrap + +### PA-PUBLISHER: authorize a publisher and its sites + +1. Before site enrollment, the verifier maintains a publisher authorization record binding an authenticated account, canonical owner-domain `publisher_id`, explicitly authorized site hosts, proof method/evidence digest, inclusive `not_before`, exclusive `not_after`, approver, and withdrawal state. One account's owner identifier cannot be claimed by another account without an authenticated recorded delegation or recovery. +2. The default `publisher-owner-dns-v1` method proves technical control at `_ts-publisher.`. The verifier creates a single-use ten-minute challenge binding its issuer, owner identifier, authenticated account reference, the exact proposed site set, random 32-byte nonce and issue/expiry times. Publish `ts-publisher-owner-v1=` at that name. The verifier checks the exact TXT and outstanding challenge, and retains its evidence. This private administrative challenge is not a signed public object; its concrete API encoding is provisional and must be frozen before P1b implementation. +3. The publisher record is valid for at most 30 days after proof. Site membership is explicit, including unrelated DNS domains; adding sites or changing the authenticated account requires fresh publisher authorization. The same verified record can authorize several site enrollments without repeating owner proof for each site. Each site still independently proves its own exact host/key through PA-ENROLLMENT. Owner-domain DNS alone never proves site control. +4. Before enrollment, renewal and positive publication, require an active record with exact publisher/account equality, the site in its authorized set and `not_before <= now < not_after`. Cap assessment expiry by this record as well as site/operator/key authority. Missing, foreign-account, unproven, out-of-scope, expired or withdrawn records fail `publisher_unauthorized`. +5. Withdrawal of a publisher record withdraws its affected site assessments and fences their collectors/signers through PA-RECOVERY. Removing one site affects that membership only. Account transfer/recovery requires independently authorized new proof; the old site key cannot authorize it. A consumer relies on the issuer's signed membership assertion, rather than reading private account records. +6. The owner-domain identifier permits comparison with ads.txt `OWNERDOMAIN` and seller-domain records, but equality alone does not validate those files, seller IDs, legal ownership or business relationships. Those checks remain independent consumer policy. +7. V1 requires a distinct key for each site enrollment, including sites of one publisher. Reuse is a provisioning error requiring investigation and withdrawal of every affected enrollment, rather than an authorized sharing mode. The narrower compromise radius costs one to four secrets per site and per-site rotation work; publisher-wide keys need a separately approved profile. + +### PA-ENROLLMENT: authorize before endorsing + +1. The reference verifier accepts enrollment only through an authenticated operator workflow, not an anonymous public URL submission that automatically grants trust. + +2. Required private enrollment input is the full authorized tuple from PA-IDENTITY, publisher contact/recovery authorization, public key and validity, and selected policy. + +3. Provider observations additionally require an existing Fastly service ID, intended host mapping, and narrowly scoped provider credentials. + +4. The declaration profile can be satisfied without provider credentials or release acquisition; when no provider observation is collected, provider checks are not assessed. + +5. An optional fingerprint observation requires independent provider credentials and the host/service/version checks above, without upgrading declaration acceptance to official assurance. + +6. A Fastly service ID remains part of the operator-declared enrolled tuple; Axum declarations are isolated development fixtures with null service identity; no production/staging Axum support is introduced. + +7. No signing private key is submitted to a verifier. + +8. The authenticated enrollment account MUST have a verifier-maintained authorization record permitting any non-null `operator_id` it submits. + +9. Null means publisher self-operation; the intersection of publisher-record and site host/key authorization supplies the finite authority interval and no separate operator identity is asserted. + +10. That record binds account identity, the exact canonical operator identifier, permitted publisher/deployment scope, authorization method, approver or delegating authority, evidence digest, validity interval, and withdrawal state. + +11. The default version 1 operator-control method, `operator-dns-control-v1`, is a fresh DNS proof at `_ts-operator.` with value `ts-operator-v1=`, using the same exact identifier, publisher/deployment/key scope, verifier issuer, nonce, and ten-minute deadline as the publisher challenge. + +12. The host is parsed from the authorized HTTPS operator identifier under PA-ROUTING; the verifier performs no arbitrary URL fetch. + +13. Operator DNS control grants technical authority for that identifier/scope, not a legal relationship. + +14. The optional path is descriptive: the DNS controller can authorize every path under the same operator host, so distinct paths do not establish independent operators. + +15. A documented preapproved association or delegation may be a separately named consumer-opted-in method; a self-submitted URL, publisher DNS proof, key possession, or an authenticated login alone does not establish that association. + +16. Do not fetch an arbitrary operator URL to manufacture proof. + +17. If the record is absent, expired, outside scope, or withdrawn, fail enrollment with `operator_unauthorized` before any positive assessment. + +18. Retain and recheck the authorization record on renewal, recovery, and assessment publication. + +19. A legitimate operator-identifier change, including switching between self-operation and a separate operator, requires new authorization/binding. + +20. An old publisher-key signature alone cannot authorize a third-party operator. + +21. Prefixed DNS owner names must fit DNS length limits; unrepresentable challenge names fail enrollment explicitly. + +The verifier MUST also establish DNS control for the exact host and proof of possession of the exact key, rather than treating either as sufficient alone. A parent publisher domain is descriptive; it does not authorize arbitrary subdomains. Every host is enrolled explicitly. Neither a wildcard nor a string suffix check grants enrollment. + +22. The verifier creates a single-use enrollment challenge: a closed JSON object with `schema: ts-publisher-enrollment-challenge-v1`, `issuer`, `host`, `publisher_id`, `operator_id`, `deployment_id`, `provider`, `service_id`, `environment`, `publisher_key_kid`, random 32-byte base64url `nonce`, `issued_at`, and `expires_at` with `issued_at < expires_at <= issued_at + 600`. + +23. The operator publishes `ts-publisher-v1=` as a TXT value at `_ts-attestation.`. + +24. The verifier retains the exact challenge and hash, checks that value before its deadline using a documented trusted resolver, and does not accept unrelated existing tokens. + +25. A TXT record split into DNS character strings is joined within that one record; separate records are never concatenated. + +26. The DNS check, any operator DNS check and the key-possession fetch that follows must all complete before the challenge's `expires_at`; otherwise the attempt fails and a new challenge is required. + +27. Verifiers SHOULD confirm the TXT value from more than one network vantage point to reduce exposure to localized DNS hijacking, and record which resolvers they used. + +28. The verifier then fetches fresh HTTPS publisher evidence and verifies it with the proposed key, matches the enrolled tuple, and corroborates provider host/service ownership and production version where the chosen policy requires them. + +29. Only after these steps may it issue an enrollment ID and assessment. + +30. DNS proves technical control under the resolver trust model, not legal ownership or that all requests use Trusted Server. + +31. DNSSEC validation status is retained; absence does not become a false DNSSEC claim. + +32. The operator may remove the enrollment TXT value after success. + +33. An explicit loopback development fixture substitutes a manually pinned host/key authorization for DNS and uses a separate test trust configuration; its environment is development, it cannot produce the production profile, and its objects MUST NOT be accepted by production trust configuration. + +34. Enrollment expires after at most 30 days and requires new DNS and key-possession challenges to renew. + +35. This is deliberate fresh-control proof, not a promise of zero DNS operations. + +36. Operator tooling SHOULD automate challenge publication, propagation checks, retry within the deadline, cleanup, and renewal scheduling using publisher-controlled DNS credentials; registries receive no DNS write credentials. + +37. For `operator-dns-control-v1`, operator authorization expires no later than 30 days after successful proof and no later than the publisher enrollment; a shorter registry policy may apply. + +38. Each site/deployment/key scope needs a fresh challenge at renewal. A publisher with 200 sites and two verifiers needs about 400 site DNS changes every 30 days, each within its ten-minute challenge window, plus owner-record renewal and any distinct operator proofs. V1 accepts this burden and requires operator tooling to automate it; no single owner proof replaces site control. + +39. Separately approved delegation methods must state their own finite maximum lifetime. + +40. Persistent per-site delegation to a verified publisher/issuer, with owner-level fresh renewal, is the named task-force alternative. Multi-site publishers are the driving case; delegation needs finite validity, removal, compromise and revocation rules before adoption. + +41. Propagation failure leaves the existing unexpired authorization unchanged and cannot silently extend it. + +42. A new key requires new proof and enrollment authorization; possession of an old key alone cannot authorize recovery. + +43. A routine rotation is a key transition within the existing enrollment: the verifier issues a fresh challenge naming the successor thumbprint, proves possession through `?kid=` before the successor is active (parent PA-KEYS), records the successor under a new authorization generation, advances the status sequence and issues the successor binding's assessment. + +44. Compromise recovery is different: it withdraws the enrollment and re-enrolls under a new `enrollment_id`. + +45. Production/staging, host, publisher/operator, deployment ID, or service changes require explicit enrollment review. + +46. A new service version within the enrolled service requires a new assessment/binding, not automatically new DNS proof. + +47. Suspected DNS/host ownership changes suspend renewal until reauthorization. + +### PA-ENROLLMENT-API: provisional operator-interface sketch + +The following is a candidate follow-up for a common operator interface, not a frozen v1 wire API or a P1a prerequisite. P1b uses the authenticated enrollment workflow and operator-supplied inputs defined by PA-ENROLLMENT and PA-REFERENCE. A future interoperable API needs exact field names and closed request/response schemas, identifier formats, authentication/authorization binding, errors, response/work limits, idempotency and credential-provisioning rules before a tool can claim compatibility across verifiers. The paths below are illustrative operations under a configured issuer base: + +| Operation | Request | Response | +| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `POST {issuer}/v1/enrollment-requests` | The PA-IDENTITY tuple, the publisher public JWK with `not_before`/`not_after`, and the requested policy `id`/`version` | `201` with `request_id`, the challenge object, and each TXT owner name and value to publish (publisher and, if any, operator) | +| `POST {issuer}/v1/enrollment-requests/{request_id}/verify` | Empty | `202`; the verifier checks DNS and key possession before the challenge expires | +| `GET {issuer}/v1/enrollment-requests/{request_id}` | None | `state` (`pending`, `verifying`, `enrolled`, `failed`), `enrollment_id` when enrolled, and PA-FAILURE `reason_codes` when failed | +| `POST {issuer}/v1/enrollments/{enrollment_id}/key-transitions` | The successor public JWK with validity | `201` with a new `request_id` and challenge naming the successor thumbprint; continue with `verify` and `GET` as above | +| `POST {issuer}/v1/enrollments/{enrollment_id}/reassessment-hints` | Optional `expected_binding_id` | `202`; rate-limited and coalesced per enrollment; an untrusted scheduling hint only | + +In this sketch, renewal would reuse `enrollment-requests` for the same tuple and return the existing `enrollment_id` on success. The later wire contract must make each request/response a closed JSON object and reject unknown members. No enrollment interface may accept publisher private keys, DNS credentials or provider write credentials. Nothing here authorizes fetching an untrusted issuer endpoint or sending it credentials. + +### PA-TRUST: discovery is not authority + +Consumer trust configuration supplies verifier ID, pinned public keys and validity, permitted policies/methods, acceptable releases, expected host/environment, observation age, and status requirements. The publisher's bounded `verifiers` discovery list is only an untrusted hint; a consumer queries only publication bases it configured for issuers it independently trusts. Unknown issuers, self-provided trust roots, unapproved policies, and additional untrusted endorsements cannot turn rejection into acceptance. + +The first consumer policy accepts one independently configured verifier; multiple-verifier support means evaluating each signed assessment independently. A consumer may require a threshold of distinct configured issuers whose accepted assessments match the same binding ID. Repeated keys/objects from one issuer count once. This is not evidence that organizations are operationally independent. There is no protocol-wide majority vote or mandatory registry. + +Trust-anchor rotation occurs through an authenticated consumer configuration update with an overlap period. A new root published solely in an untrusted assessment is not accepted. Verifiers retain historical public keys for audit verification. Compromise of a verifier key requires consumer trust withdrawal and reissuance through trusted recovery, not a status response signed only by that compromised key. + +Each verifier publishes an unsigned issuer metadata document at `GET {issuer}/v1/issuer` with `schema: ts-publisher-verifier-metadata-v1`, `issuer`, `publication_bases` (1–4 canonical HTTPS identifiers where its read surfaces can be fetched, normally including the issuer itself), `keys` (1–4 entries of `kid`, `jwk`, `not_before`, `not_after`), `policies` (up to 16 entries of `id`, `version`, `digest`), and `live_status` (boolean: whether it serves PA-STATUS). The document is a convenience for setting up consumer configuration and is untrusted until an administrator confirms it out of band and pins it; fetching it never changes trust on its own. Every verifier signing key has a finite, published validity; the maximum is a governance decision (parent task-force question 8). + +Consumer trust configuration maps each trusted issuer to one or more publication bases. A base may be the issuer's own service or an untrusted mirror or directory, such as a third-party registry that aggregates several verifiers. A mirror never adds trust: the consumer verifies every object it returns against the pinned keys of the object's `issuer`, exactly as if the issuer had served it. + +## Collection, freshness, and monitoring + +### PA-COLLECTION: deterministic independent assessment + +For each assessment, the verifier performs these steps under an operation ID and a total 60-second collection deadline. Provider/release steps apply only when required by the selected policy or explicitly requested as additional observations. When no additional provider observation is requested, the declaration profile skips provider/release steps and records those checks as `not_assessed`; it still validates enrollment, evidence, actual descriptor/projection consistency, clock, and key possession. Optional provider observations must satisfy their contextual evidence requirements and never upgrade declaration acceptance to official assurance. Production Fastly declarations require real DNS/TLS enrollment; Axum uses only the isolated development fixture exception: + +1. Load an active enrolled tuple, trusted publisher key, and appraisal policy; load provider credentials and authenticated release eligibility policy only when the selected collection requires them. Checking current enrollment state is a new observation; the retained original DNS proof has its own historical timestamp and 30-day authorization deadline and is not misrepresented as a new DNS lookup. +2. Read provider host/service mapping, production active version, package identity and required resource links without reading the app-config payload solely for the identity projection. Record retrieval timestamps and retained response digests privately. +3. Fetch fresh evidence with a new unpredictable nonce over authenticated HTTPS to the expected host. Verify signature, clock, enrolled key, host, environment, descriptor/projection hashes, and all tuple values. +4. Compare reported service/version with independently observed state. Never use a report's service ID as authorization to query another service. +5. Apply the qualified selected-executable binding method, if available. Verify retained official release bytes and eligibility; classify the outcome without upgrading a fingerprint match into artifact binding. +6. Repeat provider version/host observations and, when needed, a fresh evidence challenge to detect declared-identity changes. On a detected race, discard the collection and make at most one fresh attempt within a new bounded operation. Never combine old/new observations into one successful interval. +7. Calculate results under the named policy; sign an assessment referencing exact evidence and policy digests. Persist the object and evidence before advertising success. + +No write or deployment is necessary for collection. Missing credentials, 403/429, outage, incomplete audit pages, malformed provider responses, or unavailable release evidence produce unavailable/indeterminate results, not a successful downgraded official assessment. A verifier may separately issue a clearly named declaration assessment after its own required checks; that is a different result, not continuation of the failed policy. + +### PA-TIME: bounded acceptance + +Evidence lifetime is at most 60 seconds. Verifier assessment issuance MUST occur within 60 seconds after `observed_until`. An assessment MUST satisfy `observed_from <= observed_until <= issued_at < expires_at <= observed_from + 900`. Its expiry is also no later than publisher-authorization, publisher-key, enrollment, verifier-signing-key, or any applicable separate operator-authorization expiry; self-operation uses the finite publisher enrollment authority. The verifier computes that minimum at commit/publication, not just when collection begins. A publisher record or non-null separate operator claim with no independently established finite validity interval cannot authorize a positive assessment. Old observations cannot receive a new 15-minute lease by re-signing them. + +Consumers require `issued_at <= now + 30`, unexpired evidence/assessment (valid while `now < expires_at`) and any explicitly required status, and `observed_until <= issued_at`. Clock skew allowance applies only to future issuance. Pinned verifier keys and authorization records require exact inclusive not-before and exclusive expiry boundaries; skew never extends either authority. A consumer may require a shorter observation age or lifetime. All interval and cross-object relationships are validated even when signatures are correct. A failed platform time operation is endpoint 503 (the endpoint cannot detect arbitrary clock drift); verifier clock failure stops issuance; consumer clock failure stops current-assurance acceptance. + +The default verifier renews positive assessments every five minutes, subject to provider rate limits and a configured queue. For the monitored Fastly profile it polls provider version/host state at least every 60 seconds; a new signed projection is checked for consistency during renewal. V1 deliberately uses polling. Audit-log polling may trigger earlier rechecks only after event visibility/completeness is qualified; no push feed or complete audit history is assumed. If that monitoring interval cannot be maintained, stop representing the service as currently monitored and stop new positive official assessments until a complete collection succeeds. Previously issued assessments retain their explicitly bounded validity unless withdrawn. + +An ordinary deploy changes `service_version` in the binding. Fresh version-43 evidence cannot be combined with a version-42 assessment, even if that assessment has not expired. There is therefore an expected unendorsed interval after activation until a complete new-version assessment is published. How long it lasts depends on how the verifier learns of the new version: + +- **Monitored Fastly profile (provider polling):** up to the 60-second polling interval plus collection and publication time. +- **Declaration profile without provider observation:** the verifier learns of the new version only when fresh evidence reports it, so up to the 5-minute renewal interval plus collection and publication time, unless a reassessment hint (below) arrives sooner. +- **Either profile:** longer during outages or queueing. + +This is a loss of endorsement, not necessarily website availability. Rolling deployments may expose both versions; consumers require an assessment matching whichever version answered and MUST NOT fall back to an old-version assessment. The superseded version's assessment therefore stays published and `current` until its own expiry, so POPs still serving the old version remain endorsed during propagation; the verifier stops renewing it once the new version is the active one. Publisher tooling SHOULD request prompt reassessment after activation through the registry's authenticated enrollment-management interface; PA-ENROLLMENT-API sketches a possible later transport rather than requiring one in v1. Such a request is rate-limited and coalesced by enrollment, carries only the enrollment and an optional expected binding, and is an untrusted scheduling hint: it never supplies observation evidence, selects arbitrary fetch targets, bypasses the complete collection, or guarantees zero gap. Baseline renewal and polling remain sufficient for protocol conformance. + +Observed changes trigger reassessment, but only some justify withdrawal: + +- **Reassess, do not withdraw:** a new active service version, or a completed key transition. Each produces a new binding within the enrolled tuple; the old binding's assessment is still accurate for that binding, is not renewed, and lapses at its own expiry. +- **Stop positive issuance pending enrollment review:** a changed declared identity projection. Its five fields are the enrolled tuple, so a change (for example a new `deployment_id` or `environment`) is outside the enrollment and receives no positive assessment until the enrollment is reviewed; existing assessments lapse at expiry unless the change also indicates lost authorization. +- **Withdraw known affected assessments:** loss of the authorized host mapping, loss or expiry of publisher, enrolled or operator authorization, a provider observation contradicting the binding (`artifact_mismatch`, host or service mismatch), and key compromise. Withdrawal advances the status sequence. + +A confirmed key compromise also changes key status. API unavailability alone is not a declaration that the publisher key was compromised. Monitoring credentials being revoked prevents further positive collection. + +The v1 default consumer policy uses short assessment expiry alone and does not require a live registry status call. A previously obtained assessment and fresh publisher evidence may be checked without contacting the registry while their validity and independently configured trust policy hold. Registry unavailability prevents obtaining/renewing assessments but does not shorten an existing assessment to 60 seconds. Known withdrawal or compromise evidence is never ignored; without a live update the consumer may continue accepting until the existing assessment expires, at most the remaining bounded lifetime. + +Live-status checking is a separately configured stricter consumer mode, not a field selected by the publisher or an implicit v1 requirement. That mode requires a signed status no older than 60 seconds and denies acceptance on status failure. It introduces registry availability and query-observation dependencies: the registry may learn the checking client's network identity and publisher interest. The collector and consumer must document that choice and its privacy/load implications. Default expiry-only consumers cannot claim prompt revocation. Initial assessment discovery or renewal can still reveal a client's network identity and publisher interest; retaining a valid signed copy avoids a mandatory new registry query for each check, not all registry traffic. No per-bid registry lookup or auction/bid transport is specified here. + +## Consumer acceptance + +### PA-CONSUMER: acceptance algorithm + +A current publisher check MUST: + +1. Take expected host/environment, trusted issuers/keys, permitted policies/methods, and freshness/status settings from independent configuration. +2. Generate a fresh nonce and fetch publisher evidence. Strictly validate the JWS and schema, and verify the signature with the key in the evidence's own `binding.attestation_key`, whose thumbprint must equal the protected-header `kid`. That key is endorsed only through binding equality in step 4. Require the payload `nonce` to equal the generated nonce byte for byte (`nonce_mismatch`) and evidence times to satisfy PA-TIME (`stale`). Unsigned discovery may supply candidate bytes only. A valid evidence signature alone is self-declared, not independently trusted. +3. Verify each candidate assessment with an allowed verifier key and exact message type, then validate policy, result, intervals, enrollment status, build assurance, release eligibility, and required check outcomes. +4. Require full descriptor and binding-ID equality between fresh evidence and assessment, including host, deployment ID, service/version/environment, projection, key material, and validity. +5. Under the default expiry-only mode, enforce assessment expiry and any independently known withdrawal/compromise. Only if the consumer explicitly requires live status, fetch it from the independently configured verifier base and match issuer, enrollment, assessment, binding, key, sequence, and timing; a live-status failure denies that stricter mode. A publisher cannot disable the consumer's configured checks. +6. Apply the consumer's distinct-issuer threshold to matching accepted assessments. Return structured reasons and the accepted scope. No acceptable assessment means unendorsed, not a registry-verified success. + +Consumers evaluate in this order and report the reason code of the first stage that fails. Each stage applies to the fresh evidence before the candidate assessment, so the first failing stage across both objects is reported; they may also report later failures for diagnostics, but acceptance depends only on the first: + +1. Bounded decoding, duplicate-member rejection and limits (`malformed`). +2. Protected header and message type, and lookup of an active independently pinned verifier key (exact kid, JWK, and `not_before <= now < not_after`) for an assessment/status issuer (evidence uses its binding key with matching thumbprint, pending endorsement) (`signature_invalid`; an unknown issuer is `policy_untrusted`). +3. Strict Ed25519 signature verification (`signature_invalid`). +4. Payload schema and cross-field consistency, including digest equalities, the per-check digest rules above and the PA-TIME lifetime/interval relations of the signed object (`malformed`). First reject a Fastly staging/environment contradiction as `environment_mismatch`; an untrusted signer cannot bypass this binding invariant. Evidence whose lifetime falls outside its attestation key validity is `key_unauthorized`. An inactive pinned verifier key remains stage 2 `signature_invalid`. +5. Issuer, policy `id`/`version`/`digest` and method allowlists (`policy_untrusted`). +6. Time: future issuance beyond the skew allowance, expiry, observation age (`stale`, `clock_unavailable`); for fresh evidence, first require exact equality with the locally generated nonce (`nonce_mismatch`). +7. Exact equality with fresh evidence: host, environment, binding (`host_mismatch`, `environment_mismatch`, `binding_mismatch`). +8. `result` against the policy's required checks; a `satisfied` result contradicting them is `malformed`. A well-formed `not_satisfied` or `indeterminate` result is rejected with `policy_untrusted`, with its signed reason codes available as supplemental diagnostics. +9. Build assurance and release eligibility against the consumer's requirement (`artifact_binding_unproven`, `release_ineligible`). +10. Independently known withdrawal or compromise in every mode, then live status only when the consumer requires it (`withdrawn`, `key_compromised`, `key_unauthorized` for a retired publisher key, `status_unavailable`). +11. Distinct-issuer threshold (`policy_untrusted`). + +The check returns a consumer result, a local object that is not signed and not transmitted: `host`, `environment`, `accepted`, the accepted scope (policy, assurance, issuers and `binding_id`), `reason_codes`, `evaluated_at`, and `valid_until`. `valid_until` is the minimum deadline for the selected assessments that satisfy the distinct-issuer threshold: their `expires_at`, any independently configured trust-key validity limit, and any configured observation-age deadline derived from the original observation times. Only when live status is required, also include each required status's `expires_at` and `issued_at + configured_status_max_age` (at most 60 seconds). A shorter configured cache lifetime is measured from `evaluated_at`; reading a cached result never resets any deadline. Expiry-only mode does not acquire a live-status requirement or a 60-second status cache limit. A cached positive result MUST NOT be used at or after `valid_until`; it must also be invalidated on newly known withdrawal/compromise or a relevant change to the consumer's trust, policy, threshold or status settings. In live-status mode, cache state retains each supporting status's issuer, enrollment and sequence; learning a higher sequence for that issuer/enrollment invalidates a result supported by an older status until fresh matching status is checked. Cache entries are scoped to that consumer configuration, and say that the host answered a fresh challenge only as of `evaluated_at`, not at every later cache read. + +Publisher-authorization, operator-authorization, durable-state and signer-fencing conditions are verifier issuance preconditions, not consumer inputs. A consumer cannot observe them; it relies on assessment and status expiry being capped by those authorities (PA-TIME). The public vectors mark each case as a consumer-acceptance or a verifier-issuance case accordingly. + +The assessment-only operation is explicitly historical/bounded-observation verification. Without fresh publisher evidence it cannot claim that the host responded now. Conversely, a fresh publisher response cannot refresh an old assessment. Consumers MUST NOT accept `result: satisfied` without evaluating the policy and concrete required fields. A known policy with `result: satisfied` and a failed/unavailable/not-assessed required check is internally contradictory and rejected as `malformed`; `policy_untrusted` also covers an honest unsuccessful policy result; malformed is reserved for contradictory or structurally invalid claims. + +### PA-REFERENCE: tools and registry boundary + +The delivery includes a host-target reference verifier/collector and a pure verification library, with machine-readable results and offline historical verification. Planning must select its concrete crate/CLI placement without making runtime core depend on host HTTP, GitHub CLI subprocesses, or credentials. Use a separate host-only verifier binary/crate sharing the pure verification library. Publisher provisioning belongs to a distinct attestation command namespace, not request-signing `ts keys`. Placement alone establishes no independence: a publisher-run verifier produces a self-assessment, and consumers MUST NOT configure it as independent merely because it runs this reference software. Independent trust requires an out-of-band decision about who operates the signing key. Names of future commands are not frozen by this spec. + +The reference collector consumes operator-supplied enrollment and, only for provider-backed profiles, read-only provider credentials, retains evidence, and produces signed assessments/status. Its local state writes are atomic and restrictive. A production operator owns scheduling, authenticated administration, durable sequence allocation, backup, serving, abuse controls, and signer isolation. Advance the status sequence and persist the changed status state atomically before signing or publishing a status that reflects the change, following PA-RECOVERY below. A failed publication must not undo recorded withdrawal. After restart, never reset a sequence; if durable monotonic state cannot be recovered, stop current issuance and use authenticated recovery with a new enrollment identifier rather than risk rollback. A deployment of this reference component is not automatically an endorsed public registry. A full multi-tenant hosted registry and UI require a separate operational design. The named demo-registry follow-up below is a smaller illustrative deployment, not a production accreditation service. + +### PA-RECOVERY: fence stale collectors and restored state + +All registry modes, including expiry-only, must prevent new issuance from known withdrawn or stale restored authorization. The following production recovery design is registry-side qualification work. Each enrollment has an authoritative authorization generation in addition to its public status sequence. Changing publisher membership/authorization, operator authorization, enrollment/key status, accepted binding, or withdrawal increments that generation in a serialized durable transaction. A collector captures the generation when it starts and, immediately before committing a positive assessment, rechecks the generation, current authorization/key/enrollment validity, expiry deadlines, and policy. Commit uses compare-and-swap against that generation; an intervening withdrawal/change discards the candidate. A generation change fences in-flight work; it does not by itself withdraw previously issued assessments. The enrollment retains the authorized key/binding records needed to serve current status for an unexpired superseded assessment, unless those records are independently withdrawn, expired or compromised. Recording a different accepted binding advances both generation and public status sequence, while renewing the same binding does not; old assessments can receive fresh statuses carrying the current sequence without renewing their assessment lifetime. Signing/outbox publication is likewise gated on the current generation, so a pre-withdrawal job cannot publish a new positive assessment or `current` status afterward. Already delivered objects retain only the bounded exposure explicitly described in PA-TIME. + +Local persistence alone does not prove a restored backup is current. A production registry offering the monitored/live-status contract MUST have a trusted recovery checkpoint outside the rollback domain of its ordinary backups. That checkpoint binds enrollment ID, highest allocated sequence, authorization generation, a digest of the authoritative authorization/withdrawal state, and a current signer fencing epoch. It is updated monotonically and acknowledged before any affected object is signed/released. A checkpoint digest covers authorization state rather than secrets or raw credential responses. A database transaction/outbox and checkpoint protocol must treat a partially acknowledged transition as pending and fail closed; it cannot sign using a guessed earlier state. A deployment unable to supply and qualify this checkpoint mechanism cannot offer the monitored production profile. + +Every restart or restore verifies local state against the trusted checkpoint and obtains the current exclusive signer epoch. A lower sequence/generation, inconsistent state digest, unknown checkpoint, stale epoch, or checkpoint outage fences signing, positive collection commits, and current-status publication with `state_recovery_required`. A restored process does not automatically overwrite the checkpoint or reset sequence state. Authorized recovery restores the latest corroborated state or withdraws the affected enrollment through trusted administration and re-enrolls with a new identifier; an old backup/key cannot authorize its own clean replacement. The checkpoint/signing owner must ensure a fenced old worker cannot continue publishing through another outbox or signer instance. Retained historical verification remains available without a current-assurance claim. + +Qualification must exercise restore of an internally valid backup that predates a withdrawal, crashes on both sides of checkpoint acknowledgment, duplicate/fenced signer instances, and a delayed collector completing after compromise. This registry-side contract specifies the required invariant; the concrete transaction/checkpoint backend and its failure proof belong to P3's bounded operational plan. The edge endpoint and local protocol fixtures do not implement that production backend. No distributed-store atomicity or safe backup restoration is assumed merely because local files use atomic rename. + +### PA-PUBLICATION: verifier read surfaces + +A publisher-level site-list read surface (for example `/v1/publishers/{publisher}/sites`) is a named follow-up requiring bounded pagination and withdrawal semantics. Current consumers still discover and independently verify exact-host assessments; they cannot treat an unsigned owner grouping as endorsement. Multi-site and foreign-account squatting fixtures cover the PA-PUBLISHER issuance rule. + +A minimal verifier publication service exposes these read-only paths under an independently configured HTTPS base: + +| GET path | Response | +| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | +| `/v1/hosts/{canonical-host}/assessments?environment={environment}` | JSON with exactly `schema: ts-publisher-assessment-list-v1`, `host`, `environment`, and `assessments` (at most 16 compact JWS strings) | +| `/v1/assessments/{assessment_id}` | Exact assessment compact JWS, `application/jose` | +| `/v1/assessments/{assessment_id}/status` | Signed status compact JWS, `application/jose` | + +The host lookup makes assessment discovery possible without putting changing assessment IDs in publisher evidence. Its list is only a transport hint; each signed object is independently checked against the fresh binding and configured issuer. The list includes the newest unexpired assessment for each `(issuer, binding_id, policy)` combination, so a mirror aggregating several verifiers keeps each issuer's assessment separate; it suppresses withdrawn entries. Optional query parameters `binding_id`, `policy` and `issuer` (each at most once, canonical values only) narrow the list; a consumer normally passes the `binding_id` from the fresh evidence it just verified. If more than 16 entries or more than the byte bound still match, the server returns 409 and the consumer retries with narrower parameters. Each compact JWS list item is explicitly permitted up to 32,768 ASCII bytes, overriding the ordinary claim-string limit; the entire decoded response including JSON framing is capped at 524,288 bytes. The server never silently omits applicable assessments. Unknown IDs/hosts return 404, unavailable status returns 503, and no public route exposes admin operations. Error bodies use the parent's closed `ts-publisher-error-v1` shape with codes `invalid_request` (400), `not_found` (404), `conflict` (409) and `unavailable` (503). + +The status of an assessment that has expired returns 404 with error `not_found`; a status response is never `current` for an expired assessment, and a superseded but unexpired assessment keeps returning its real status. + +Caching differs by response. A response from `/v1/assessments/{assessment_id}` is an immutable signed object and MAY carry `Cache-Control: public, immutable, max-age=` with `n` no greater than the seconds remaining until its `expires_at`, so shared caches can absorb load; consumers still evaluate expiry themselves. List, status and key-history responses use `Cache-Control: no-store`. No redirect or unsigned success status is accepted. Consumers may instead receive explicit assessment files/IDs from independent input. The reference consumer's provider abstraction supports lookup, assessment, and status retrieval; it cannot assume a publisher-advertised endpoint is trusted. Enrollment, withdrawal, trust updates, and signer operations require authenticated administration and auditable authorization. + +### PA-HOSTING: listing in an existing registry + +An existing registry, for example an industry directory that already lists agents or other services, can host publisher attestation in either role: + +- **As a mirror or directory:** it lists hosts and serves the PA-PUBLICATION read surfaces for verifiers it aggregates. It is untrusted; consumers verify each object against the issuing verifier's pinned keys. +- **As a verifier:** it also performs enrollment and signs assessments under its own issuer identifier and keys, and is then one verifier among possibly several, not a mandatory central authority. + +Either way, these rules keep the registry's presentation honest: + +1. Any additional transport, such as a registry's own REST API or MCP tool, MUST return the exact compact JWS bytes of each signed object. A re-encoded or summarized rendering is never a substitute. +2. A listing that shows verification marks MUST map them to the assessment's policy, `build.assurance`, issuer and `expires_at`. A bare check mark that hides whether a result is declared, fingerprint-only or official, or which verifier issued it, is not conforming. +3. A listing stops showing a host as assessed once its newest assessment has expired or been withdrawn, using the same rules as consumers. +4. The registry's own entry model (categories, descriptions, logos) is presentation only and never enters a signed object. + +## Deliberate storage and access decision + +Assessments are stored and served by their issuing registry, not written into publisher config/secret stores. This deliberately avoids granting verifiers provider write/deploy permission and avoids making renewal a publisher-store write transaction. Publisher discovery lists only untrusted issuer identifiers; actual trust comes from consumer configuration. A registry must not gain write access merely to make its endorsement appear on the publisher endpoint. + +A Fastly read token is not a five-field disclosure channel. Depending on role/scope/resource permissions it can expose broader service and account configuration, including commercially sensitive settings. Service restrictions on `global:read` do not isolate non-service endpoints. Plaintext secret-store values are separately protected; do not claim that every secret is readable. The registry must inventory and disclose actual readable endpoints/resources and retention before enrollment. Removing the v1 full-config projection read reduces unnecessary collection, but does not narrow the provider credential's inherent access. + +## Governance, withdrawal and publisher exit + +Each verifier publishes its issuer/key identities, policy versions, assessment scope, suspension/withdrawal criteria, evidence-retention policy, administrative authorization roles, contact/appeal process, and consumer-mode availability expectations. No protocol participant gains authority over another verifier's assessments or the publisher's website. + +- Authorized publisher administrators may request exit for their own enrolled tuple through the registry's authenticated administration channel. Verify an enrolled credential or approved recovery/fresh DNS proof, record the request, stop renewal immediately, and withdraw that registry's enrollment/assessments. A compromised publisher key alone does not authorize a clean replacement identity. +- The verifier's explicitly authorized incident/policy administrators may stop issuance or withdraw their own assessments for confirmed mismatches, lost authorization, compromise, or policy breach. Keep reason/evidence and timestamps; distinguish unavailable observations from misconduct or key compromise. Technical emergencies may be acted on immediately, with notice and appeal afterward. +- Give the publisher an explanation and a documented review/correction route. Sensitive provider evidence is not automatically public. An appeal cannot extend expired objects or turn a failed check into success. Corrected deployments receive new assessments; terminally withdrawn identifiers are not resurrected. +- Under expiry-only consumption, withdrawal/exit stops new issuance but previously delivered assessments can be accepted until their bounded expiry. Optional live-status consumers can learn a withdrawal sooner, subject to their stated freshness/network limits. DNS token cleanup or persistent-record deletion is not immediate revocation: DNS caches/serve-stale, polling, and outstanding assessments matter. +- Withdrawal affects only that verifier's signed endorsement. It is not a global kill switch, does not disable site traffic, and does not revoke another independent verifier's opinion. Consumers retain their own trust/acceptance decisions. + +The task force and participating organizations must approve the governance and any registry service agreements before public production operation. This specification does not assign legal liability, create indemnities, or certify regulatory compliance; those questions remain explicit agreement/approval items rather than consequences of a valid signature. + +## Public publisher-key history + +The registry provides `GET /v1/hosts/{canonical-host}/key-history?environment={environment}` with an optional opaque `cursor` of 1–256 printable ASCII bytes. The JSON response is exactly `schema: ts-publisher-key-history-v1`, `host`, `environment`, `entries`, and `next_cursor` (null or a bounded printable-ASCII opaque string). Entries are closed objects containing an assessment compact JWS (at most 32 KiB) and an optional status compact JWS (at most 8 KiB, or null). Limit pages to 16 entries and 512 KiB including framing; page earlier when the byte bound is reached. Cursor values never name URLs. Unknown host/environment/cursor returns 404/400 as appropriate; responses use no-store. + +History is a key log, not an assessment log: it holds one entry per `(issuer, policy.id, policy.version, policy.digest, publisher key kid, binding_id)` combination, carrying the most recent assessment issued for that combination and its latest status. Mirrors preserve each issuer/policy separately; an assessment from one verifier never replaces another verifier's evidence merely because the publisher key and binding match, so renewals every five minutes do not grow it. It retains entries for the documented retention window (90 days by default) and is ordered by the entry's assessment issuance time plus assessment ID. The list itself is an untrusted transport hint; independently verify every object's issuer, message type, signature and exact host. Expired/withdrawn objects are historical evidence only and cannot authorize current publisher keys, extend expiry, bootstrap a new verifier trust root, or prove completeness across other registries. A null/expired historical status does not establish current revocation state. Verifier signing-key history is a separate issuer trust-management responsibility; archived public keys never become trusted automatically. + +## Stacked demo-registry follow-up + +P4 is a named follow-up stacked on the approved publisher/reference-verifier protocol. It demonstrates enrollment, declared/custom/fingerprint/official labels, assessment lookup, optional status, publisher-key history, and a consumer verification walkthrough. Its initial inputs are fictional/local fixtures; an official-shaped fixture is marked synthetic and cannot claim a qualified live Fastly deployment. The demo's UI/hosting plan and data sources require separate review. It is not an acceptance dependency for the edge endpoint and does not authorize deploying an operational public registry. + +```mermaid +sequenceDiagram + participant C as Consumer + participant P as Publisher endpoint + participant R as Trusted registry or retained assessment + C->>P: New random nonce + P-->>C: Fresh signed binding + C->>R: Obtain assessment, or use retained unexpired copy + R-->>C: Signed assessment + C->>C: Check trust, exact binding, policy and expiry + opt Consumer explicitly selects live status + C->>R: Fetch recent signed status + R-->>C: Status or unavailable + C->>C: Deny this stricter mode on unavailable/withdrawn status + end +``` + +## Registry verification matrix + +| Family | Required cases | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Enrollment/authorization | Owner-domain publisher proof; two unrelated sites of one publisher; foreign-account identifier squatting; missing/expired/withdrawn publisher record and site membership; assessment expiry capped by publisher authority; DNS reuse/expiry; key possession; unauthorized operator; self-operation; per-scope operator renewal and lifetime cap; recovery without old-key authority; canonical host across enrollment and index lookup | +| Collection/network | Qualified provider/release positive and negative cases; wrong service/version/staging; non-null hash without successful host/version checks; redirects/SNI mismatch; DNS rebinding; nonpublic addresses; proxy/token leakage; response bounds/deadlines/retries/429; no report-selected resource lookup | +| Assessment semantics | Exact binding; unsupported policy/method; canonical built-in policy digest; satisfied with failed/missing required check is malformed; per-check digest equalities; coverage determined by assurance; reversed intervals; oldest-observation expiry; full-size embedded JWS; declared/fingerprint/artifact-bound distinctions; official-shaped unqualified rejection | +| Consumer | Multiple issuers and duplicates; mirror-served objects verified against issuer keys; first-failure reason precedence; `valid_until` bounded by selected assessment/trust-key/observation deadlines and required live-status expiry/freshness; expiry-only cache unaffected by optional status; cache invalidated by known withdrawal or changed consumer policy; exact fresh-evidence binding match; compromised/withdrawn keys and enrollment; expiry-only outage tolerance; optional-live outage denial; sequence rollback; equal-sequence statuses for concurrently current assessments; superseded-version assessment still accepted for old-version evidence until expiry with a fresh status at the advanced sequence; rolling-version mismatch | +| Rotation and lifecycle | Staged-key proof via `kid` before activation; key transition within the enrollment with the old key retired; challenge expiry covering DNS and possession steps; status 404 for expired assessments; key-history deduplication per key and binding | +| Hosting | Additional transports return exact JWS bytes; listing marks map to policy, assurance, issuer and expiry; list filtering by `binding_id`, `policy` and `issuer`; cache headers per response type | +| Operations/recovery | Poll/renewal outage; reassessment hint abuse/coalescing; publication failure; retained-state restart; stale checkpoint/backup mismatch; collector/signing race with withdrawal; operator expiry between collection and commit; assessment/status cap at authority expiry; current-status denial after expiry before any timer update | + +These tests qualify registry/reference-consumer behavior separately from the publisher runtime. Provider executable binding still requires the companion's independent qualification evidence. + +## Review log + +- **Split-contract review (Codex, independent security and contract review):** Checked consumer defaults, operator proof, governance, history and trust. Corrected the shared configuration-method identifier; no blocking contract issue remained after correction. No registry deployment or provider qualification was performed. +- **Cleanup review (Codex, independent contract review):** Checked contextual fingerprint evidence, deployment gaps, operator lifetime, rejection semantics and test ownership; corrected declaration-profile wording to permit optional provider observations. +- **Cleanup follow-up (Codex, independent contract review):** No remaining blocking consistency findings. Structural and signature fixtures pass; runtime and provider qualification remain unexecuted. +- **Final detailed review (Codex, independent security and protocol reviewers):** No blocking findings. Clarified that declaration collection omits provider checks only when no optional observation is requested; contextual fingerprints retain their narrower assurance. +- **Gap review (Claude Code, independent security, consistency, implementability, adoption and lifecycle review):** Superseded-version assessments now stay valid until expiry instead of being withdrawn; built-in policies have a normative check table, method identifiers and a canonical policy document whose digest consumers pin; per-check digests and coverage follow defined rules (`provider_package_fingerprint` for fingerprint-only); key rotation is a transition within the enrollment with pre-activation proof; consumers follow a fixed evaluation order and produce a result with `valid_until`; verifier-only conditions are no longer consumer inputs. Added a provisional enrollment-API sketch, issuer metadata, mirror bases, list filters and caching, expired-status behavior, key-history deduplication and hosting rules for existing registries. Vectors were regenerated with new ephemeral keys and checked with two independent Ed25519 implementations. DNS-proof age stays bounded by enrollment lifetime rather than exposed; an issuer-allowlist record is left to the task force. +- **Self-review (Claude Code, independent contract review):** No blocking findings. Per-issuance status sequences could make a consumer reject a valid reused status for one of two concurrently current assessments during a rolling deployment; the sequence now advances only on recorded status-state changes, and the consumer matrix covers equal sequences. +- **Correction review (Codex, independent contract and security reviewers):** Preserved issuance fencing while allowing refreshed current-sequence status for superseded assessments; capped cached acceptance by all applicable deadlines, preserved issuer/policy history, and marked the incomplete operator API as provisional. +- **Correction follow-up (Codex, both independent reviewers):** No remaining blocking design/fixture findings after checking cache invalidation and boundary cases. Production registry operation and provider qualification remain unexecuted. + +- **Maintainer-feedback correction (Codex):** Defined pinned policy-document evaluation, explicit consumer nonce/key checks and verifier independence; aligned development-only Axum and split P1a/P1b. Runtime and operated-verifier qualification remain outstanding. + +- **Round-2 correction (Codex, internal author check):** Added owner-domain publisher authority with explicit account/site membership, per-site key costs and expiry caps. Corrected pinned-key, signed-status and first-failure fixture checks; extracted normative policies. The author check identified missing authority-bound admission, corrected with four regressions. This is specification/fixture evidence, not production qualification or maintainer approval. diff --git a/docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json b/docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json new file mode 100644 index 0000000000..a7a2d69659 --- /dev/null +++ b/docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json @@ -0,0 +1,3633 @@ +{ + "description": "Public synthetic interoperability fixtures, not runtime implementation or provider qualification. Deterministic public TEST-ONLY seeds in scripts/publisher-attestation/generate.mjs must never protect real data. Decoded payloads, unsigned documents and cases are declarative generator inputs; derived keys, hashes, signed segments and transports are regenerated. Base positive is a fictional production Fastly declaration with provider checks not_assessed. Axum is development-only. No production provider method is qualified.", + "encoding": "JWS signing input is ASCII protected_segment + dot + payload_segment. Verify those exact segments. JSON bytes use sorted keys and compact ASCII-compatible serialization; fixture values use the RFC 8785-compatible integer/ASCII subset. Digests of signed objects hash exact compact ASCII bytes.", + "validation_order": [ + "1 bounded strict JSON/JWS decode with duplicate rejection", + "2 protected header, expected message type and trusted issuer key", + "3 strict Ed25519 signature", + "4 payload schema and cross-field consistency, including per-check digest rules, message lifetimes, assessment observation intervals and the evidence key validity window", + "5 issuer, policy id/version/digest and method allowlists", + "6 exact evidence nonce comparison, then issuance skew, exclusive expiry and observation age", + "7 exact host, environment and binding equality with fresh evidence", + "8 result against the policy's required checks", + "9 build assurance and release eligibility", + "10 independently known withdrawal/compromise, then live status when required", + "11 distinct-issuer threshold", + "Verifier-issuance cases (operator authorization, durable state, signer fencing) are preconditions checked by the verifier, not consumer inputs." + ], + "public_keys": { + "publisher": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + } + }, + "verifier": { + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "Kay64UG8yvCyLhqU000LxzYeUm0L_hLIl5S8kyKWbdc" + } + }, + "publisher_site": { + "kid": "WeVHjIQTOLKu-P74oC-WL-MNn_sLM5CvrzuMjDFC3-s", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "JUO5L_EJVRFHatyDadtt3JM2ZaEZeN2hQE7hBmypVZ0" + } + } + }, + "external_context": { + "now": 1793793600, + "expected_host": "news.example.com", + "expected_environment": "production", + "nonce": "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8", + "trusted_verifier": { + "issuer": "https://verifier.example.com/publisher-attestation", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "not_before": 1793790000, + "not_after": 1793880000, + "allowed_policies": ["publisher-declaration-v1"], + "allowed_methods": [ + "dns-key-control-v1", + "publisher-evidence-jws-v1", + "public-identity-projection-v1", + "publisher-custom-declaration-v1" + ], + "pinned_policy_digests": { + "publisher-declaration-v1": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + }, + "policy_documents": ["policy_publisher_declaration_v1"], + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "Kay64UG8yvCyLhqU000LxzYeUm0L_hLIl5S8kyKWbdc" + } + }, + "require_status": false, + "highest_status_sequence": 1, + "maximum_observation_age_seconds": 900, + "production_provider_qualification": false, + "operator_authorization": { + "operator_id": "https://operator.example.com/publisher-attestation", + "authenticated_account": "fixture-publisher-account", + "authorization_record_active": true, + "source": "Synthetic independent exact operator DNS/account proof record; never inferred from publisher host DNS", + "not_before": 1793790000, + "not_after": 1793880000, + "operator_dns_account_proof_verified": true, + "publisher_id": "owner.example.com", + "deployment_id": "22222222-2222-4222-8222-222222222222" + }, + "durable_authorization_history_available": true, + "signer_fence_current": true, + "status_available": true, + "test_context": "Isolated synthetic protocol acceptance only; no real enrollment, operator proof, provider qualification or production authorization is claimed.", + "fresh_evidence": "evidence", + "configured_status_max_age": 60, + "maximum_cache_age_seconds": null, + "trusted_key_accept_until": null, + "cache_action": "evaluate", + "cached_consumer_result": null, + "status_object": "status", + "target_assessment": "assessment", + "distinct_issuer_threshold": 1, + "known_withdrawal": false, + "known_key_compromise": false, + "authenticated_account": "fixture-publisher-account", + "publisher_authorization": { + "publisher_id": "owner.example.com", + "authenticated_account": "fixture-publisher-account", + "authorized_sites": ["news.example.com", "sport.example.net"], + "proof_method": "publisher-owner-dns-v1", + "proof_verified": true, + "authorization_record_active": true, + "not_before": 1793790000, + "not_after": 1793880000 + } + }, + "signed_objects": { + "evidence": { + "protected": { + "alg": "Ed25519", + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "typ": "ts-publisher-evidence+jws" + }, + "payload": { + "schema": "ts-publisher-evidence-v1", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "nonce": "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8", + "issued_at": 1793793595, + "expires_at": 1793793655, + "build_report": { + "declared_class": "custom", + "source_commit": null, + "package_version": null, + "target": null, + "variant": null + }, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ", + "payload_segment": "eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9", + "signature_segment": "N3jumn0wUj1CHZ0dQJdbpgrCp6NGM5086cp9aw29wb-9lImChLBtY2ifuKlphRy8OpzkBjqdAEh5lrtmDvR0BQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.N3jumn0wUj1CHZ0dQJdbpgrCp6NGM5086cp9aw29wb-9lImChLBtY2ifuKlphRy8OpzkBjqdAEh5lrtmDvR0BQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "4269e20bd09c1d416103f43f42deed6f53556b589cfab7e1e3dfa5fdb9d42175" + } + }, + "assessment": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-assessment+jws" + }, + "payload": { + "schema": "ts-publisher-assessment-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + }, + "observed_from": 1793793590, + "observed_until": 1793793598, + "issued_at": 1793793599, + "expires_at": 1793794200, + "policy": { + "id": "publisher-declaration-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + } + }, + "result": "satisfied", + "checks": { + "host_authorization": { + "outcome": "pass", + "method": "dns-key-control-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "cc4c418d4b84877cdaa3817d13875daf9adff15c038d1f9ee2fbfd4e5d9f1cb5" + } + }, + "key_possession": { + "outcome": "pass", + "method": "publisher-evidence-jws-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + } + }, + "host_service_binding": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "active_version": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "configuration": { + "outcome": "pass", + "method": "public-identity-projection-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + } + } + }, + "build": { + "provenance": "custom", + "assurance": "declared", + "method": "publisher-custom-declaration-v1", + "artifact_sha256": null, + "provider_files_hash": null, + "release": null, + "coverage": "declaration_only" + }, + "reason_codes": [], + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9", + "signature_segment": "aMOuwvxoym69MsKKdF5MnrIP9Ag-AhLzAkUaMmA389PEcGDQBO9uBR-byjjOMnFzaid3ka7d66KD2gsH_5Z6Bg", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.aMOuwvxoym69MsKKdF5MnrIP9Ag-AhLzAkUaMmA389PEcGDQBO9uBR-byjjOMnFzaid3ka7d66KD2gsH_5Z6Bg", + "compact_sha256": { + "algorithm": "sha256", + "value": "ef6915ceae1a6811afa61a18a721bc4e90a6197a00cade8ac98afc4372a1845c" + } + }, + "status": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 1, + "assessment_status": "current", + "enrollment_status": "active", + "publisher_key_status": "active", + "reason_codes": [], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ", + "signature_segment": "-JyNTGmBNu1GgVo-QmKrS39bwT6z3lwU9WKRwdLiOrgLTdgZpK2ltOeieEOm2cNgQKi5Du0VHj5slZbqsWieCw", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ.-JyNTGmBNu1GgVo-QmKrS39bwT6z3lwU9WKRwdLiOrgLTdgZpK2ltOeieEOm2cNgQKi5Du0VHj5slZbqsWieCw", + "compact_sha256": { + "algorithm": "sha256", + "value": "8f8aa56f66f8e1f3bf6e54dc81d64099c4ae5cabc608f9eb40d2bf90dbb58026" + } + }, + "self_operated_evidence": { + "protected": { + "alg": "Ed25519", + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "typ": "ts-publisher-evidence+jws" + }, + "payload": { + "schema": "ts-publisher-evidence-v1", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": null, + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "c8c9d9098fac84e8a110a48c2a2271ccc2f1de44d5fa8ed436989cf10d99a661" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": null, + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "930a832853ff36acfaf8dfc969b2f942cc426a3a70b3a72f18389151d33c8c04", + "nonce": "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8", + "issued_at": 1793793595, + "expires_at": 1793793655, + "build_report": { + "declared_class": "custom", + "source_commit": null, + "package_version": null, + "target": null, + "variant": null + }, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ", + "payload_segment": "eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImM4YzlkOTA5OGZhYzg0ZThhMTEwYTQ4YzJhMjI3MWNjYzJmMWRlNDRkNWZhOGVkNDM2OTg5Y2YxMGQ5OWE2NjEifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6bnVsbCwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6bnVsbCwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjkzMGE4MzI4NTNmZjM2YWNmYWY4ZGZjOTY5YjJmOTQyY2M0MjZhM2E3MGIzYTcyZjE4Mzg5MTUxZDMzYzhjMDQiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9", + "signature_segment": "zl5mWmv9cnXFb7PkaT0lTsu_IvlISkGTqV1F9uEIx8FVVAcJ9XlIGnyuEUHjpXl9oX9xEViSkYBnvTcV6N4bAQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImM4YzlkOTA5OGZhYzg0ZThhMTEwYTQ4YzJhMjI3MWNjYzJmMWRlNDRkNWZhOGVkNDM2OTg5Y2YxMGQ5OWE2NjEifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6bnVsbCwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6bnVsbCwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjkzMGE4MzI4NTNmZjM2YWNmYWY4ZGZjOTY5YjJmOTQyY2M0MjZhM2E3MGIzYTcyZjE4Mzg5MTUxZDMzYzhjMDQiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.zl5mWmv9cnXFb7PkaT0lTsu_IvlISkGTqV1F9uEIx8FVVAcJ9XlIGnyuEUHjpXl9oX9xEViSkYBnvTcV6N4bAQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "809600097c3f8db9a0a1dd7d86e087ded0b1fad8420311b50740d432ae0b4019" + } + }, + "assessment_required_check_failed": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-assessment+jws" + }, + "payload": { + "schema": "ts-publisher-assessment-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "assessment_id": "33333333-3333-4333-8333-000000000002", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + }, + "observed_from": 1793793590, + "observed_until": 1793793598, + "issued_at": 1793793599, + "expires_at": 1793794200, + "policy": { + "id": "publisher-declaration-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + } + }, + "result": "satisfied", + "checks": { + "host_authorization": { + "outcome": "pass", + "method": "dns-key-control-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "cc4c418d4b84877cdaa3817d13875daf9adff15c038d1f9ee2fbfd4e5d9f1cb5" + } + }, + "key_possession": { + "outcome": "fail", + "method": "publisher-evidence-jws-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + } + }, + "host_service_binding": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "active_version": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "configuration": { + "outcome": "pass", + "method": "public-identity-projection-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + } + } + }, + "build": { + "provenance": "custom", + "assurance": "declared", + "method": "publisher-custom-declaration-v1", + "artifact_sha256": null, + "provider_files_hash": null, + "release": null, + "coverage": "declaration_only" + }, + "reason_codes": [], + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAyIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJmYWlsIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9", + "signature_segment": "T5jUCFYENkAg1zs2j5QG-q2FIBQQriPPJP1sh6XUnEaAli4tFyqe26Pd0DZ7vmIL9o6iyVkBF_8BLLRUvvpnDA", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAyIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJmYWlsIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.T5jUCFYENkAg1zs2j5QG-q2FIBQQriPPJP1sh6XUnEaAli4tFyqe26Pd0DZ7vmIL9o6iyVkBF_8BLLRUvvpnDA", + "compact_sha256": { + "algorithm": "sha256", + "value": "da5ac3866b7179a103758be21668d00e9044211dad6ace18293475d3fe52c066" + } + }, + "assessment_reversed_time": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-assessment+jws" + }, + "payload": { + "schema": "ts-publisher-assessment-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "assessment_id": "33333333-3333-4333-8333-000000000003", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + }, + "observed_from": 1793793590, + "observed_until": 1793793598, + "issued_at": 1793793599, + "expires_at": 1793793598, + "policy": { + "id": "publisher-declaration-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + } + }, + "result": "satisfied", + "checks": { + "host_authorization": { + "outcome": "pass", + "method": "dns-key-control-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "cc4c418d4b84877cdaa3817d13875daf9adff15c038d1f9ee2fbfd4e5d9f1cb5" + } + }, + "key_possession": { + "outcome": "pass", + "method": "publisher-evidence-jws-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + } + }, + "host_service_binding": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "active_version": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "configuration": { + "outcome": "pass", + "method": "public-identity-projection-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + } + } + }, + "build": { + "provenance": "custom", + "assurance": "declared", + "method": "publisher-custom-declaration-v1", + "artifact_sha256": null, + "provider_files_hash": null, + "release": null, + "coverage": "declaration_only" + }, + "reason_codes": [], + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAzIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzkzNTk4LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9", + "signature_segment": "B-CQ4Oy5Wawj1PLoVxOQJ8bicmoc0KtmBouUcDK_bQLGjGJoIIL8wp-pXiNPqxxSAxk5h-UrsdEWG5dNtM8tDQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAzIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzkzNTk4LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.B-CQ4Oy5Wawj1PLoVxOQJ8bicmoc0KtmBouUcDK_bQLGjGJoIIL8wp-pXiNPqxxSAxk5h-UrsdEWG5dNtM8tDQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "cdfbbd956090d114e43d38c61c2ebfcb8ee3ce34a92d25c01e606ea8e91cc296" + } + }, + "assessment_dev_fixture_method": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-assessment+jws" + }, + "payload": { + "schema": "ts-publisher-assessment-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "assessment_id": "33333333-3333-4333-8333-000000000004", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + }, + "observed_from": 1793793590, + "observed_until": 1793793598, + "issued_at": 1793793599, + "expires_at": 1793794200, + "policy": { + "id": "publisher-declaration-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + } + }, + "result": "satisfied", + "checks": { + "host_authorization": { + "outcome": "pass", + "method": "fixture-enrollment-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "cc4c418d4b84877cdaa3817d13875daf9adff15c038d1f9ee2fbfd4e5d9f1cb5" + } + }, + "key_possession": { + "outcome": "pass", + "method": "publisher-evidence-jws-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + } + }, + "host_service_binding": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "active_version": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "configuration": { + "outcome": "pass", + "method": "public-identity-projection-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + } + } + }, + "build": { + "provenance": "custom", + "assurance": "declared", + "method": "publisher-custom-declaration-v1", + "artifact_sha256": null, + "provider_files_hash": null, + "release": null, + "coverage": "declaration_only" + }, + "reason_codes": [], + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDA0IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImZpeHR1cmUtZW5yb2xsbWVudC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9", + "signature_segment": "YXs9mydK9QZsk9zLCfoc4sYLOiofF3CpyJ5o1nJkgBrDr4Ng14BnJjBgqcKaiazrlD64hANaDjuzv2Efw2AYCg", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDA0IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImZpeHR1cmUtZW5yb2xsbWVudC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.YXs9mydK9QZsk9zLCfoc4sYLOiofF3CpyJ5o1nJkgBrDr4Ng14BnJjBgqcKaiazrlD64hANaDjuzv2Efw2AYCg", + "compact_sha256": { + "algorithm": "sha256", + "value": "b7678be5709a7f04a66c587c6eed46573a46c450ca0250befcd0e5e89dd6a8a8" + } + }, + "assessment_fingerprint_only": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-assessment+jws" + }, + "payload": { + "schema": "ts-publisher-assessment-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "assessment_id": "33333333-3333-4333-8333-000000000005", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + }, + "observed_from": 1793793590, + "observed_until": 1793793598, + "issued_at": 1793793599, + "expires_at": 1793794200, + "policy": { + "id": "publisher-declaration-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + } + }, + "result": "satisfied", + "checks": { + "host_authorization": { + "outcome": "pass", + "method": "dns-key-control-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "cc4c418d4b84877cdaa3817d13875daf9adff15c038d1f9ee2fbfd4e5d9f1cb5" + } + }, + "key_possession": { + "outcome": "pass", + "method": "publisher-evidence-jws-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + } + }, + "host_service_binding": { + "outcome": "pass", + "method": "fictional-fastly-host-service-binding-v1", + "observed_at": 1793793598, + "evidence_digest": { + "algorithm": "sha256", + "value": "4ddbd57e2866943c6fe4cbe7ae411b9c1ffb378a1e8e71cd084447916982f4d6" + } + }, + "active_version": { + "outcome": "pass", + "method": "fictional-fastly-active-version-v1", + "observed_at": 1793793598, + "evidence_digest": { + "algorithm": "sha256", + "value": "dcfa49474ad800692952de5e764cd6771875a5f3b6d0ecebe554ff7e9ebc957f" + } + }, + "configuration": { + "outcome": "pass", + "method": "public-identity-projection-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + } + } + }, + "build": { + "provenance": "unknown", + "assurance": "fingerprint_only", + "method": "fictional-fastly-fingerprint-v1", + "artifact_sha256": null, + "provider_files_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "release": null, + "coverage": "provider_package_fingerprint" + }, + "reason_codes": [], + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDA1IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJmaW5nZXJwcmludF9vbmx5IiwiY292ZXJhZ2UiOiJwcm92aWRlcl9wYWNrYWdlX2ZpbmdlcnByaW50IiwibWV0aG9kIjoiZmljdGlvbmFsLWZhc3RseS1maW5nZXJwcmludC12MSIsInByb3ZlbmFuY2UiOiJ1bmtub3duIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6ImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIiwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJkY2ZhNDk0NzRhZDgwMDY5Mjk1MmRlNWU3NjRjZDY3NzE4NzVhNWYzYjZkMGVjZWJlNTU0ZmY3ZTllYmM5NTdmIn0sIm1ldGhvZCI6ImZpY3Rpb25hbC1mYXN0bHktYWN0aXZlLXZlcnNpb24tdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5OCwib3V0Y29tZSI6InBhc3MifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0ZGRiZDU3ZTI4NjY5NDNjNmZlNGNiZTdhZTQxMWI5YzFmZmIzNzhhMWU4ZTcxY2QwODQ0NDc5MTY5ODJmNGQ2In0sIm1ldGhvZCI6ImZpY3Rpb25hbC1mYXN0bHktaG9zdC1zZXJ2aWNlLWJpbmRpbmctdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5OCwib3V0Y29tZSI6InBhc3MifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9", + "signature_segment": "EBGqiHYeTZ7_PtqkTXF8c0o66NhLv62rOumsNkCBi1467yPr6oeuKBayN1STelHLNZL5uNd1DdbzF4brw4OuCg", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDA1IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJmaW5nZXJwcmludF9vbmx5IiwiY292ZXJhZ2UiOiJwcm92aWRlcl9wYWNrYWdlX2ZpbmdlcnByaW50IiwibWV0aG9kIjoiZmljdGlvbmFsLWZhc3RseS1maW5nZXJwcmludC12MSIsInByb3ZlbmFuY2UiOiJ1bmtub3duIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6ImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIiwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJkY2ZhNDk0NzRhZDgwMDY5Mjk1MmRlNWU3NjRjZDY3NzE4NzVhNWYzYjZkMGVjZWJlNTU0ZmY3ZTllYmM5NTdmIn0sIm1ldGhvZCI6ImZpY3Rpb25hbC1mYXN0bHktYWN0aXZlLXZlcnNpb24tdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5OCwib3V0Y29tZSI6InBhc3MifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0ZGRiZDU3ZTI4NjY5NDNjNmZlNGNiZTdhZTQxMWI5YzFmZmIzNzhhMWU4ZTcxY2QwODQ0NDc5MTY5ODJmNGQ2In0sIm1ldGhvZCI6ImZpY3Rpb25hbC1mYXN0bHktaG9zdC1zZXJ2aWNlLWJpbmRpbmctdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5OCwib3V0Y29tZSI6InBhc3MifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.EBGqiHYeTZ7_PtqkTXF8c0o66NhLv62rOumsNkCBi1467yPr6oeuKBayN1STelHLNZL5uNd1DdbzF4brw4OuCg", + "compact_sha256": { + "algorithm": "sha256", + "value": "1e47c899ad7973d1c4ec522d4d99cbeb760320ef275ac54167ed1d352cf759da" + } + }, + "assessment_official_shaped_unqualified": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-assessment+jws" + }, + "payload": { + "schema": "ts-publisher-assessment-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "assessment_id": "33333333-3333-4333-8333-000000000006", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + }, + "observed_from": 1793793590, + "observed_until": 1793793598, + "issued_at": 1793793599, + "expires_at": 1793794200, + "policy": { + "id": "fictional-official-profile-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "407594246b35a9f8e390421593f5e7ff5a02bcd992d9a276f698ff8e077b3d33" + } + }, + "result": "satisfied", + "checks": { + "host_authorization": { + "outcome": "pass", + "method": "dns-key-control-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "cc4c418d4b84877cdaa3817d13875daf9adff15c038d1f9ee2fbfd4e5d9f1cb5" + } + }, + "key_possession": { + "outcome": "pass", + "method": "publisher-evidence-jws-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + } + }, + "host_service_binding": { + "outcome": "pass", + "method": "fictional-fastly-host-service-binding-v1", + "observed_at": 1793793598, + "evidence_digest": { + "algorithm": "sha256", + "value": "4ddbd57e2866943c6fe4cbe7ae411b9c1ffb378a1e8e71cd084447916982f4d6" + } + }, + "active_version": { + "outcome": "pass", + "method": "fictional-fastly-active-version-v1", + "observed_at": 1793793598, + "evidence_digest": { + "algorithm": "sha256", + "value": "dcfa49474ad800692952de5e764cd6771875a5f3b6d0ecebe554ff7e9ebc957f" + } + }, + "configuration": { + "outcome": "pass", + "method": "public-identity-projection-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + } + } + }, + "build": { + "provenance": "official", + "assurance": "artifact_bound", + "method": "fictional-fastly-artifact-binding-v1", + "artifact_sha256": { + "algorithm": "sha256", + "value": "5931956b4a7ea501c6db03558808b7389137efa75c13477bd8a9b67e316fa9b2" + }, + "provider_files_hash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "release": { + "repository": "example/project", + "tag": "fictional-v1", + "source_commit": "cccccccccccccccccccccccccccccccccccccccc", + "asset_name": "fictional-release.wasm", + "artifact_sha256": { + "algorithm": "sha256", + "value": "5931956b4a7ea501c6db03558808b7389137efa75c13477bd8a9b67e316fa9b2" + }, + "verification_evidence_digest": { + "algorithm": "sha256", + "value": "251bdcb583f9c0943b87bd26e1d21bc010eb2069554532a55ba38f10751341c0" + } + }, + "coverage": "selected_executable_only" + }, + "reason_codes": [], + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDA2IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNTkzMTk1NmI0YTdlYTUwMWM2ZGIwMzU1ODgwOGI3Mzg5MTM3ZWZhNzVjMTM0NzdiZDhhOWI2N2UzMTZmYTliMiJ9LCJhc3N1cmFuY2UiOiJhcnRpZmFjdF9ib3VuZCIsImNvdmVyYWdlIjoic2VsZWN0ZWRfZXhlY3V0YWJsZV9vbmx5IiwibWV0aG9kIjoiZmljdGlvbmFsLWZhc3RseS1hcnRpZmFjdC1iaW5kaW5nLXYxIiwicHJvdmVuYW5jZSI6Im9mZmljaWFsIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6ImJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiIiwicmVsZWFzZSI6eyJhcnRpZmFjdF9zaGEyNTYiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI1OTMxOTU2YjRhN2VhNTAxYzZkYjAzNTU4ODA4YjczODkxMzdlZmE3NWMxMzQ3N2JkOGE5YjY3ZTMxNmZhOWIyIn0sImFzc2V0X25hbWUiOiJmaWN0aW9uYWwtcmVsZWFzZS53YXNtIiwicmVwb3NpdG9yeSI6ImV4YW1wbGUvcHJvamVjdCIsInNvdXJjZV9jb21taXQiOiJjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjIiwidGFnIjoiZmljdGlvbmFsLXYxIiwidmVyaWZpY2F0aW9uX2V2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjI1MWJkY2I1ODNmOWMwOTQzYjg3YmQyNmUxZDIxYmMwMTBlYjIwNjk1NTQ1MzJhNTViYTM4ZjEwNzUxMzQxYzAifX19LCJjaGVja3MiOnsiYWN0aXZlX3ZlcnNpb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNmYTQ5NDc0YWQ4MDA2OTI5NTJkZTVlNzY0Y2Q2NzcxODc1YTVmM2I2ZDBlY2ViZTU1NGZmN2U5ZWJjOTU3ZiJ9LCJtZXRob2QiOiJmaWN0aW9uYWwtZmFzdGx5LWFjdGl2ZS12ZXJzaW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTgsIm91dGNvbWUiOiJwYXNzIn0sImNvbmZpZ3VyYXRpb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZWQyZDhhZDIxZjc3YWNiYmNkYzQ2ZTQ0YTg0MjU1ODE1ZjE5MDQwNWM4OTkxMjA3NDJhOTUwNDkwYjZjOTIzYiJ9LCJtZXRob2QiOiJwdWJsaWMtaWRlbnRpdHktcHJvamVjdGlvbi12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X2F1dGhvcml6YXRpb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiY2M0YzQxOGQ0Yjg0ODc3Y2RhYTM4MTdkMTM4NzVkYWY5YWRmZjE1YzAzOGQxZjllZTJmYmZkNGU1ZDlmMWNiNSJ9LCJtZXRob2QiOiJkbnMta2V5LWNvbnRyb2wtdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5Niwib3V0Y29tZSI6InBhc3MifSwiaG9zdF9zZXJ2aWNlX2JpbmRpbmciOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNGRkYmQ1N2UyODY2OTQzYzZmZTRjYmU3YWU0MTFiOWMxZmZiMzc4YTFlOGU3MWNkMDg0NDQ3OTE2OTgyZjRkNiJ9LCJtZXRob2QiOiJmaWN0aW9uYWwtZmFzdGx5LWhvc3Qtc2VydmljZS1iaW5kaW5nLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTgsIm91dGNvbWUiOiJwYXNzIn0sImtleV9wb3NzZXNzaW9uIjp7ImV2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjIxNDgwZDFiNTM2ZWI0YmJkZjQ5ODBlM2ZkODRhMGZlMjg2NjQzYzU0YTgxMDNlNTkwM2Y1YmNlNTc2M2E1OTIifSwibWV0aG9kIjoicHVibGlzaGVyLWV2aWRlbmNlLWp3cy12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9fSwiZW5yb2xsbWVudF9pZCI6IjQ0NDQ0NDQ0LTQ0NDQtNDQ0NC04NDQ0LTQ0NDQ0NDQ0NDQ0NCIsImV2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjIxNDgwZDFiNTM2ZWI0YmJkZjQ5ODBlM2ZkODRhMGZlMjg2NjQzYzU0YTgxMDNlNTkwM2Y1YmNlNTc2M2E1OTIifSwiZXhwaXJlc19hdCI6MTc5Mzc5NDIwMCwiZXh0ZW5zaW9ucyI6e30sImlzc3VlZF9hdCI6MTc5Mzc5MzU5OSwiaXNzdWVyIjoiaHR0cHM6Ly92ZXJpZmllci5leGFtcGxlLmNvbS9wdWJsaXNoZXItYXR0ZXN0YXRpb24iLCJvYnNlcnZlZF9mcm9tIjoxNzkzNzkzNTkwLCJvYnNlcnZlZF91bnRpbCI6MTc5Mzc5MzU5OCwicG9saWN5Ijp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjQwNzU5NDI0NmIzNWE5ZjhlMzkwNDIxNTkzZjVlN2ZmNWEwMmJjZDk5MmQ5YTI3NmY2OThmZjhlMDc3YjNkMzMifSwiaWQiOiJmaWN0aW9uYWwtb2ZmaWNpYWwtcHJvZmlsZS12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9", + "signature_segment": "idCyPFb2oKR8qaqQMCwZjn-5brEN5mplSjjVcOfgEhvsUCj3VJzSY0nMCZdZXz3-vPqmBZhnhRBbmcuPPZ13Cw", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDA2IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNTkzMTk1NmI0YTdlYTUwMWM2ZGIwMzU1ODgwOGI3Mzg5MTM3ZWZhNzVjMTM0NzdiZDhhOWI2N2UzMTZmYTliMiJ9LCJhc3N1cmFuY2UiOiJhcnRpZmFjdF9ib3VuZCIsImNvdmVyYWdlIjoic2VsZWN0ZWRfZXhlY3V0YWJsZV9vbmx5IiwibWV0aG9kIjoiZmljdGlvbmFsLWZhc3RseS1hcnRpZmFjdC1iaW5kaW5nLXYxIiwicHJvdmVuYW5jZSI6Im9mZmljaWFsIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6ImJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiIiwicmVsZWFzZSI6eyJhcnRpZmFjdF9zaGEyNTYiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI1OTMxOTU2YjRhN2VhNTAxYzZkYjAzNTU4ODA4YjczODkxMzdlZmE3NWMxMzQ3N2JkOGE5YjY3ZTMxNmZhOWIyIn0sImFzc2V0X25hbWUiOiJmaWN0aW9uYWwtcmVsZWFzZS53YXNtIiwicmVwb3NpdG9yeSI6ImV4YW1wbGUvcHJvamVjdCIsInNvdXJjZV9jb21taXQiOiJjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjIiwidGFnIjoiZmljdGlvbmFsLXYxIiwidmVyaWZpY2F0aW9uX2V2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjI1MWJkY2I1ODNmOWMwOTQzYjg3YmQyNmUxZDIxYmMwMTBlYjIwNjk1NTQ1MzJhNTViYTM4ZjEwNzUxMzQxYzAifX19LCJjaGVja3MiOnsiYWN0aXZlX3ZlcnNpb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZGNmYTQ5NDc0YWQ4MDA2OTI5NTJkZTVlNzY0Y2Q2NzcxODc1YTVmM2I2ZDBlY2ViZTU1NGZmN2U5ZWJjOTU3ZiJ9LCJtZXRob2QiOiJmaWN0aW9uYWwtZmFzdGx5LWFjdGl2ZS12ZXJzaW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTgsIm91dGNvbWUiOiJwYXNzIn0sImNvbmZpZ3VyYXRpb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZWQyZDhhZDIxZjc3YWNiYmNkYzQ2ZTQ0YTg0MjU1ODE1ZjE5MDQwNWM4OTkxMjA3NDJhOTUwNDkwYjZjOTIzYiJ9LCJtZXRob2QiOiJwdWJsaWMtaWRlbnRpdHktcHJvamVjdGlvbi12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X2F1dGhvcml6YXRpb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiY2M0YzQxOGQ0Yjg0ODc3Y2RhYTM4MTdkMTM4NzVkYWY5YWRmZjE1YzAzOGQxZjllZTJmYmZkNGU1ZDlmMWNiNSJ9LCJtZXRob2QiOiJkbnMta2V5LWNvbnRyb2wtdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5Niwib3V0Y29tZSI6InBhc3MifSwiaG9zdF9zZXJ2aWNlX2JpbmRpbmciOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNGRkYmQ1N2UyODY2OTQzYzZmZTRjYmU3YWU0MTFiOWMxZmZiMzc4YTFlOGU3MWNkMDg0NDQ3OTE2OTgyZjRkNiJ9LCJtZXRob2QiOiJmaWN0aW9uYWwtZmFzdGx5LWhvc3Qtc2VydmljZS1iaW5kaW5nLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTgsIm91dGNvbWUiOiJwYXNzIn0sImtleV9wb3NzZXNzaW9uIjp7ImV2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjIxNDgwZDFiNTM2ZWI0YmJkZjQ5ODBlM2ZkODRhMGZlMjg2NjQzYzU0YTgxMDNlNTkwM2Y1YmNlNTc2M2E1OTIifSwibWV0aG9kIjoicHVibGlzaGVyLWV2aWRlbmNlLWp3cy12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9fSwiZW5yb2xsbWVudF9pZCI6IjQ0NDQ0NDQ0LTQ0NDQtNDQ0NC04NDQ0LTQ0NDQ0NDQ0NDQ0NCIsImV2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjIxNDgwZDFiNTM2ZWI0YmJkZjQ5ODBlM2ZkODRhMGZlMjg2NjQzYzU0YTgxMDNlNTkwM2Y1YmNlNTc2M2E1OTIifSwiZXhwaXJlc19hdCI6MTc5Mzc5NDIwMCwiZXh0ZW5zaW9ucyI6e30sImlzc3VlZF9hdCI6MTc5Mzc5MzU5OSwiaXNzdWVyIjoiaHR0cHM6Ly92ZXJpZmllci5leGFtcGxlLmNvbS9wdWJsaXNoZXItYXR0ZXN0YXRpb24iLCJvYnNlcnZlZF9mcm9tIjoxNzkzNzkzNTkwLCJvYnNlcnZlZF91bnRpbCI6MTc5Mzc5MzU5OCwicG9saWN5Ijp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjQwNzU5NDI0NmIzNWE5ZjhlMzkwNDIxNTkzZjVlN2ZmNWEwMmJjZDk5MmQ5YTI3NmY2OThmZjhlMDc3YjNkMzMifSwiaWQiOiJmaWN0aW9uYWwtb2ZmaWNpYWwtcHJvZmlsZS12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.idCyPFb2oKR8qaqQMCwZjn-5brEN5mplSjjVcOfgEhvsUCj3VJzSY0nMCZdZXz3-vPqmBZhnhRBbmcuPPZ13Cw", + "compact_sha256": { + "algorithm": "sha256", + "value": "9c5c652f9d510471fc9d7f8c4e45c6f66a334ff32a35fec8db6aed3e394cc22f" + } + }, + "status_after_binding_generation": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 2, + "assessment_status": "current", + "enrollment_status": "active", + "publisher_key_status": "active", + "reason_codes": [], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoyfQ", + "signature_segment": "t7zFXgNkhGUyCzcyEUQzI2KTrp-LwG0COl0_wi0uH3h1NJY8BMQoEGrMUCN7gduvhJc7RIrXP8KV9bC9airyAw", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoyfQ.t7zFXgNkhGUyCzcyEUQzI2KTrp-LwG0COl0_wi0uH3h1NJY8BMQoEGrMUCN7gduvhJc7RIrXP8KV9bC9airyAw", + "compact_sha256": { + "algorithm": "sha256", + "value": "3d40562d97f0fea4d17324413948e67c68313f7445109e916b2ee8b21d89d2a3" + } + }, + "collected_evidence": { + "protected": { + "alg": "Ed25519", + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "typ": "ts-publisher-evidence+jws" + }, + "payload": { + "schema": "ts-publisher-evidence-v1", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "nonce": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8", + "issued_at": 1793793595, + "expires_at": 1793793655, + "build_report": { + "declared_class": "custom", + "source_commit": null, + "package_version": null, + "target": null, + "variant": null + }, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ", + "payload_segment": "eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiQUFFQ0F3UUZCZ2NJQ1FvTERBME9EeEFSRWhNVUZSWVhHQmthR3h3ZEhoOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9", + "signature_segment": "xc4S8DU6BX4jBFmbOb94sUWX35RAVxWZ3bK0fQaR5fahiSVDjM-eYfn13zgosc6djDiTzCNeWaeb-T5sVP65CQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiQUFFQ0F3UUZCZ2NJQ1FvTERBME9EeEFSRWhNVUZSWVhHQmthR3h3ZEhoOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.xc4S8DU6BX4jBFmbOb94sUWX35RAVxWZ3bK0fQaR5fahiSVDjM-eYfn13zgosc6djDiTzCNeWaeb-T5sVP65CQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + } + }, + "other_binding_evidence": { + "protected": { + "alg": "Ed25519", + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "typ": "ts-publisher-evidence+jws" + }, + "payload": { + "schema": "ts-publisher-evidence-v1", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 43, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "ed31cd744ee24b4dcd9928dcf5bc6ab3e79c4381687d9a3840fb8b71e1020a4a", + "nonce": "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8", + "issued_at": 1793793595, + "expires_at": 1793793655, + "build_report": { + "declared_class": "custom", + "source_commit": null, + "package_version": null, + "target": null, + "variant": null + }, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ", + "payload_segment": "eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQzLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6ImVkMzFjZDc0NGVlMjRiNGRjZDk5MjhkY2Y1YmM2YWIzZTc5YzQzODE2ODdkOWEzODQwZmI4YjcxZTEwMjBhNGEiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9", + "signature_segment": "KCITadm8ch3NCK9vIsdN_TtSr9HDkENP9tLphwY1RF-A19KixjJ6Q05lcmyj-IVvYOH235f0qX59oJ5P3yFCBQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQzLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6ImVkMzFjZDc0NGVlMjRiNGRjZDk5MjhkY2Y1YmM2YWIzZTc5YzQzODE2ODdkOWEzODQwZmI4YjcxZTEwMjBhNGEiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.KCITadm8ch3NCK9vIsdN_TtSr9HDkENP9tLphwY1RF-A19KixjJ6Q05lcmyj-IVvYOH235f0qX59oJ5P3yFCBQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "0984e95f3cac757211e84bd53cacabedfff2867ae04655641b56f896eb0d2a50" + } + }, + "status_withdrawn_assessment": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 1, + "assessment_status": "withdrawn", + "enrollment_status": "active", + "publisher_key_status": "active", + "reason_codes": ["withdrawn"], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJ3aXRoZHJhd24iLCJiaW5kaW5nX2lkIjoiNTdhYjRkYWJiM2Y4NGFmYWE4YzcwNjQ0NzVlNDFiNjRiNjc2NDAxNDMyMTNiZjAxNzM5ZjNlNWRkMjNhMjdiZiIsImVucm9sbG1lbnRfaWQiOiI0NDQ0NDQ0NC00NDQ0LTQ0NDQtODQ0NC00NDQ0NDQ0NDQ0NDQiLCJlbnJvbGxtZW50X3N0YXR1cyI6ImFjdGl2ZSIsImV4cGlyZXNfYXQiOjE3OTM3OTM2NTksImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTksImlzc3VlciI6Imh0dHBzOi8vdmVyaWZpZXIuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2tleV9raWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwicHVibGlzaGVyX2tleV9zdGF0dXMiOiJhY3RpdmUiLCJyZWFzb25fY29kZXMiOlsid2l0aGRyYXduIl0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1zdGF0dXMtdjEiLCJzZXF1ZW5jZSI6MX0", + "signature_segment": "srCJwbbKhvBp2fAL9Pok7nCFFZ7pfOBWkqf7Eb1THodnJPngPf2ZbRhanIAXueT90imF2mTpMKkK7rNa59ETCA", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJ3aXRoZHJhd24iLCJiaW5kaW5nX2lkIjoiNTdhYjRkYWJiM2Y4NGFmYWE4YzcwNjQ0NzVlNDFiNjRiNjc2NDAxNDMyMTNiZjAxNzM5ZjNlNWRkMjNhMjdiZiIsImVucm9sbG1lbnRfaWQiOiI0NDQ0NDQ0NC00NDQ0LTQ0NDQtODQ0NC00NDQ0NDQ0NDQ0NDQiLCJlbnJvbGxtZW50X3N0YXR1cyI6ImFjdGl2ZSIsImV4cGlyZXNfYXQiOjE3OTM3OTM2NTksImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTksImlzc3VlciI6Imh0dHBzOi8vdmVyaWZpZXIuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2tleV9raWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwicHVibGlzaGVyX2tleV9zdGF0dXMiOiJhY3RpdmUiLCJyZWFzb25fY29kZXMiOlsid2l0aGRyYXduIl0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1zdGF0dXMtdjEiLCJzZXF1ZW5jZSI6MX0.srCJwbbKhvBp2fAL9Pok7nCFFZ7pfOBWkqf7Eb1THodnJPngPf2ZbRhanIAXueT90imF2mTpMKkK7rNa59ETCA", + "compact_sha256": { + "algorithm": "sha256", + "value": "b003e8a8a0dbad74ae23fd82bf98ee05616fb8304caeac1786f4f50b79da107c" + } + }, + "status_withdrawn_enrollment": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 1, + "assessment_status": "current", + "enrollment_status": "withdrawn", + "publisher_key_status": "active", + "reason_codes": ["withdrawn"], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJ3aXRoZHJhd24iLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbIndpdGhkcmF3biJdLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItc3RhdHVzLXYxIiwic2VxdWVuY2UiOjF9", + "signature_segment": "3uo5hvibm6potKa4ZUtCaLcB-hCDBbsayvUxe9QW4qxBSDqEcTZYVH5twokmfI04mHo3QwopQEU_6P_7rksxAw", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJ3aXRoZHJhd24iLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbIndpdGhkcmF3biJdLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItc3RhdHVzLXYxIiwic2VxdWVuY2UiOjF9.3uo5hvibm6potKa4ZUtCaLcB-hCDBbsayvUxe9QW4qxBSDqEcTZYVH5twokmfI04mHo3QwopQEU_6P_7rksxAw", + "compact_sha256": { + "algorithm": "sha256", + "value": "3dc3ab49c82fcd03dbe68f90b949131bad4606fee742ef99f460cd790c221dae" + } + }, + "status_compromised_key": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 1, + "assessment_status": "current", + "enrollment_status": "active", + "publisher_key_status": "compromised", + "reason_codes": ["key_compromised"], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiY29tcHJvbWlzZWQiLCJyZWFzb25fY29kZXMiOlsia2V5X2NvbXByb21pc2VkIl0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1zdGF0dXMtdjEiLCJzZXF1ZW5jZSI6MX0", + "signature_segment": "mGLoSxzbw5M2axmVmKmCqA7T7qq3mqBsK4ZqOFJr9afzxUuvkOd_pNgdye1W2L1KEsK3Etk2ojzboJ7BRnFUDA", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiY29tcHJvbWlzZWQiLCJyZWFzb25fY29kZXMiOlsia2V5X2NvbXByb21pc2VkIl0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1zdGF0dXMtdjEiLCJzZXF1ZW5jZSI6MX0.mGLoSxzbw5M2axmVmKmCqA7T7qq3mqBsK4ZqOFJr9afzxUuvkOd_pNgdye1W2L1KEsK3Etk2ojzboJ7BRnFUDA", + "compact_sha256": { + "algorithm": "sha256", + "value": "4126da87f07b5101f34ff9052494695f79f5c4c3bb115c985d6b508c5a734822" + } + }, + "status_foreign_assessment": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 1, + "assessment_status": "current", + "enrollment_status": "active", + "publisher_key_status": "active", + "reason_codes": [], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiYWFhYWFhYWEtYWFhYS00YWFhLThhYWEtYWFhYWFhYWFhYWFhIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ", + "signature_segment": "oOCuY11yBYxU6YxrPDPc06MnShyS3jcIOh-irT1b65eVE34_g_5VrPzWMZpG6kUGgpoKBl2hgWnAlEvCnoXfDQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiYWFhYWFhYWEtYWFhYS00YWFhLThhYWEtYWFhYWFhYWFhYWFhIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ.oOCuY11yBYxU6YxrPDPc06MnShyS3jcIOh-irT1b65eVE34_g_5VrPzWMZpG6kUGgpoKBl2hgWnAlEvCnoXfDQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "ad57225154d2c02a5e04a460e69126d97fcc7acb6c8b7589eff0b0a9f20c5b75" + } + }, + "status_foreign_enrollment": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 1, + "assessment_status": "current", + "enrollment_status": "active", + "publisher_key_status": "active", + "reason_codes": [], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiYWFhYWFhYWEtYWFhYS00YWFhLThhYWEtYWFhYWFhYWFhYWFhIiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ", + "signature_segment": "lm5EQgCM2dNHVvZcFz1_Z_kH2aIStEqXEIqnkptT-6Hft5wiQ-9rqkL9MfwhD6L1XDI9mMm5wjpzzXXXp8XTBg", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiYWFhYWFhYWEtYWFhYS00YWFhLThhYWEtYWFhYWFhYWFhYWFhIiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ.lm5EQgCM2dNHVvZcFz1_Z_kH2aIStEqXEIqnkptT-6Hft5wiQ-9rqkL9MfwhD6L1XDI9mMm5wjpzzXXXp8XTBg", + "compact_sha256": { + "algorithm": "sha256", + "value": "1f1bda42c5a709c9e5ea6133cfcb414dd2141d5afd8e8771f1fc65136f26813d" + } + }, + "status_foreign_binding": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 1, + "assessment_status": "current", + "enrollment_status": "active", + "publisher_key_status": "active", + "reason_codes": [], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6ImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ", + "signature_segment": "kZqAeLupZKB7nkreYIaJZVLVKuCtQXPC5HeVRtiin_wA6CTrw7AUH1DoUnB3BTNUTols3hn6xm84dGRrfoDiBg", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6ImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ.kZqAeLupZKB7nkreYIaJZVLVKuCtQXPC5HeVRtiin_wA6CTrw7AUH1DoUnB3BTNUTols3hn6xm84dGRrfoDiBg", + "compact_sha256": { + "algorithm": "sha256", + "value": "63efb93f6af01bedc92c94f2a12f0685e5398c6e6bde4ee080f41c9b12ede947" + } + }, + "status_foreign_key": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "sequence": 1, + "assessment_status": "current", + "enrollment_status": "active", + "publisher_key_status": "active", + "reason_codes": [], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ", + "signature_segment": "PzdaZdKNfZAJZJbsM8l22Gp724iy8jGz-eZy06HtBRl1u5x9c6jJyWTyFlnsjBUQxHIkwPcGZOcv56rXB4j-AQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ.PzdaZdKNfZAJZJbsM8l22Gp724iy8jGz-eZy06HtBRl1u5x9c6jJyWTyFlnsjBUQxHIkwPcGZOcv56rXB4j-AQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "fcf482e73a1858ea0ed2ac285956b5b1d4c2a99c6bd08a2d8c74c3a8d9e5b9c7" + } + }, + "assessment_not_satisfied": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-assessment+jws" + }, + "payload": { + "schema": "ts-publisher-assessment-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "assessment_id": "33333333-3333-4333-8333-000000000099", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + }, + "observed_from": 1793793590, + "observed_until": 1793793598, + "issued_at": 1793793599, + "expires_at": 1793794200, + "policy": { + "id": "publisher-declaration-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + } + }, + "result": "not_satisfied", + "checks": { + "host_authorization": { + "outcome": "pass", + "method": "dns-key-control-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "cc4c418d4b84877cdaa3817d13875daf9adff15c038d1f9ee2fbfd4e5d9f1cb5" + } + }, + "key_possession": { + "outcome": "fail", + "method": "publisher-evidence-jws-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "21480d1b536eb4bbdf4980e3fd84a0fe286643c54a8103e5903f5bce5763a592" + } + }, + "host_service_binding": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "active_version": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "configuration": { + "outcome": "pass", + "method": "public-identity-projection-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "ed2d8ad21f77acbbcdc46e44a84255815f190405c899120742a950490b6c923b" + } + } + }, + "build": { + "provenance": "custom", + "assurance": "declared", + "method": "publisher-custom-declaration-v1", + "artifact_sha256": null, + "provider_files_hash": null, + "release": null, + "coverage": "declaration_only" + }, + "reason_codes": ["key_unauthorized"], + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDk5IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJmYWlsIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOlsia2V5X3VuYXV0aG9yaXplZCJdLCJyZXN1bHQiOiJub3Rfc2F0aXNmaWVkIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWFzc2Vzc21lbnQtdjEifQ", + "signature_segment": "Tx6sbbtyTPWbotfoKBJ8E5JCoCk8hurTPysv7vh-CgqJiohqheCfQfBtSeC4UH9_xvFbvtZvDsR1hp06zy2NBQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDk5IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJmYWlsIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOlsia2V5X3VuYXV0aG9yaXplZCJdLCJyZXN1bHQiOiJub3Rfc2F0aXNmaWVkIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWFzc2Vzc21lbnQtdjEifQ.Tx6sbbtyTPWbotfoKBJ8E5JCoCk8hurTPysv7vh-CgqJiohqheCfQfBtSeC4UH9_xvFbvtZvDsR1hp06zy2NBQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "d2814edb38fe48025e1860b7d4d2d4511e17c638e439735e56b827d972e25d44" + } + }, + "second_site_assessment": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-assessment+jws" + }, + "payload": { + "schema": "ts-publisher-assessment-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "assessment_id": "33333333-3333-4333-8333-000000000100", + "enrollment_id": "44444444-4444-4444-8444-444444444445", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "sport.example.net", + "publisher_id": "owner.example.com", + "operator_id": null, + "deployment_id": "22222222-2222-4222-8222-222222222223", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-sport-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "WeVHjIQTOLKu-P74oC-WL-MNn_sLM5CvrzuMjDFC3-s", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "JUO5L_EJVRFHatyDadtt3JM2ZaEZeN2hQE7hBmypVZ0" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "223be82747c7f51f5ef82e73533fa7c2fb8fabf8838a6a50fdd91ac349c181ab" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "sport.example.net", + "publisher_id": "owner.example.com", + "operator_id": null, + "deployment_id": "22222222-2222-4222-8222-222222222223", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "934f0e72cd3794a408b6a6af99ca4d82cc014ff3c005baaffd2df2afbe22ec71", + "evidence_digest": { + "algorithm": "sha256", + "value": "9c4875be5301878d1bd9e56f14748ce0c1587db63d33d48a10b780ec12c4db26" + }, + "observed_from": 1793793590, + "observed_until": 1793793598, + "issued_at": 1793793599, + "expires_at": 1793794200, + "policy": { + "id": "publisher-declaration-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + } + }, + "result": "satisfied", + "checks": { + "host_authorization": { + "outcome": "pass", + "method": "dns-key-control-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "cc4c418d4b84877cdaa3817d13875daf9adff15c038d1f9ee2fbfd4e5d9f1cb5" + } + }, + "key_possession": { + "outcome": "pass", + "method": "publisher-evidence-jws-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "9c4875be5301878d1bd9e56f14748ce0c1587db63d33d48a10b780ec12c4db26" + } + }, + "host_service_binding": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "active_version": { + "outcome": "not_assessed", + "method": null, + "observed_at": null, + "evidence_digest": null + }, + "configuration": { + "outcome": "pass", + "method": "public-identity-projection-v1", + "observed_at": 1793793596, + "evidence_digest": { + "algorithm": "sha256", + "value": "223be82747c7f51f5ef82e73533fa7c2fb8fabf8838a6a50fdd91ac349c181ab" + } + } + }, + "build": { + "provenance": "custom", + "assurance": "declared", + "method": "publisher-custom-declaration-v1", + "artifact_sha256": null, + "provider_files_hash": null, + "release": null, + "coverage": "declaration_only" + }, + "reason_codes": [], + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMTAwIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiSlVPNUxfRUpWUkZIYXR5RGFkdHQzSk0yWmFFWmVOMmhRRTdoQm15cFZaMCJ9LCJraWQiOiJXZVZIaklRVE9MS3UtUDc0b0MtV0wtTU5uX3NMTTVDdnJ6dU1qREZDMy1zIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIyMjNiZTgyNzQ3YzdmNTFmNWVmODJlNzM1MzNmYTdjMmZiOGZhYmY4ODM4YTZhNTBmZGQ5MWFjMzQ5YzE4MWFiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMyIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJzcG9ydC5leGFtcGxlLm5ldCIsIm9wZXJhdG9yX2lkIjpudWxsLCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMyIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImV4dGVuc2lvbnMiOnt9LCJob3N0Ijoic3BvcnQuZXhhbXBsZS5uZXQiLCJvcGVyYXRvcl9pZCI6bnVsbCwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNwb3J0LXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjkzNGYwZTcyY2QzNzk0YTQwOGI2YTZhZjk5Y2E0ZDgyY2MwMTRmZjNjMDA1YmFhZmZkMmRmMmFmYmUyMmVjNzEiLCJidWlsZCI6eyJhcnRpZmFjdF9zaGEyNTYiOm51bGwsImFzc3VyYW5jZSI6ImRlY2xhcmVkIiwiY292ZXJhZ2UiOiJkZWNsYXJhdGlvbl9vbmx5IiwibWV0aG9kIjoicHVibGlzaGVyLWN1c3RvbS1kZWNsYXJhdGlvbi12MSIsInByb3ZlbmFuY2UiOiJjdXN0b20iLCJwcm92aWRlcl9maWxlc19oYXNoIjpudWxsLCJyZWxlYXNlIjpudWxsfSwiY2hlY2tzIjp7ImFjdGl2ZV92ZXJzaW9uIjp7ImV2aWRlbmNlX2RpZ2VzdCI6bnVsbCwibWV0aG9kIjpudWxsLCJvYnNlcnZlZF9hdCI6bnVsbCwib3V0Y29tZSI6Im5vdF9hc3Nlc3NlZCJ9LCJjb25maWd1cmF0aW9uIjp7ImV2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjIyM2JlODI3NDdjN2Y1MWY1ZWY4MmU3MzUzM2ZhN2MyZmI4ZmFiZjg4MzhhNmE1MGZkZDkxYWMzNDljMTgxYWIifSwibWV0aG9kIjoicHVibGljLWlkZW50aXR5LXByb2plY3Rpb24tdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5Niwib3V0Y29tZSI6InBhc3MifSwiaG9zdF9hdXRob3JpemF0aW9uIjp7ImV2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImNjNGM0MThkNGI4NDg3N2NkYWEzODE3ZDEzODc1ZGFmOWFkZmYxNWMwMzhkMWY5ZWUyZmJmZDRlNWQ5ZjFjYjUifSwibWV0aG9kIjoiZG5zLWtleS1jb250cm9sLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3Rfc2VydmljZV9iaW5kaW5nIjp7ImV2aWRlbmNlX2RpZ2VzdCI6bnVsbCwibWV0aG9kIjpudWxsLCJvYnNlcnZlZF9hdCI6bnVsbCwib3V0Y29tZSI6Im5vdF9hc3Nlc3NlZCJ9LCJrZXlfcG9zc2Vzc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5YzQ4NzViZTUzMDE4NzhkMWJkOWU1NmYxNDc0OGNlMGMxNTg3ZGI2M2QzM2Q0OGExMGI3ODBlYzEyYzRkYjI2In0sIm1ldGhvZCI6InB1Ymxpc2hlci1ldmlkZW5jZS1qd3MtdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5Niwib3V0Y29tZSI6InBhc3MifX0sImVucm9sbG1lbnRfaWQiOiI0NDQ0NDQ0NC00NDQ0LTQ0NDQtODQ0NC00NDQ0NDQ0NDQ0NDUiLCJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5YzQ4NzViZTUzMDE4NzhkMWJkOWU1NmYxNDc0OGNlMGMxNTg3ZGI2M2QzM2Q0OGExMGI3ODBlYzEyYzRkYjI2In0sImV4cGlyZXNfYXQiOjE3OTM3OTQyMDAsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTksImlzc3VlciI6Imh0dHBzOi8vdmVyaWZpZXIuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwib2JzZXJ2ZWRfZnJvbSI6MTc5Mzc5MzU5MCwib2JzZXJ2ZWRfdW50aWwiOjE3OTM3OTM1OTgsInBvbGljeSI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIxOGEyYjQ2MDhmMTNmNmQyOGEyNDM5MjIyNzMxZWYwZTM3ZGZhYmQ2NGVlODQwYTc3OWVkOWY2MGYyYjJiMmYzIn0sImlkIjoicHVibGlzaGVyLWRlY2xhcmF0aW9uLXYxIiwidmVyc2lvbiI6MX0sInJlYXNvbl9jb2RlcyI6W10sInJlc3VsdCI6InNhdGlzZmllZCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50LXYxIn0", + "signature_segment": "ngp0BbpYM-Z0DSVcb1YfY_0lbZ_ZIvVTQovw7ygJM4qm2dJ9t2gVBhNdmM-JM4h78_9j1eUO4KPanpHewcWGBg", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMTAwIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiSlVPNUxfRUpWUkZIYXR5RGFkdHQzSk0yWmFFWmVOMmhRRTdoQm15cFZaMCJ9LCJraWQiOiJXZVZIaklRVE9MS3UtUDc0b0MtV0wtTU5uX3NMTTVDdnJ6dU1qREZDMy1zIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIyMjNiZTgyNzQ3YzdmNTFmNWVmODJlNzM1MzNmYTdjMmZiOGZhYmY4ODM4YTZhNTBmZGQ5MWFjMzQ5YzE4MWFiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMyIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJzcG9ydC5leGFtcGxlLm5ldCIsIm9wZXJhdG9yX2lkIjpudWxsLCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMyIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImV4dGVuc2lvbnMiOnt9LCJob3N0Ijoic3BvcnQuZXhhbXBsZS5uZXQiLCJvcGVyYXRvcl9pZCI6bnVsbCwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNwb3J0LXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjkzNGYwZTcyY2QzNzk0YTQwOGI2YTZhZjk5Y2E0ZDgyY2MwMTRmZjNjMDA1YmFhZmZkMmRmMmFmYmUyMmVjNzEiLCJidWlsZCI6eyJhcnRpZmFjdF9zaGEyNTYiOm51bGwsImFzc3VyYW5jZSI6ImRlY2xhcmVkIiwiY292ZXJhZ2UiOiJkZWNsYXJhdGlvbl9vbmx5IiwibWV0aG9kIjoicHVibGlzaGVyLWN1c3RvbS1kZWNsYXJhdGlvbi12MSIsInByb3ZlbmFuY2UiOiJjdXN0b20iLCJwcm92aWRlcl9maWxlc19oYXNoIjpudWxsLCJyZWxlYXNlIjpudWxsfSwiY2hlY2tzIjp7ImFjdGl2ZV92ZXJzaW9uIjp7ImV2aWRlbmNlX2RpZ2VzdCI6bnVsbCwibWV0aG9kIjpudWxsLCJvYnNlcnZlZF9hdCI6bnVsbCwib3V0Y29tZSI6Im5vdF9hc3Nlc3NlZCJ9LCJjb25maWd1cmF0aW9uIjp7ImV2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjIyM2JlODI3NDdjN2Y1MWY1ZWY4MmU3MzUzM2ZhN2MyZmI4ZmFiZjg4MzhhNmE1MGZkZDkxYWMzNDljMTgxYWIifSwibWV0aG9kIjoicHVibGljLWlkZW50aXR5LXByb2plY3Rpb24tdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5Niwib3V0Y29tZSI6InBhc3MifSwiaG9zdF9hdXRob3JpemF0aW9uIjp7ImV2aWRlbmNlX2RpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImNjNGM0MThkNGI4NDg3N2NkYWEzODE3ZDEzODc1ZGFmOWFkZmYxNWMwMzhkMWY5ZWUyZmJmZDRlNWQ5ZjFjYjUifSwibWV0aG9kIjoiZG5zLWtleS1jb250cm9sLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3Rfc2VydmljZV9iaW5kaW5nIjp7ImV2aWRlbmNlX2RpZ2VzdCI6bnVsbCwibWV0aG9kIjpudWxsLCJvYnNlcnZlZF9hdCI6bnVsbCwib3V0Y29tZSI6Im5vdF9hc3Nlc3NlZCJ9LCJrZXlfcG9zc2Vzc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5YzQ4NzViZTUzMDE4NzhkMWJkOWU1NmYxNDc0OGNlMGMxNTg3ZGI2M2QzM2Q0OGExMGI3ODBlYzEyYzRkYjI2In0sIm1ldGhvZCI6InB1Ymxpc2hlci1ldmlkZW5jZS1qd3MtdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5Niwib3V0Y29tZSI6InBhc3MifX0sImVucm9sbG1lbnRfaWQiOiI0NDQ0NDQ0NC00NDQ0LTQ0NDQtODQ0NC00NDQ0NDQ0NDQ0NDUiLCJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5YzQ4NzViZTUzMDE4NzhkMWJkOWU1NmYxNDc0OGNlMGMxNTg3ZGI2M2QzM2Q0OGExMGI3ODBlYzEyYzRkYjI2In0sImV4cGlyZXNfYXQiOjE3OTM3OTQyMDAsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTksImlzc3VlciI6Imh0dHBzOi8vdmVyaWZpZXIuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwib2JzZXJ2ZWRfZnJvbSI6MTc5Mzc5MzU5MCwib2JzZXJ2ZWRfdW50aWwiOjE3OTM3OTM1OTgsInBvbGljeSI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIxOGEyYjQ2MDhmMTNmNmQyOGEyNDM5MjIyNzMxZWYwZTM3ZGZhYmQ2NGVlODQwYTc3OWVkOWY2MGYyYjJiMmYzIn0sImlkIjoicHVibGlzaGVyLWRlY2xhcmF0aW9uLXYxIiwidmVyc2lvbiI6MX0sInJlYXNvbl9jb2RlcyI6W10sInJlc3VsdCI6InNhdGlzZmllZCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50LXYxIn0.ngp0BbpYM-Z0DSVcb1YfY_0lbZ_ZIvVTQovw7ygJM4qm2dJ9t2gVBhNdmM-JM4h78_9j1eUO4KPanpHewcWGBg", + "compact_sha256": { + "algorithm": "sha256", + "value": "4b72d2696c53eea12bb1430974ed67c396f2439790edabf591260c085e060961" + }, + "evidence_source": "collected_second_site_evidence" + }, + "collected_second_site_evidence": { + "protected": { + "alg": "Ed25519", + "kid": "WeVHjIQTOLKu-P74oC-WL-MNn_sLM5CvrzuMjDFC3-s", + "typ": "ts-publisher-evidence+jws" + }, + "payload": { + "schema": "ts-publisher-evidence-v1", + "binding": { + "schema": "ts-publisher-binding-v1", + "host": "sport.example.net", + "publisher_id": "owner.example.com", + "operator_id": null, + "deployment_id": "22222222-2222-4222-8222-222222222223", + "environment": "production", + "platform": { + "provider": "fastly", + "service_id": "fictional-sport-service", + "service_version": 42, + "staging": false + }, + "attestation_key": { + "kid": "WeVHjIQTOLKu-P74oC-WL-MNn_sLM5CvrzuMjDFC3-s", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "JUO5L_EJVRFHatyDadtt3JM2ZaEZeN2hQE7hBmypVZ0" + }, + "not_before": 1793790000, + "not_after": 1793880000 + }, + "configuration": { + "schema": "ts-publisher-identity-v1", + "digest": { + "algorithm": "sha256", + "value": "223be82747c7f51f5ef82e73533fa7c2fb8fabf8838a6a50fdd91ac349c181ab" + }, + "projection": { + "schema": "ts-publisher-identity-v1", + "host": "sport.example.net", + "publisher_id": "owner.example.com", + "operator_id": null, + "deployment_id": "22222222-2222-4222-8222-222222222223", + "environment": "production" + } + }, + "extensions": {} + }, + "binding_id": "934f0e72cd3794a408b6a6af99ca4d82cc014ff3c005baaffd2df2afbe22ec71", + "nonce": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8", + "issued_at": 1793793595, + "expires_at": 1793793655, + "build_report": { + "declared_class": "custom", + "source_commit": null, + "package_version": null, + "target": null, + "variant": null + }, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiV2VWSGpJUVRPTEt1LVA3NG9DLVdMLU1Obl9zTE01Q3ZyenVNakRGQzMtcyIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ", + "payload_segment": "eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJKVU81TF9FSlZSRkhhdHlEYWR0dDNKTTJaYUVaZU4yaFFFN2hCbXlwVlowIn0sImtpZCI6IldlVkhqSVFUT0xLdS1QNzRvQy1XTC1NTm5fc0xNNUN2cnp1TWpERkMzLXMiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjIyM2JlODI3NDdjN2Y1MWY1ZWY4MmU3MzUzM2ZhN2MyZmI4ZmFiZjg4MzhhNmE1MGZkZDkxYWMzNDljMTgxYWIifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIzIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6InNwb3J0LmV4YW1wbGUubmV0Iiwib3BlcmF0b3JfaWQiOm51bGwsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIzIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJzcG9ydC5leGFtcGxlLm5ldCIsIm9wZXJhdG9yX2lkIjpudWxsLCJwbGF0Zm9ybSI6eyJwcm92aWRlciI6ImZhc3RseSIsInNlcnZpY2VfaWQiOiJmaWN0aW9uYWwtc3BvcnQtc2VydmljZSIsInNlcnZpY2VfdmVyc2lvbiI6NDIsInN0YWdpbmciOmZhbHNlfSwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYmluZGluZy12MSJ9LCJiaW5kaW5nX2lkIjoiOTM0ZjBlNzJjZDM3OTRhNDA4YjZhNmFmOTljYTRkODJjYzAxNGZmM2MwMDViYWFmZmQyZGYyYWZiZTIyZWM3MSIsImJ1aWxkX3JlcG9ydCI6eyJkZWNsYXJlZF9jbGFzcyI6ImN1c3RvbSIsInBhY2thZ2VfdmVyc2lvbiI6bnVsbCwic291cmNlX2NvbW1pdCI6bnVsbCwidGFyZ2V0IjpudWxsLCJ2YXJpYW50IjpudWxsfSwiZXhwaXJlc19hdCI6MTc5Mzc5MzY1NSwiZXh0ZW5zaW9ucyI6e30sImlzc3VlZF9hdCI6MTc5Mzc5MzU5NSwibm9uY2UiOiJBQUVDQXdRRkJnY0lDUW9MREEwT0R4QVJFaE1VRlJZWEdCa2FHeHdkSGg4Iiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWV2aWRlbmNlLXYxIn0", + "signature_segment": "jPiZ5Yzlf-DOhMu5YgNtVyzPmrlKHJuyfuza_3clpRBVU0H81AUXFJAc2rmY6QFnHx217CpUn9kapjzr5vhLDQ", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiV2VWSGpJUVRPTEt1LVA3NG9DLVdMLU1Obl9zTE01Q3ZyenVNakRGQzMtcyIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJKVU81TF9FSlZSRkhhdHlEYWR0dDNKTTJaYUVaZU4yaFFFN2hCbXlwVlowIn0sImtpZCI6IldlVkhqSVFUT0xLdS1QNzRvQy1XTC1NTm5fc0xNNUN2cnp1TWpERkMzLXMiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjIyM2JlODI3NDdjN2Y1MWY1ZWY4MmU3MzUzM2ZhN2MyZmI4ZmFiZjg4MzhhNmE1MGZkZDkxYWMzNDljMTgxYWIifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIzIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6InNwb3J0LmV4YW1wbGUubmV0Iiwib3BlcmF0b3JfaWQiOm51bGwsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIzIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJzcG9ydC5leGFtcGxlLm5ldCIsIm9wZXJhdG9yX2lkIjpudWxsLCJwbGF0Zm9ybSI6eyJwcm92aWRlciI6ImZhc3RseSIsInNlcnZpY2VfaWQiOiJmaWN0aW9uYWwtc3BvcnQtc2VydmljZSIsInNlcnZpY2VfdmVyc2lvbiI6NDIsInN0YWdpbmciOmZhbHNlfSwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYmluZGluZy12MSJ9LCJiaW5kaW5nX2lkIjoiOTM0ZjBlNzJjZDM3OTRhNDA4YjZhNmFmOTljYTRkODJjYzAxNGZmM2MwMDViYWFmZmQyZGYyYWZiZTIyZWM3MSIsImJ1aWxkX3JlcG9ydCI6eyJkZWNsYXJlZF9jbGFzcyI6ImN1c3RvbSIsInBhY2thZ2VfdmVyc2lvbiI6bnVsbCwic291cmNlX2NvbW1pdCI6bnVsbCwidGFyZ2V0IjpudWxsLCJ2YXJpYW50IjpudWxsfSwiZXhwaXJlc19hdCI6MTc5Mzc5MzY1NSwiZXh0ZW5zaW9ucyI6e30sImlzc3VlZF9hdCI6MTc5Mzc5MzU5NSwibm9uY2UiOiJBQUVDQXdRRkJnY0lDUW9MREEwT0R4QVJFaE1VRlJZWEdCa2FHeHdkSGg4Iiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWV2aWRlbmNlLXYxIn0.jPiZ5Yzlf-DOhMu5YgNtVyzPmrlKHJuyfuza_3clpRBVU0H81AUXFJAc2rmY6QFnHx217CpUn9kapjzr5vhLDQ", + "compact_sha256": { + "algorithm": "sha256", + "value": "9c4875be5301878d1bd9e56f14748ce0c1587db63d33d48a10b780ec12c4db26" + } + }, + "status_retired_key": { + "protected": { + "alg": "Ed25519", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "typ": "ts-publisher-status+jws" + }, + "payload": { + "schema": "ts-publisher-status-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "assessment_id": "33333333-3333-4333-8333-000000000001", + "binding_id": "57ab4dabb3f84afaa8c7064475e41b64b67640143213bf01739f3e5dd23a27bf", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "sequence": 1, + "assessment_status": "current", + "enrollment_status": "active", + "publisher_key_status": "retired", + "reason_codes": ["key_unauthorized"], + "issued_at": 1793793599, + "expires_at": 1793793659, + "extensions": {} + }, + "protected_segment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0", + "payload_segment": "eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoicmV0aXJlZCIsInJlYXNvbl9jb2RlcyI6WyJrZXlfdW5hdXRob3JpemVkIl0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1zdGF0dXMtdjEiLCJzZXF1ZW5jZSI6MX0", + "signature_segment": "VbTONKw7JRleOIEZJKWLD-iXUHjsX5vzqG0je4u62QAi_P55dUtddBq5xOVqpnVmdQb3w6wlszsNrO1WrB8VAA", + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoicmV0aXJlZCIsInJlYXNvbl9jb2RlcyI6WyJrZXlfdW5hdXRob3JpemVkIl0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1zdGF0dXMtdjEiLCJzZXF1ZW5jZSI6MX0.VbTONKw7JRleOIEZJKWLD-iXUHjsX5vzqG0je4u62QAi_P55dUtddBq5xOVqpnVmdQb3w6wlszsNrO1WrB8VAA", + "compact_sha256": { + "algorithm": "sha256", + "value": "ba8f5eb3f13b264772eed18bd611866ed4bb3d2b1ae5edfb388173c9caf817e3" + } + } + }, + "negative_encodings": { + "tampered_status": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ.-JyNTGmBNu1GgVo-QmKrS39bwT6z3lwU9WKRwdLiOrgLTdgZpK2ltOeieEOm2cNgQKi5Du0VHj5slZbqsWieCg" + }, + "legacy_algorithm": { + "compact_jws": "eyJhbGciOiJFZERTQSIsImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJ0eXAiOiJ0cy1wdWJsaXNoZXItZXZpZGVuY2UrandzIn0.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.WzMgROVxwhD5CsBII8QgUO2J94IOxZxMCmS-1es3_flG3LSQzTZvB1P2WK2nc9C9Lhdq5nJwlZ6Mf30zvMMYCw" + }, + "wrong_message_type": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.1HMlG4NxuPMdsZ5Nfgr0iifVICW6D-BgaYfDFBOT0th7G9FBrKhoaGLV72GiZ7XMfCVOBprhYRBoTC1695B2Dw" + }, + "unknown_member": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSIsInVuZXhwZWN0ZWQiOnRydWV9.Bx9Nma1hBFTGUczC6-FzCAX7EdkQf2JtAA3LvlGOoJEoDsmQiankNAroWUCxsHzMNQrqi5MLOxVTRu98nH6HCg" + }, + "signature_tamper": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.Nnjumn0wUj1CHZ0dQJdbpgrCp6NGM5086cp9aw29wb-9lImChLBtY2ifuKlphRy8OpzkBjqdAEh5lrtmDvR0BQ" + }, + "non_canonical_s": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.N3jumn0wUj1CHZ0dQJdbpgrCp6NGM5086cp9aw29wb-qaH_fnhOAuz48sExIf_vQOpzkBjqdAEh5lrtmDvR0FQ" + }, + "small_order_r": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABq4V5zDWEdldX0KKi-XVbfgIG84skX85myWboPbt5bDg" + }, + "non_canonical_r": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.7v_______________________________________3-1uGRL-xibN0eKBFlGW8iqro4fLgzCr1xGiiHEVNVMBg" + }, + "fingerprint_without_active_version": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDA1IiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJmaW5nZXJwcmludF9vbmx5IiwiY292ZXJhZ2UiOiJwcm92aWRlcl9wYWNrYWdlX2ZpbmdlcnByaW50IiwibWV0aG9kIjoiZmljdGlvbmFsLWZhc3RseS1maW5nZXJwcmludC12MSIsInByb3ZlbmFuY2UiOiJ1bmtub3duIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6ImFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhIiwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI0ZGRiZDU3ZTI4NjY5NDNjNmZlNGNiZTdhZTQxMWI5YzFmZmIzNzhhMWU4ZTcxY2QwODQ0NDc5MTY5ODJmNGQ2In0sIm1ldGhvZCI6ImZpY3Rpb25hbC1mYXN0bHktaG9zdC1zZXJ2aWNlLWJpbmRpbmctdjEiLCJvYnNlcnZlZF9hdCI6MTc5Mzc5MzU5OCwib3V0Y29tZSI6InBhc3MifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.XiNyCvLhEuLSwLT3T80k9I4kXPwfKpBY1f55szcfRavwFXn33gpzgl9rfzXx94wOlKR-wptRkGY_1U6D58uSAw" + }, + "contradictory_environment": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjp0cnVlfSwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYmluZGluZy12MSJ9LCJiaW5kaW5nX2lkIjoiYzQyNTZjOGFjZDZmNzc3MjkxMGJjN2Y5OTQwM2RkODY2NDE2Y2E1NzE4Y2ZjYWJmOGNhYmY3MzdhNTE5MTM0OCIsImJ1aWxkX3JlcG9ydCI6eyJkZWNsYXJlZF9jbGFzcyI6ImN1c3RvbSIsInBhY2thZ2VfdmVyc2lvbiI6bnVsbCwic291cmNlX2NvbW1pdCI6bnVsbCwidGFyZ2V0IjpudWxsLCJ2YXJpYW50IjpudWxsfSwiZXhwaXJlc19hdCI6MTc5Mzc5MzY1NSwiZXh0ZW5zaW9ucyI6e30sImlzc3VlZF9hdCI6MTc5Mzc5MzU5NSwibm9uY2UiOiJJQ0VpSXlRbEppY29LU29yTEMwdUx6QXhNak0wTlRZM09EazZPenc5UGo4Iiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWV2aWRlbmNlLXYxIn0.n8LuQ7tsGIkVv9DFW0AXTRH0mUpQ1DAfrphHXx8LE9bdx2YAcQ-ZWgdWJ9shcflEH4kY0w1kp8G6GTD-QX6QAg" + }, + "evidence_lifetime_exceeded": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTYsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.DOiHYHEsRNKW_OLOmp8qDdrxEsEfMO44VdJpVebaZ0ZmTg_KenamMlVm5IkZxln4EVyRVL9FtcsmLYmtoaLeDg" + }, + "evidence_key_expires_first": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM3OTM2NTQsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjExMDkwYzg0NDUwYTMwYThkYmMyMTI5MDVjZjczZDU3YjQ5YWVkNmJlMmY5YjhkNjNhMzNlZGE3MDg1ZTdhZjgiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.zLtIj645ZLRnGwcbVWkvK8kxpUKsezwhkPWTcTJDO8YjQouJ5bveUuVl6JfmjhoL8ax59XNLSYU03MUStJL1Bw" + }, + "evidence_key_not_yet_valid": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTM1OTZ9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjhhYTdkMDgwNTE4YWIxNTY2NjdmY2I5YjYwYjFjMWQwZGY2MDA2YmYzNWU3OTE5NGNhOWMyNmEzMzdlYTk0M2MiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJpc3N1ZWRfYXQiOjE3OTM3OTM1OTUsIm5vbmNlIjoiSUNFaUl5UWxKaWNvS1NvckxDMHVMekF4TWpNME5UWTNPRGs2T3p3OVBqOCIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZS12MSJ9.GZqhDW9q4NHfNu_sU1xE0Hd6gBiiVqYM5MPJjAq5H1rO1SVvYQJ83Kf3YqCFCepU-02SNzjyfaS6hxH7GFtVDg" + }, + "status_lifetime_exceeded": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjYwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ.ES2n4-MPbdMaKPPEivYxkkUb6gwqkZp5VeanMJbvout7wytM0PmFqiCeMBFI1KMaLasPjCSA3HsqFjQiAayUAg" + }, + "mismatched_key_possession_digest": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.CTsLzfWWwOCcxCu_ES0OqajNekkMEKKWyw7URcCwGcxUJ6QSqeBrldvA4d_glEwFcDhoZ4iT15NYkTEtKDPTBg" + }, + "mismatched_configuration_digest": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.hFyu2bMS5NzdK2i1VRs6WQZCNGTYc5JUtWo6NU8YHt5VZJL9g1N1yhY954-Q3kTZfZJHLbYNXpHhZw9HQWDdBA" + }, + "reversed_observation_interval": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTksIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.X6QkOGcUd-ZS-n_F4_tSxhOPz9R1e2_3GW8qeFegN6RK-6a_-16dKcqelh4Nr4fRXyUgpU2BoHIGLvPrpJr9Ag" + }, + "observation_after_issuance": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNjAwLCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.LFqnfEN7H_6PUuvih1uTLdzgvZdPomYP1u6pvMY-INxyBrXs9DrJBR_xhBeK7SaFFa2Y3xtkGPgIGWatBcguBA" + }, + "late_assessment_issuance": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1MzAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTM4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.EHLKX9Bp4maXSo4IGSmLGwnZv-Mgld6r89n51lah1EB3oq2vEMrK0YlTGZ53s1L65J-9JZ9Aoubu_Bx5JkWqAw" + }, + "assessment_lifetime_exceeded": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0NDkxLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.62eLOolVaZE3V6AfSEmhv-nuU6W3GyGDt8EQFANumdIls-cXYUtkXbk-Ua7_IY6TK_U2_reWfOfrva6GdOEzAQ" + }, + "duplicate_member_top_level": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sImRlcGxveW1lbnRfaWQiOiIyMjIyMjIyMi0yMjIyLTQyMjItODIyMi0yMjIyMjIyMjIyMjIiLCJlbnZpcm9ubWVudCI6InByb2R1Y3Rpb24iLCJleHRlbnNpb25zIjp7fSwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJvcGVyYXRvcl9pZCI6Imh0dHBzOi8vb3BlcmF0b3IuZXhhbXBsZS5jb20vcHVibGlzaGVyLWF0dGVzdGF0aW9uIiwicGxhdGZvcm0iOnsicHJvdmlkZXIiOiJmYXN0bHkiLCJzZXJ2aWNlX2lkIjoiZmljdGlvbmFsLXNlcnZpY2UiLCJzZXJ2aWNlX3ZlcnNpb24iOjQyLCJzdGFnaW5nIjpmYWxzZX0sInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWJpbmRpbmctdjEifSwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJidWlsZF9yZXBvcnQiOnsiZGVjbGFyZWRfY2xhc3MiOiJjdXN0b20iLCJwYWNrYWdlX3ZlcnNpb24iOm51bGwsInNvdXJjZV9jb21taXQiOm51bGwsInRhcmdldCI6bnVsbCwidmFyaWFudCI6bnVsbH0sImV4cGlyZXNfYXQiOjE3OTM3OTM2NTUsImV4dGVuc2lvbnMiOnt9LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk1LCJub25jZSI6IklDRWlJeVFsSmljb0tTb3JMQzB1THpBeE1qTTBOVFkzT0RrNk96dzlQajgiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItZXZpZGVuY2UtdjEifQ.AD_CmkiUtN3zA5ElL4c8eizO1OcxlfPuZkORwlSSVqaw5d3CaoSloX9oQQW43awttaCfgCR7EpbyFdG0PV_NDg" + }, + "duplicate_member_nested": { + "compact_jws": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInR5cCI6InRzLXB1Ymxpc2hlci1ldmlkZW5jZStqd3MifQ.eyJiaW5kaW5nIjp7ImF0dGVzdGF0aW9uX2tleSI6eyJqd2siOnsiY3J2IjoiRWQyNTUxOSIsImt0eSI6Ik9LUCIsIngiOiJBNkVIdl9QT0VMNGRjTjBZNTB2QW1XZmsxakNicFExZkhkeUdaQkpWTWJnIn0sImtpZCI6IjFJRzJ0TUg3SjJ3Ykpabk9mOExKelFpdEtmN0xNdm9BRWxzdURNVk01NFkiLCJub3RfYWZ0ZXIiOjE3OTM4ODAwMDAsIm5vdF9iZWZvcmUiOjE3OTM3OTAwMDB9LCJjb25maWd1cmF0aW9uIjp7ImRpZ2VzdCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImVkMmQ4YWQyMWY3N2FjYmJjZGM0NmU0NGE4NDI1NTgxNWYxOTA0MDVjODk5MTIwNzQyYTk1MDQ5MGI2YzkyM2IifSwicHJvamVjdGlvbiI6eyJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiaG9zdCI6Im5ld3MuZXhhbXBsZS5jb20iLCJob3N0IjoibmV3cy5leGFtcGxlLmNvbSIsIm9wZXJhdG9yX2lkIjoiaHR0cHM6Ly9vcGVyYXRvci5leGFtcGxlLmNvbS9wdWJsaXNoZXItYXR0ZXN0YXRpb24iLCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItaWRlbnRpdHktdjEifSwiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImV4dGVuc2lvbnMiOnt9LCJob3N0IjoibmV3cy5leGFtcGxlLmNvbSIsIm9wZXJhdG9yX2lkIjoiaHR0cHM6Ly9vcGVyYXRvci5leGFtcGxlLmNvbS9wdWJsaXNoZXItYXR0ZXN0YXRpb24iLCJwbGF0Zm9ybSI6eyJwcm92aWRlciI6ImZhc3RseSIsInNlcnZpY2VfaWQiOiJmaWN0aW9uYWwtc2VydmljZSIsInNlcnZpY2VfdmVyc2lvbiI6NDIsInN0YWdpbmciOmZhbHNlfSwicHVibGlzaGVyX2lkIjoib3duZXIuZXhhbXBsZS5jb20iLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYmluZGluZy12MSJ9LCJiaW5kaW5nX2lkIjoiNTdhYjRkYWJiM2Y4NGFmYWE4YzcwNjQ0NzVlNDFiNjRiNjc2NDAxNDMyMTNiZjAxNzM5ZjNlNWRkMjNhMjdiZiIsImJ1aWxkX3JlcG9ydCI6eyJkZWNsYXJlZF9jbGFzcyI6ImN1c3RvbSIsInBhY2thZ2VfdmVyc2lvbiI6bnVsbCwic291cmNlX2NvbW1pdCI6bnVsbCwidGFyZ2V0IjpudWxsLCJ2YXJpYW50IjpudWxsfSwiZXhwaXJlc19hdCI6MTc5Mzc5MzY1NSwiZXh0ZW5zaW9ucyI6e30sImlzc3VlZF9hdCI6MTc5Mzc5MzU5NSwibm9uY2UiOiJJQ0VpSXlRbEppY29LU29yTEMwdUx6QXhNak0wTlRZM09EazZPenc5UGo4Iiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWV2aWRlbmNlLXYxIn0.jGaxrVw3pR53DsBd4KIUvKhGGix7aCKe7cbYJFJ_sBuALfl4rvMNlrGLT6yEaow2dDgE1JyMEN7irSqMw11FDA" + } + }, + "unsigned_objects": { + "key_discovery": { + "schema": "ts-publisher-key-discovery-v1", + "host": "news.example.com", + "keys": [ + { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000 + } + ], + "verifiers": ["https://verifier.example.com/publisher-attestation"] + }, + "assessment_list": { + "schema": "ts-publisher-assessment-list-v1", + "host": "news.example.com", + "environment": "production", + "assessments": [ + "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.aMOuwvxoym69MsKKdF5MnrIP9Ag-AhLzAkUaMmA389PEcGDQBO9uBR-byjjOMnFzaid3ka7d66KD2gsH_5Z6Bg" + ] + }, + "enrollment_challenge": { + "schema": "ts-publisher-enrollment-challenge-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://operator.example.com/publisher-attestation", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "provider": "fastly", + "service_id": "fictional-service", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "nonce": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8", + "issued_at": 1793793600, + "expires_at": 1793794200 + }, + "key_metadata": { + "schema": "ts-publisher-key-metadata-v1", + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + }, + "not_before": 1793790000, + "not_after": 1793880000, + "secret_name": "ts-publisher-example-key" + }, + "self_operated_enrollment_challenge": { + "schema": "ts-publisher-enrollment-challenge-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": null, + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "provider": "fastly", + "service_id": "fictional-service", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "nonce": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8", + "issued_at": 1793793600, + "expires_at": 1793794200 + }, + "key_history": { + "schema": "ts-publisher-key-history-v1", + "host": "news.example.com", + "environment": "production", + "entries": [ + { + "assessment": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1hc3Nlc3NtZW50K2p3cyJ9.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYmluZGluZyI6eyJhdHRlc3RhdGlvbl9rZXkiOnsiandrIjp7ImNydiI6IkVkMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiQTZFSHZfUE9FTDRkY04wWTUwdkFtV2ZrMWpDYnBRMWZIZHlHWkJKVk1iZyJ9LCJraWQiOiIxSUcydE1IN0oyd2JKWm5PZjhMSnpRaXRLZjdMTXZvQUVsc3VETVZNNTRZIiwibm90X2FmdGVyIjoxNzkzODgwMDAwLCJub3RfYmVmb3JlIjoxNzkzNzkwMDAwfSwiY29uZmlndXJhdGlvbiI6eyJkaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sInByb2plY3Rpb24iOnsiZGVwbG95bWVudF9pZCI6IjIyMjIyMjIyLTIyMjItNDIyMi04MjIyLTIyMjIyMjIyMjIyMiIsImVudmlyb25tZW50IjoicHJvZHVjdGlvbiIsImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9pZCI6Im93bmVyLmV4YW1wbGUuY29tIiwic2NoZW1hIjoidHMtcHVibGlzaGVyLWlkZW50aXR5LXYxIn0sInNjaGVtYSI6InRzLXB1Ymxpc2hlci1pZGVudGl0eS12MSJ9LCJkZXBsb3ltZW50X2lkIjoiMjIyMjIyMjItMjIyMi00MjIyLTgyMjItMjIyMjIyMjIyMjIyIiwiZW52aXJvbm1lbnQiOiJwcm9kdWN0aW9uIiwiZXh0ZW5zaW9ucyI6e30sImhvc3QiOiJuZXdzLmV4YW1wbGUuY29tIiwib3BlcmF0b3JfaWQiOiJodHRwczovL29wZXJhdG9yLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInBsYXRmb3JtIjp7InByb3ZpZGVyIjoiZmFzdGx5Iiwic2VydmljZV9pZCI6ImZpY3Rpb25hbC1zZXJ2aWNlIiwic2VydmljZV92ZXJzaW9uIjo0Miwic3RhZ2luZyI6ZmFsc2V9LCJwdWJsaXNoZXJfaWQiOiJvd25lci5leGFtcGxlLmNvbSIsInNjaGVtYSI6InRzLXB1Ymxpc2hlci1iaW5kaW5nLXYxIn0sImJpbmRpbmdfaWQiOiI1N2FiNGRhYmIzZjg0YWZhYThjNzA2NDQ3NWU0MWI2NGI2NzY0MDE0MzIxM2JmMDE3MzlmM2U1ZGQyM2EyN2JmIiwiYnVpbGQiOnsiYXJ0aWZhY3Rfc2hhMjU2IjpudWxsLCJhc3N1cmFuY2UiOiJkZWNsYXJlZCIsImNvdmVyYWdlIjoiZGVjbGFyYXRpb25fb25seSIsIm1ldGhvZCI6InB1Ymxpc2hlci1jdXN0b20tZGVjbGFyYXRpb24tdjEiLCJwcm92ZW5hbmNlIjoiY3VzdG9tIiwicHJvdmlkZXJfZmlsZXNfaGFzaCI6bnVsbCwicmVsZWFzZSI6bnVsbH0sImNoZWNrcyI6eyJhY3RpdmVfdmVyc2lvbiI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwiY29uZmlndXJhdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlZDJkOGFkMjFmNzdhY2JiY2RjNDZlNDRhODQyNTU4MTVmMTkwNDA1Yzg5OTEyMDc0MmE5NTA0OTBiNmM5MjNiIn0sIm1ldGhvZCI6InB1YmxpYy1pZGVudGl0eS1wcm9qZWN0aW9uLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn0sImhvc3RfYXV0aG9yaXphdGlvbiI6eyJldmlkZW5jZV9kaWdlc3QiOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYzRjNDE4ZDRiODQ4NzdjZGFhMzgxN2QxMzg3NWRhZjlhZGZmMTVjMDM4ZDFmOWVlMmZiZmQ0ZTVkOWYxY2I1In0sIm1ldGhvZCI6ImRucy1rZXktY29udHJvbC12MSIsIm9ic2VydmVkX2F0IjoxNzkzNzkzNTk2LCJvdXRjb21lIjoicGFzcyJ9LCJob3N0X3NlcnZpY2VfYmluZGluZyI6eyJldmlkZW5jZV9kaWdlc3QiOm51bGwsIm1ldGhvZCI6bnVsbCwib2JzZXJ2ZWRfYXQiOm51bGwsIm91dGNvbWUiOiJub3RfYXNzZXNzZWQifSwia2V5X3Bvc3Nlc3Npb24iOnsiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJtZXRob2QiOiJwdWJsaXNoZXItZXZpZGVuY2UtandzLXYxIiwib2JzZXJ2ZWRfYXQiOjE3OTM3OTM1OTYsIm91dGNvbWUiOiJwYXNzIn19LCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZXZpZGVuY2VfZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMjE0ODBkMWI1MzZlYjRiYmRmNDk4MGUzZmQ4NGEwZmUyODY2NDNjNTRhODEwM2U1OTAzZjViY2U1NzYzYTU5MiJ9LCJleHBpcmVzX2F0IjoxNzkzNzk0MjAwLCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsIm9ic2VydmVkX2Zyb20iOjE3OTM3OTM1OTAsIm9ic2VydmVkX3VudGlsIjoxNzkzNzkzNTk4LCJwb2xpY3kiOnsiZGlnZXN0Ijp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiMThhMmI0NjA4ZjEzZjZkMjhhMjQzOTIyMjczMWVmMGUzN2RmYWJkNjRlZTg0MGE3NzllZDlmNjBmMmIyYjJmMyJ9LCJpZCI6InB1Ymxpc2hlci1kZWNsYXJhdGlvbi12MSIsInZlcnNpb24iOjF9LCJyZWFzb25fY29kZXMiOltdLCJyZXN1bHQiOiJzYXRpc2ZpZWQiLCJzY2hlbWEiOiJ0cy1wdWJsaXNoZXItYXNzZXNzbWVudC12MSJ9.aMOuwvxoym69MsKKdF5MnrIP9Ag-AhLzAkUaMmA389PEcGDQBO9uBR-byjjOMnFzaid3ka7d66KD2gsH_5Z6Bg", + "status": "eyJhbGciOiJFZDI1NTE5Iiwia2lkIjoiQWtYSVpGem9uYjU5Wm1Hd3lLZ2kzSDNCd01pNmFtZXZxZEtRaUxaaGR0YyIsInR5cCI6InRzLXB1Ymxpc2hlci1zdGF0dXMrandzIn0.eyJhc3Nlc3NtZW50X2lkIjoiMzMzMzMzMzMtMzMzMy00MzMzLTgzMzMtMDAwMDAwMDAwMDAxIiwiYXNzZXNzbWVudF9zdGF0dXMiOiJjdXJyZW50IiwiYmluZGluZ19pZCI6IjU3YWI0ZGFiYjNmODRhZmFhOGM3MDY0NDc1ZTQxYjY0YjY3NjQwMTQzMjEzYmYwMTczOWYzZTVkZDIzYTI3YmYiLCJlbnJvbGxtZW50X2lkIjoiNDQ0NDQ0NDQtNDQ0NC00NDQ0LTg0NDQtNDQ0NDQ0NDQ0NDQ0IiwiZW5yb2xsbWVudF9zdGF0dXMiOiJhY3RpdmUiLCJleHBpcmVzX2F0IjoxNzkzNzkzNjU5LCJleHRlbnNpb25zIjp7fSwiaXNzdWVkX2F0IjoxNzkzNzkzNTk5LCJpc3N1ZXIiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUuY29tL3B1Ymxpc2hlci1hdHRlc3RhdGlvbiIsInB1Ymxpc2hlcl9rZXlfa2lkIjoiMUlHMnRNSDdKMndiSlpuT2Y4TEp6UWl0S2Y3TE12b0FFbHN1RE1WTTU0WSIsInB1Ymxpc2hlcl9rZXlfc3RhdHVzIjoiYWN0aXZlIiwicmVhc29uX2NvZGVzIjpbXSwic2NoZW1hIjoidHMtcHVibGlzaGVyLXN0YXR1cy12MSIsInNlcXVlbmNlIjoxfQ.-JyNTGmBNu1GgVo-QmKrS39bwT6z3lwU9WKRwdLiOrgLTdgZpK2ltOeieEOm2cNgQKi5Du0VHj5slZbqsWieCw" + } + ], + "next_cursor": null + }, + "policy_publisher_declaration_v1": { + "schema": "ts-publisher-policy-v1", + "id": "publisher-declaration-v1", + "version": 1, + "checks": { + "host_authorization": { + "requirement": "pass", + "methods": ["dns-key-control-v1"] + }, + "key_possession": { + "requirement": "pass", + "methods": ["publisher-evidence-jws-v1"] + }, + "host_service_binding": { + "requirement": "pass_or_not_assessed", + "methods": null + }, + "active_version": { + "requirement": "pass_or_not_assessed", + "methods": null + }, + "configuration": { + "requirement": "pass", + "methods": ["public-identity-projection-v1"] + } + }, + "build": { + "assurance": ["declared", "fingerprint_only"], + "declared_methods": [ + "publisher-custom-declaration-v1", + "publisher-unspecified-declaration-v1" + ], + "fingerprint_methods": null, + "artifact_methods": [] + }, + "max_assessment_lifetime_seconds": 900, + "extensions": {} + }, + "policy_fictional_official_profile_v1": { + "schema": "ts-publisher-policy-v1", + "id": "fictional-official-profile-v1", + "version": 1, + "checks": { + "host_authorization": { + "requirement": "pass", + "methods": ["dns-key-control-v1"] + }, + "key_possession": { + "requirement": "pass", + "methods": ["publisher-evidence-jws-v1"] + }, + "host_service_binding": { + "requirement": "pass", + "methods": ["fictional-fastly-host-service-binding-v1"] + }, + "active_version": { + "requirement": "pass", + "methods": ["fictional-fastly-active-version-v1"] + }, + "configuration": { + "requirement": "pass", + "methods": ["public-identity-projection-v1"] + } + }, + "build": { + "assurance": ["artifact_bound"], + "declared_methods": [], + "fingerprint_methods": [], + "artifact_methods": ["fictional-fastly-artifact-binding-v1"] + }, + "max_assessment_lifetime_seconds": 900, + "extensions": {} + }, + "verifier_metadata": { + "schema": "ts-publisher-verifier-metadata-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "publication_bases": [ + "https://verifier.example.com/publisher-attestation" + ], + "keys": [ + { + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "Kay64UG8yvCyLhqU000LxzYeUm0L_hLIl5S8kyKWbdc" + }, + "not_before": 1793790000, + "not_after": 1793880000 + } + ], + "policies": [ + { + "id": "publisher-declaration-v1", + "version": 1, + "digest": { + "algorithm": "sha256", + "value": "18a2b4608f13f6d28a2439222731ef0e37dfabd64ee840a779ed9f60f2b2b2f3" + } + } + ], + "live_status": true + }, + "error_example": { + "schema": "ts-publisher-error-v1", + "error": "unknown_kid" + } + }, + "enrollment_txt_value": "ts-publisher-v1=2OSe6RGqZSvgA5LJdI0PMBLu-jWtwDLLcGbdHlZ19eE", + "cases": [ + { + "id": "evidence-positive", + "perspective": "consumer_acceptance", + "signed_object": "evidence", + "context_overrides": {}, + "expected": { + "accept": true, + "stage": 7 + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-evidence+jws" + }, + { + "id": "assessment-positive", + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "context_overrides": {}, + "expected": { + "accept": true, + "stage": 11 + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "status-positive", + "perspective": "consumer_acceptance", + "signed_object": "status", + "context_overrides": {}, + "expected": { + "accept": true, + "stage": 11 + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-status+jws" + }, + { + "id": "legacy_algorithm", + "perspective": "consumer_acceptance", + "negative_encoding": "legacy_algorithm", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "signature_invalid" + }, + "pipeline": "consumer" + }, + { + "id": "signature_tamper", + "perspective": "consumer_acceptance", + "negative_encoding": "signature_tamper", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 3, + "reason_code": "signature_invalid" + }, + "pipeline": "consumer" + }, + { + "id": "wrong_message_type", + "perspective": "consumer_acceptance", + "negative_encoding": "wrong_message_type", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "signature_invalid" + }, + "pipeline": "consumer" + }, + { + "id": "unknown_member", + "perspective": "consumer_acceptance", + "negative_encoding": "unknown_member", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "pipeline": "consumer" + }, + { + "id": "wrong_nonce", + "perspective": "consumer_acceptance", + "signed_object": "evidence", + "context_overrides": { + "nonce": "Hx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQA" + }, + "expected": { + "accept": false, + "stage": 6, + "reason_code": "nonce_mismatch" + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-evidence+jws" + }, + { + "id": "expired_evidence", + "perspective": "consumer_acceptance", + "signed_object": "evidence", + "context_overrides": { + "now": 1793793655 + }, + "expected": { + "accept": false, + "stage": 6, + "reason_code": "stale" + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-evidence+jws" + }, + { + "id": "binding_mismatch", + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "context_overrides": { + "fresh_evidence": "other_binding_evidence" + }, + "expected": { + "accept": false, + "stage": 7, + "reason_code": "binding_mismatch" + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "wrong_host", + "perspective": "consumer_acceptance", + "signed_object": "evidence", + "context_overrides": { + "expected_host": "other.example.com" + }, + "expected": { + "accept": false, + "stage": 7, + "reason_code": "host_mismatch" + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-evidence+jws" + }, + { + "id": "operator_unauthorized", + "perspective": "verifier_issuance", + "signed_object": "assessment", + "context_overrides": { + "operator_authorization": { + "operator_id": "https://operator.example.com/publisher-attestation", + "authorization_record_active": false + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "operator_unauthorized" + }, + "pipeline": "verifier_issuance", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "state_recovery_required", + "perspective": "verifier_issuance", + "signed_object": "status", + "context_overrides": { + "durable_authorization_history_available": false, + "signer_fence_current": false + }, + "expected": { + "accept": false, + "stage": 3, + "reason_code": "state_recovery_required" + }, + "note": "Verifier issuance/publication precondition: stop signing/current status when durable state/fencing is unavailable. This condition is externally supplied operational state, not detectable from a historical signature alone.", + "pipeline": "verifier_issuance", + "expected_type": "ts-publisher-status+jws" + }, + { + "id": "operator_authorization_expired", + "perspective": "verifier_issuance", + "signed_object": "assessment", + "context_overrides": { + "operator_authorization": { + "operator_id": "https://operator.example.com/publisher-attestation", + "authenticated_account": "fixture-operator-account", + "authorization_record_active": true, + "source": "Synthetic independent exact operator DNS/account proof record; never inferred from publisher host DNS", + "not_before": 1793790000, + "not_after": 1793793599, + "operator_dns_account_proof_verified": true + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "operator_unauthorized" + }, + "pipeline": "verifier_issuance", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "assessment_outlives_operator_authority", + "perspective": "verifier_issuance", + "signed_object": "assessment", + "context_overrides": { + "operator_authorization": { + "operator_id": "https://operator.example.com/publisher-attestation", + "authenticated_account": "fixture-operator-account", + "authorization_record_active": true, + "source": "Synthetic independent exact operator DNS/account proof record; never inferred from publisher host DNS", + "not_before": 1793790000, + "not_after": 1793793610, + "operator_dns_account_proof_verified": true + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "operator_unauthorized" + }, + "pipeline": "verifier_issuance", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "self_operated_nullable_operator", + "perspective": "consumer_acceptance", + "signed_object": "self_operated_evidence", + "context_overrides": { + "operator_authorization": null, + "fresh_evidence": "self_operated_evidence" + }, + "expected": { + "accept": true, + "stage": 7 + }, + "note": "Self-operated; null makes no distinct operator identity claim.", + "pipeline": "consumer", + "expected_type": "ts-publisher-evidence+jws" + }, + { + "id": "satisfied_with_failed_required_check", + "perspective": "consumer_acceptance", + "signed_object": "assessment_required_check_failed", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 8, + "reason_code": "malformed" + }, + "note": "Schema-valid signed object; satisfied is inconsistent with a failed required check.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "assessment_expiry_before_issuance", + "perspective": "consumer_acceptance", + "signed_object": "assessment_reversed_time", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "production_rejects_dev_fixture_method", + "perspective": "consumer_acceptance", + "signed_object": "assessment_dev_fixture_method", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 5, + "reason_code": "policy_untrusted" + }, + "note": "Loopback fixture enrollment exemption applies only in isolated development; forbidden by production trust settings.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "fingerprint_only_declaration_scope", + "perspective": "consumer_acceptance", + "signed_object": "assessment_fingerprint_only", + "context_overrides": { + "trusted_verifier": { + "issuer": "https://verifier.example.com/publisher-attestation", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "not_before": 1793790000, + "not_after": 1793880000, + "allowed_policies": ["publisher-declaration-v1"], + "allowed_methods": [ + "dns-key-control-v1", + "publisher-evidence-jws-v1", + "public-identity-projection-v1", + "publisher-custom-declaration-v1", + "fictional-fastly-fingerprint-v1", + "fictional-fastly-host-service-binding-v1", + "fictional-fastly-active-version-v1" + ] + } + }, + "expected": { + "accept": true, + "stage": 11 + }, + "note": "Synthetic parser/policy exercise only. Fingerprint remains fingerprint_only, never artifact-bound or official qualification.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "official_shape_without_provider_qualification", + "perspective": "consumer_acceptance", + "signed_object": "assessment_official_shaped_unqualified", + "context_overrides": { + "production_provider_qualification": false, + "trusted_verifier": { + "issuer": "https://verifier.example.com/publisher-attestation", + "kid": "AkXIZFzonb59ZmGwyKgi3H3BwMi6amevqdKQiLZhdtc", + "not_before": 1793790000, + "not_after": 1793880000, + "allowed_policies": ["fictional-official-profile-v1"], + "allowed_methods": [ + "dns-key-control-v1", + "publisher-evidence-jws-v1", + "public-identity-projection-v1", + "publisher-custom-declaration-v1", + "fictional-fastly-host-service-binding-v1", + "fictional-fastly-active-version-v1", + "fictional-fastly-artifact-binding-v1" + ], + "pinned_policy_digests": { + "fictional-official-profile-v1": "407594246b35a9f8e390421593f5e7ff5a02bcd992d9a276f698ff8e077b3d33" + }, + "policy_documents": ["policy_fictional_official_profile_v1"] + } + }, + "expected": { + "accept": false, + "stage": 9, + "reason_code": "artifact_binding_unproven" + }, + "note": "Fictional release, method, digests and service; the consumer trusts this fictional policy so the first failing stage is provider qualification. Shape and signature never establish provider byte-recovery qualification or official installed code.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "fingerprint_without_active_version", + "perspective": "consumer_acceptance", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "Signed negative encoding: non-null provider hash without a passing active-version check is structurally invalid.", + "negative_encoding": "fingerprint_without_active_version", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "default_expiry_only_status_unavailable", + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "context_overrides": { + "status_available": false + }, + "expected": { + "accept": true, + "stage": 11 + }, + "note": "Default require_status=false accepts while assessment remains valid under the isolated synthetic declaration policy.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "optional_live_status_unavailable", + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_available": false + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + }, + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "non_canonical_s", + "perspective": "consumer_acceptance", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 3, + "reason_code": "signature_invalid" + }, + "note": "Signature scalar S replaced by S + L. Non-strict verifiers may accept it; strict verification (parent PA-WIRE) must reject it.", + "negative_encoding": "non_canonical_s", + "expected_type": "ts-publisher-evidence+jws", + "pipeline": "consumer" + }, + { + "id": "small_order_key_discovery", + "perspective": "consumer_acceptance", + "unsigned_object": "small_order_key_discovery", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "Public key is a small-order point. Reject at discovery, enrollment and verification.", + "pipeline": "unsigned_admission" + }, + { + "id": "non_canonical_operator_identifier", + "perspective": "verifier_issuance", + "unsigned_object": "non_canonical_operator_identifier", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 1, + "reason_code": "malformed" + }, + "note": "Uppercase host and trailing slash: not a canonical HTTPS identifier, and not an alias of the canonical form.", + "pipeline": "verifier_issuance" + }, + { + "id": "small_order_r", + "perspective": "consumer_acceptance", + "negative_encoding": "small_order_r", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 3, + "reason_code": "signature_invalid" + }, + "note": "Canonical R encodes the identity point; A is canonical and non-small-order, and S is canonical. Ordinary cofactorless Ed25519 verification may accept this signature; the strict profile rejects small-order R.", + "pipeline": "consumer" + }, + { + "id": "required_live_status_bounds_cache", + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_available": true + }, + "expected": { + "accept": true, + "valid_until": 1793793659, + "stage": 11 + }, + "note": "Accepted consumer cache deadline includes the required live-status expiry only when live status is required; assessment expiry is later.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "expiry_only_cache_ignores_status_deadline", + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "context_overrides": { + "require_status": false, + "status_available": true + }, + "expected": { + "accept": true, + "valid_until": 1793794200, + "stage": 11 + }, + "note": "Accepted consumer cache deadline includes the required live-status expiry only when live status is required; assessment expiry is later.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "old_binding_current_status_after_generation_advance", + "perspective": "consumer_acceptance", + "signed_object": "status_after_binding_generation", + "context_overrides": { + "highest_status_sequence": 2 + }, + "expected": { + "accept": true, + "stage": 11 + }, + "note": "A newly accepted binding advances the enrollment generation and public sequence without withdrawing the still-valid prior assessment. Fresh current status for that prior binding uses the new sequence.", + "pipeline": "consumer", + "expected_type": "ts-publisher-status+jws" + }, + { + "id": "old_status_sequence_after_generation_advance", + "perspective": "consumer_acceptance", + "signed_object": "status", + "context_overrides": { + "highest_status_sequence": 2 + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + }, + "note": "Once sequence 2 is observed, the earlier sequence 1 response is rejected even if its time window has not expired.", + "pipeline": "consumer", + "expected_type": "ts-publisher-status+jws" + }, + { + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "id": "observation_age_bounds_cache", + "context_overrides": { + "maximum_observation_age_seconds": 20 + }, + "expected": { + "accept": true, + "valid_until": 1793793610, + "stage": 11 + }, + "note": "Observation age starts at observed_from, not evaluated_at.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "id": "required_status_max_age_bounds_cache", + "context_overrides": { + "require_status": true, + "configured_status_max_age": 20 + }, + "expected": { + "accept": true, + "valid_until": 1793793619, + "stage": 11 + }, + "note": "Required-status freshness starts at the status issued_at, not evaluated_at.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "id": "configured_cache_lifetime_bounds_cache", + "context_overrides": { + "maximum_cache_age_seconds": 20 + }, + "expected": { + "accept": true, + "valid_until": 1793793620, + "stage": 11 + }, + "note": "A configured cache lifetime starts at evaluated_at; rereading a cache never restarts it.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "id": "local_trust_key_deadline_bounds_cache", + "context_overrides": { + "trusted_key_accept_until": 1793793615 + }, + "expected": { + "accept": true, + "valid_until": 1793793615, + "stage": 11 + }, + "note": "Local trust-key acceptance may stop before the verifier-published key validity; this local deadline caps caching without changing the signed assessment expiry.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "id": "observation_age_deadline_is_exclusive", + "context_overrides": { + "maximum_observation_age_seconds": 20, + "now": 1793793610 + }, + "expected": { + "accept": false, + "stage": 6, + "reason_code": "stale" + }, + "note": "A result is no longer current at observed_from plus the configured maximum age.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "id": "required_status_max_age_deadline_is_exclusive", + "context_overrides": { + "require_status": true, + "configured_status_max_age": 20, + "now": 1793793619 + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + }, + "note": "Required status is too old at issued_at plus the configured maximum age, even before its signed expires_at.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "perspective": "consumer_acceptance", + "signed_object": "evidence", + "id": "future_issuance_exceeds_30_second_skew", + "context_overrides": { + "now": 1793793564 + }, + "expected": { + "accept": false, + "stage": 6, + "reason_code": "stale" + }, + "note": "Evidence issued 31 seconds after the consumer clock exceeds the allowed 30-second future-issuance skew.", + "pipeline": "consumer", + "expected_type": "ts-publisher-evidence+jws" + }, + { + "id": "higher_status_sequence_invalidates_cached_live_result", + "perspective": "consumer_acceptance", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "highest_status_sequence": 2, + "cache_action": "reuse", + "cached_consumer_result": { + "accepted": true, + "evaluated_at": 1793793600, + "valid_until": 1793793659, + "required_status_dependencies": [ + { + "issuer": "https://verifier.example.com/publisher-attestation", + "enrollment_id": "44444444-4444-4444-8444-444444444444", + "sequence": 1 + } + ] + }, + "now": 1793793610 + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + }, + "note": "Local cache-state trace, not a wire object: a previously validated live-mode result records sequence 1 for its supporting issuer/enrollment. Learning sequence 2 invalidates that cached result at time 3610, before valid_until 3659; fresh sequence-2 support is required before accepting again.", + "pipeline": "consumer", + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "non_canonical_public_key", + "perspective": "consumer_acceptance", + "pipeline": "unsigned_admission", + "unsigned_object": "non_canonical_public_key", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + } + }, + { + "id": "non_canonical_r", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "non_canonical_r", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 3, + "reason_code": "signature_invalid" + }, + "expected_type": "ts-publisher-evidence+jws", + "note": "Noncanonical identity R (y = p + 1), S = k*a over original R bytes. Relaxed point comparison accepts; strict verification rejects. Necessarily overlaps small-order R." + }, + { + "id": "duplicate_member_top_level", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "duplicate_member_top_level", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 1, + "reason_code": "malformed" + }, + "expected_type": "ts-publisher-evidence+jws" + }, + { + "id": "duplicate_member_nested", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "duplicate_member_nested", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 1, + "reason_code": "malformed" + }, + "expected_type": "ts-publisher-evidence+jws" + }, + { + "id": "contradictory_environment", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "contradictory_environment", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "environment_mismatch" + } + }, + { + "id": "live_status_withdrawn_assessment", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_object": "status_withdrawn_assessment" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "withdrawn" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "live_status_withdrawn_enrollment", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_object": "status_withdrawn_enrollment" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "withdrawn" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "live_status_compromised_key", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_object": "status_compromised_key" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "key_compromised" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "live_status_foreign_assessment", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_object": "status_foreign_assessment" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "live_status_foreign_enrollment", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_object": "status_foreign_enrollment" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "live_status_foreign_binding", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_object": "status_foreign_binding" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "live_status_foreign_key", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "require_status": true, + "status_object": "status_foreign_key" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "unpinned_verifier", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "trusted_verifier": { + "kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y" + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "signature_invalid" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "future_verifier", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "trusted_verifier": { + "not_before": 1793793601 + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "signature_invalid" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "expired_verifier", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "trusted_verifier": { + "not_after": 1793793600 + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "signature_invalid" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "wrong_verifier_material", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "trusted_verifier": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "A6EHv_POEL4dcN0Y50vAmWfk1jCbpQ1fHdyGZBJVMbg" + } + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "signature_invalid" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "nonce_precedes_host", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "nonce": "Hx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQA", + "expected_host": "other.example.com" + }, + "expected": { + "accept": false, + "stage": 6, + "reason_code": "nonce_mismatch" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "fresh_expiry_precedes_host", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "context_overrides": { + "now": 1793793655, + "expected_host": "other.example.com" + }, + "expected": { + "accept": false, + "stage": 6, + "reason_code": "stale" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "different_operator_authority", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "context_overrides": { + "operator_authorization": { + "operator_id": "https://other.example.com" + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "operator_unauthorized" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "future_operator_authority", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "context_overrides": { + "operator_authorization": { + "not_before": 1793793601 + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "operator_unauthorized" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "honest_not_satisfied", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment_not_satisfied", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 8, + "reason_code": "policy_untrusted" + }, + "expected_type": "ts-publisher-assessment+jws" + }, + { + "id": "threshold_two_rejects_one_issuer", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "distinct_issuer_threshold": 2 + }, + "expected": { + "accept": false, + "stage": 11, + "reason_code": "policy_untrusted" + } + }, + { + "id": "known_withdrawal_expiry_only", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "known_withdrawal": true + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "withdrawn" + } + }, + { + "id": "known_compromise_expiry_only", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "known_key_compromise": true + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "key_compromised" + } + }, + { + "id": "publisher_authorized_site", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": {}, + "expected": { + "accept": true, + "stage": 3 + } + }, + { + "id": "publisher_squatting", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "authenticated_account": "fixture-unrelated-account" + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "publisher_other_identity", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "publisher_authorization": { + "publisher_id": "another.example.com" + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "publisher_unlisted_site", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "publisher_authorization": { + "authorized_sites": ["sport.example.net"] + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "publisher_withdrawn", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "publisher_authorization": { + "authorization_record_active": false + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "publisher_future", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "publisher_authorization": { + "not_before": 1793793601 + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "publisher_expired", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "publisher_authorization": { + "not_after": 1793793600 + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "assessment_outlives_publisher", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "publisher_authorization": { + "not_after": 1793793601 + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "publisher_second_site", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "second_site_assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": {}, + "expected": { + "accept": true, + "stage": 3 + } + }, + { + "id": "live_status_signature_tamper", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "require_status": true, + "status_object": "tampered_status" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "status_unavailable" + } + }, + { + "id": "publisher_authorization_missing_not_before", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "publisher_authorization": { + "not_before": null + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "publisher_authorization_missing_not_after", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "publisher_authorization": { + "not_after": null + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "publisher_unauthorized" + } + }, + { + "id": "operator_authorization_missing_not_before", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "operator_authorization": { + "not_before": null + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "operator_unauthorized" + } + }, + { + "id": "operator_authorization_missing_not_after", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "operator_authorization": { + "not_after": null + } + }, + "expected": { + "accept": false, + "stage": 2, + "reason_code": "operator_unauthorized" + } + }, + { + "id": "evidence_lifetime_exceeded", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "evidence_lifetime_exceeded", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "Evidence lifetime of 61 seconds exceeds the 60-second bound." + }, + { + "id": "evidence_key_expires_first", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "evidence_key_expires_first", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "key_unauthorized" + }, + "note": "The attestation key expires one second before the evidence it signs." + }, + { + "id": "evidence_key_not_yet_valid", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "evidence_key_not_yet_valid", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "key_unauthorized" + }, + "note": "The attestation key becomes valid one second after evidence issuance." + }, + { + "id": "status_lifetime_exceeded", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "status_lifetime_exceeded", + "expected_type": "ts-publisher-status+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "Status lifetime of 61 seconds exceeds the 60-second bound." + }, + { + "id": "mismatched_key_possession_digest", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "mismatched_key_possession_digest", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "key_possession.evidence_digest differs from the assessment evidence_digest." + }, + { + "id": "mismatched_configuration_digest", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "mismatched_configuration_digest", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "configuration.evidence_digest differs from the bound configuration digest." + }, + { + "id": "reversed_observation_interval", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "reversed_observation_interval", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "observed_from is after observed_until." + }, + { + "id": "observation_after_issuance", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "observation_after_issuance", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "observed_until is after issued_at." + }, + { + "id": "late_assessment_issuance", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "late_assessment_issuance", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "Issuance is 61 seconds after observed_until." + }, + { + "id": "assessment_lifetime_exceeded", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "assessment_lifetime_exceeded", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 4, + "reason_code": "malformed" + }, + "note": "Expiry is 901 seconds after observed_from, beyond the policy-independent cap." + }, + { + "id": "fresh_evidence_failure_precedes_assessment_failure", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "negative_encoding": "mismatched_key_possession_digest", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "fresh_evidence": "signature_tamper" + }, + "expected": { + "accept": false, + "stage": 3, + "reason_code": "signature_invalid" + }, + "note": "Fresh evidence fails stage 3 and the assessment fails stage 4; the earlier stage is reported." + }, + { + "id": "live_status_retired_key", + "perspective": "consumer_acceptance", + "pipeline": "consumer", + "signed_object": "assessment", + "expected_type": "ts-publisher-assessment+jws", + "context_overrides": { + "require_status": true, + "status_object": "status_retired_key" + }, + "expected": { + "accept": false, + "stage": 10, + "reason_code": "key_unauthorized" + }, + "note": "Required status reports the publisher key retired." + }, + { + "id": "verifier_issuance_signature_invalid", + "perspective": "verifier_issuance", + "pipeline": "verifier_issuance", + "negative_encoding": "signature_tamper", + "expected_type": "ts-publisher-evidence+jws", + "context_overrides": {}, + "expected": { + "accept": false, + "stage": 1, + "reason_code": "signature_invalid" + }, + "note": "Decode and signature failures are issuance stage 1, not consumer stage numbers." + } + ], + "unsigned_expectations": { + "key_discovery": "Structurally valid candidate keys only; never establishes enrollment or trust.", + "assessment_list": "Structurally valid transport hint only; independently verify each embedded compact JWS.", + "enrollment_challenge": "Structurally valid fictional single-use challenge; creation time is external_context.now.", + "key_metadata": "Structurally valid public metadata; local name example-key, metadata entry ts-publisher-key-example-key. No private secret included.", + "key_history": "Untrusted bounded history transport only; each embedded signature and independent trust policy remains mandatory.", + "self_operated_enrollment_challenge": "Required operator_id is null: self-operated with no separate operator claim.", + "policy_publisher_declaration_v1": "Canonical built-in policy document; assessment policy.digest for publisher-declaration-v1 version 1 is SHA-256 of its JCS form.", + "policy_fictional_official_profile_v1": "Fictional policy document used only by the official-shaped vector; not a built-in policy and not issuable in production.", + "verifier_metadata": "Structurally valid convenience metadata; untrusted until confirmed out of band and pinned.", + "error_example": "Closed error body for a canonical, well-formed but unlisted kid on the evidence endpoint." + }, + "negative_unsigned_objects": { + "small_order_key_discovery": { + "schema": "ts-publisher-key-discovery-v1", + "host": "news.example.com", + "keys": [ + { + "kid": "eV9frzBXPTP92MWWMpoFOh0WI_kJLvGlhcNs15APU_s", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + "not_before": 1793790000, + "not_after": 1793880000 + } + ], + "verifiers": ["https://verifier.example.com/publisher-attestation"] + }, + "non_canonical_operator_identifier": { + "schema": "ts-publisher-enrollment-challenge-v1", + "issuer": "https://verifier.example.com/publisher-attestation", + "host": "news.example.com", + "publisher_id": "owner.example.com", + "operator_id": "https://Operator.example.com/publisher-attestation/", + "deployment_id": "22222222-2222-4222-8222-222222222222", + "environment": "production", + "provider": "fastly", + "service_id": "fictional-service", + "publisher_key_kid": "1IG2tMH7J2wbJZnOf8LJzQitKf7LMvoAElsuDMVM54Y", + "nonce": "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8", + "issued_at": 1793793600, + "expires_at": 1793794200 + }, + "non_canonical_public_key": { + "schema": "ts-publisher-key-discovery-v1", + "host": "news.example.com", + "keys": [ + { + "kid": "eq2rZUk3g2fSy3S5rr9avodJ-kS5sJXAFWTTPMx4iRY", + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "8P_______________________________________38" + }, + "not_before": 1793790000, + "not_after": 1793880000 + } + ], + "verifiers": ["https://verifier.example.com/publisher-attestation"] + } + }, + "harness": { + "overrides": "RFC 7396 JSON Merge Patch against a fresh deep copy of external_context for every case. Arrays replace; null removes a member. Absent authority never authorizes a claimed identity; removed optional cache fields disable that cache behavior. Cases do not share caches or highest-seen sequences.", + "inputs": "Every signed case supplies expected_type independently of untrusted bytes. status_object selects a compact signed status; target_assessment selects the independently expected assessment for status-only cases. fresh_evidence selects the separately challenged response from signed_objects or negative_encodings; the generator uses collected_evidence for assessment evidence digests. Unsigned cases select unsigned_objects or negative_unsigned_objects. Evidence-only success is a self-declaration, not endorsement.", + "stages": "expected.stage is a number in validation_order. Evidence-only cases run stages 1–4, 6–7 and report self-declaration, not endorsement; assessment cases combine fresh evidence with stages 1–11. unsigned_admission uses decode/schema/key validation (1/4). verifier_issuance uses its separately numbered preconditions below. Success stage is the last completed stage. Cache cases use consumer stage 10 for status dependency invalidation. The bounded harness has one candidate issuer: threshold 1 accepts and thresholds above 1 reject. It does not test aggregation of several accepted issuers. Narrative scope is not an asserted harness output. Each consumer stage applies to the fresh evidence before the candidate assessment, so the first failing stage across both objects is reported.", + "verifier_issuance_stages": { + "1": "bounded decoding, schema, key admission and signature verification (malformed or signature_invalid)", + "2": "Active exact publisher/account/site authorization and optional operator authorization, with publication expiry caps (publisher_unauthorized/operator_unauthorized)", + "3": "durable authorization history and signer fencing (state_recovery_required)" + }, + "policy_documents": "trusted_verifier.policy_documents contains the names of unsigned_objects policy documents pinned independently by id/version/digest. Evaluate those complete documents, not a consumer-local required_checks substitute." + } +} diff --git a/docs/superpowers/specs/policies/fictional-official-profile-v1.json b/docs/superpowers/specs/policies/fictional-official-profile-v1.json new file mode 100644 index 0000000000..6adaff7574 --- /dev/null +++ b/docs/superpowers/specs/policies/fictional-official-profile-v1.json @@ -0,0 +1,35 @@ +{ + "schema": "ts-publisher-policy-v1", + "id": "fictional-official-profile-v1", + "version": 1, + "checks": { + "host_authorization": { + "requirement": "pass", + "methods": ["dns-key-control-v1"] + }, + "key_possession": { + "requirement": "pass", + "methods": ["publisher-evidence-jws-v1"] + }, + "host_service_binding": { + "requirement": "pass", + "methods": ["fictional-fastly-host-service-binding-v1"] + }, + "active_version": { + "requirement": "pass", + "methods": ["fictional-fastly-active-version-v1"] + }, + "configuration": { + "requirement": "pass", + "methods": ["public-identity-projection-v1"] + } + }, + "build": { + "assurance": ["artifact_bound"], + "declared_methods": [], + "fingerprint_methods": [], + "artifact_methods": ["fictional-fastly-artifact-binding-v1"] + }, + "max_assessment_lifetime_seconds": 900, + "extensions": {} +} diff --git a/docs/superpowers/specs/policies/publisher-declaration-v1.json b/docs/superpowers/specs/policies/publisher-declaration-v1.json new file mode 100644 index 0000000000..a958be5445 --- /dev/null +++ b/docs/superpowers/specs/policies/publisher-declaration-v1.json @@ -0,0 +1,38 @@ +{ + "schema": "ts-publisher-policy-v1", + "id": "publisher-declaration-v1", + "version": 1, + "checks": { + "host_authorization": { + "requirement": "pass", + "methods": ["dns-key-control-v1"] + }, + "key_possession": { + "requirement": "pass", + "methods": ["publisher-evidence-jws-v1"] + }, + "host_service_binding": { + "requirement": "pass_or_not_assessed", + "methods": null + }, + "active_version": { + "requirement": "pass_or_not_assessed", + "methods": null + }, + "configuration": { + "requirement": "pass", + "methods": ["public-identity-projection-v1"] + } + }, + "build": { + "assurance": ["declared", "fingerprint_only"], + "declared_methods": [ + "publisher-custom-declaration-v1", + "publisher-unspecified-declaration-v1" + ], + "fingerprint_methods": null, + "artifact_methods": [] + }, + "max_assessment_lifetime_seconds": 900, + "extensions": {} +} diff --git a/docs/superpowers/specs/schemas/publisher-attestation-v1.schema.json b/docs/superpowers/specs/schemas/publisher-attestation-v1.schema.json new file mode 100644 index 0000000000..a51c91f866 --- /dev/null +++ b/docs/superpowers/specs/schemas/publisher-attestation-v1.schema.json @@ -0,0 +1,1642 @@ +{ + "$comment": "JSON Schema draft-07. Local references only. Structural validation is necessary but never verifies cryptography, trust, authorization, provider qualification or current acceptance under independently configured policy. Reject duplicate members/trailing bytes and enforce depth <=16, UTF-8 byte limits (ordinary strings <=256), HTTP limits and canonical encodings before validation. External checks include JWK thumbprints, JCS hashes, equality of projected/bound/enrolled identities, ASCII-only host canonicalization, time ordering/lifetimes/key validity, nonce consumption, policy-required checks, release eligibility/digest equality, status sequence/withdrawal and independent trust settings. Ed25519 verification is strict: reject non-canonical S, non-canonical R or public-key encodings and small-order R points or public keys. Times are exclusive at expiry (valid while now < expires_at) and inclusive at not_before. Evidence/status lifetime <=60 seconds; assessment expiry <=observed_from+900, issued_at<=observed_until+60; key validity <=90 days; challenge expiry <=issued_at+600. No schema alone establishes these relationships. Assessment expiry must not exceed independently authorized publisher-record, operator-record, enrollment, publisher-key or verifier-key expiry. Current status expiry must not exceed those bounds, assessment expiry or issued_at+60. Recheck authority at publication/commit and status reuse even when authorization generation is unchanged; expired publisher/operator authority stops positive assessment/current status. Configuration checks concern signed publisher-declared identity/enrollment consistency only, never a registry read of full configuration. Default acceptance is expiry-only (require_status=false); live status is an optional stricter policy. A cached positive consumer result never outlives the earliest accepted-threshold assessment expiry, independently configured trust-key acceptance deadline, original observation time plus configured observation age, evaluated_at plus configured cache lifetime, or required live-status expiry and original issued_at plus configured status maximum age (at most 60 seconds); expiry-only acceptance adds no status-expiry dependency. Non-null operator identity requires independent exact operator DNS/account proof; null denotes self-operated with no distinct operator claim. Publisher IDs are canonical owner domains; independent publisher/account/site membership must be authorized before positive issuance. Missing, expired, foreign-account or out-of-scope publisher authority fails publisher_unauthorized.", + "title": "Publisher attestation version 1 structural contract", + "oneOf": [ + { + "$ref": "#/definitions/evidenceProtectedHeader" + }, + { + "$ref": "#/definitions/assessmentProtectedHeader" + }, + { + "$ref": "#/definitions/statusProtectedHeader" + }, + { + "$ref": "#/definitions/identityProjection" + }, + { + "$ref": "#/definitions/binding" + }, + { + "$ref": "#/definitions/evidence" + }, + { + "$ref": "#/definitions/assessment" + }, + { + "$ref": "#/definitions/status" + }, + { + "$ref": "#/definitions/keyDiscovery" + }, + { + "$ref": "#/definitions/assessmentList" + }, + { + "$ref": "#/definitions/enrollmentChallenge" + }, + { + "$ref": "#/definitions/keyMetadata" + }, + { + "$ref": "#/definitions/keyHistory" + }, + { + "$ref": "#/definitions/policyDocument" + }, + { + "$ref": "#/definitions/verifierMetadata" + }, + { + "$ref": "#/definitions/errorBody" + } + ], + "definitions": { + "text": { + "type": "string", + "maxLength": 256 + }, + "nonemptyText": { + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "integer": { + "type": "integer", + "minimum": 0, + "maximum": 9007199254740991 + }, + "positiveInteger": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991 + }, + "time": { + "$ref": "#/definitions/integer" + }, + "hex256": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + "base64url32": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$" + }, + "uuid": { + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" + }, + "host": { + "type": "string", + "maxLength": 253, + "pattern": "^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$", + "$comment": "Protocol boundary accepts ASCII DNS only; canonicalize ASCII letters to lowercase, remove one terminal dot, reject invalid DNS syntax and IP literals. Already-encoded xn-- labels are opaque ASCII DNS labels; Unicode input is rejected, with no Unicode/UTS dependency.", + "not": { + "pattern": "(?:^|\\.)(?:[0-9]+|0x[0-9a-f]*)$" + } + }, + "httpsIdentifier": { + "type": "string", + "maxLength": 256, + "pattern": "^https://[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*(?:/[A-Za-z0-9._~-]+)*$", + "$comment": "Canonical HTTPS identifier (PA-IDENTIFIER): lowercase ASCII host without port, trailing dot or IP literal; optional path of non-empty unreserved-character segments, no trailing slash, no dot segments, no percent-encoding, userinfo, query or fragment. Compared as exact bytes. Host label/total length limits are enforced outside this pattern.", + "not": { + "anyOf": [ + { + "pattern": "^https://(?:[a-z0-9-]+\\.)*(?:[0-9]+|0x[0-9a-f]*)(?:/|$)" + }, + { + "pattern": "/\\.\\.?(?:/|$)" + } + ] + } + }, + "environment": { + "enum": ["production", "staging", "development"] + }, + "extensions": { + "type": "object", + "required": [], + "additionalProperties": false, + "properties": {} + }, + "digest": { + "type": "object", + "required": ["algorithm", "value"], + "additionalProperties": false, + "properties": { + "algorithm": { + "const": "sha256" + }, + "value": { + "$ref": "#/definitions/hex256" + } + } + }, + "publicJwk": { + "type": "object", + "required": ["kty", "crv", "x"], + "additionalProperties": false, + "properties": { + "kty": { + "const": "OKP" + }, + "crv": { + "const": "Ed25519" + }, + "x": { + "$ref": "#/definitions/base64url32" + } + } + }, + "keyValidity": { + "type": "object", + "required": ["kid", "jwk", "not_before", "not_after"], + "additionalProperties": false, + "properties": { + "kid": { + "$ref": "#/definitions/base64url32" + }, + "jwk": { + "$ref": "#/definitions/publicJwk" + }, + "not_before": { + "$ref": "#/definitions/time" + }, + "not_after": { + "$ref": "#/definitions/time" + } + } + }, + "evidenceProtectedHeader": { + "type": "object", + "required": ["alg", "kid", "typ"], + "additionalProperties": false, + "properties": { + "alg": { + "const": "Ed25519" + }, + "kid": { + "$ref": "#/definitions/base64url32" + }, + "typ": { + "const": "ts-publisher-evidence+jws" + } + } + }, + "assessmentProtectedHeader": { + "type": "object", + "required": ["alg", "kid", "typ"], + "additionalProperties": false, + "properties": { + "alg": { + "const": "Ed25519" + }, + "kid": { + "$ref": "#/definitions/base64url32" + }, + "typ": { + "const": "ts-publisher-assessment+jws" + } + } + }, + "statusProtectedHeader": { + "type": "object", + "required": ["alg", "kid", "typ"], + "additionalProperties": false, + "properties": { + "alg": { + "const": "Ed25519" + }, + "kid": { + "$ref": "#/definitions/base64url32" + }, + "typ": { + "const": "ts-publisher-status+jws" + } + } + }, + "platform": { + "type": "object", + "required": ["provider", "service_id", "service_version", "staging"], + "additionalProperties": false, + "properties": { + "provider": { + "enum": ["fastly", "axum"] + }, + "service_id": { + "anyOf": [ + { + "$ref": "#/definitions/nonemptyText" + }, + { + "type": "null" + } + ] + }, + "service_version": { + "anyOf": [ + { + "$ref": "#/definitions/positiveInteger" + }, + { + "type": "null" + } + ] + }, + "staging": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "fastly" + } + } + }, + "then": { + "properties": { + "service_id": { + "$ref": "#/definitions/nonemptyText" + }, + "service_version": { + "$ref": "#/definitions/positiveInteger" + } + } + } + }, + { + "if": { + "properties": { + "provider": { + "const": "axum" + } + } + }, + "then": { + "properties": { + "service_id": { + "type": "null" + }, + "service_version": { + "type": "null" + }, + "staging": { + "type": "null" + } + } + } + } + ] + }, + "identityProjection": { + "type": "object", + "required": [ + "schema", + "host", + "publisher_id", + "operator_id", + "deployment_id", + "environment" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-identity-v1" + }, + "host": { + "$ref": "#/definitions/host" + }, + "publisher_id": { + "$ref": "#/definitions/host" + }, + "operator_id": { + "anyOf": [ + { + "$ref": "#/definitions/httpsIdentifier" + }, + { + "type": "null" + } + ] + }, + "deployment_id": { + "$ref": "#/definitions/uuid" + }, + "environment": { + "$ref": "#/definitions/environment" + } + } + }, + "configuration": { + "type": "object", + "required": ["schema", "digest", "projection"], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-identity-v1" + }, + "digest": { + "$ref": "#/definitions/digest" + }, + "projection": { + "$ref": "#/definitions/identityProjection" + } + } + }, + "binding": { + "type": "object", + "required": [ + "schema", + "host", + "publisher_id", + "operator_id", + "deployment_id", + "environment", + "platform", + "attestation_key", + "configuration", + "extensions" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-binding-v1" + }, + "host": { + "$ref": "#/definitions/host" + }, + "publisher_id": { + "$ref": "#/definitions/host" + }, + "operator_id": { + "anyOf": [ + { + "$ref": "#/definitions/httpsIdentifier" + }, + { + "type": "null" + } + ] + }, + "deployment_id": { + "$ref": "#/definitions/uuid" + }, + "environment": { + "$ref": "#/definitions/environment" + }, + "platform": { + "$ref": "#/definitions/platform" + }, + "attestation_key": { + "$ref": "#/definitions/keyValidity" + }, + "configuration": { + "$ref": "#/definitions/configuration" + }, + "extensions": { + "$ref": "#/definitions/extensions" + } + }, + "allOf": [ + { + "if": { + "properties": { + "platform": { + "properties": { + "provider": { + "const": "axum" + } + } + } + } + }, + "then": { + "properties": { + "environment": { + "const": "development" + } + } + } + }, + { + "if": { + "properties": { + "platform": { + "properties": { + "provider": { + "const": "fastly" + }, + "staging": { + "const": true + } + } + } + } + }, + "then": { + "properties": { + "environment": { + "enum": ["staging", "development"] + } + } + } + }, + { + "if": { + "properties": { + "platform": { + "properties": { + "provider": { + "const": "fastly" + }, + "staging": { + "const": false + } + } + } + } + }, + "then": { + "properties": { + "environment": { + "enum": ["production", "development"] + } + } + } + } + ] + }, + "buildReport": { + "type": "object", + "required": [ + "declared_class", + "source_commit", + "package_version", + "target", + "variant" + ], + "additionalProperties": false, + "properties": { + "declared_class": { + "enum": ["custom", "unspecified"] + }, + "source_commit": { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9a-f]{40}$" + }, + { + "type": "null" + } + ] + }, + "package_version": { + "anyOf": [ + { + "$ref": "#/definitions/text" + }, + { + "type": "null" + } + ] + }, + "target": { + "anyOf": [ + { + "$ref": "#/definitions/text" + }, + { + "type": "null" + } + ] + }, + "variant": { + "anyOf": [ + { + "$ref": "#/definitions/text" + }, + { + "type": "null" + } + ] + } + } + }, + "evidence": { + "type": "object", + "required": [ + "schema", + "binding", + "binding_id", + "nonce", + "issued_at", + "expires_at", + "build_report", + "extensions" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-evidence-v1" + }, + "binding": { + "$ref": "#/definitions/binding" + }, + "binding_id": { + "$ref": "#/definitions/hex256" + }, + "nonce": { + "$ref": "#/definitions/base64url32" + }, + "issued_at": { + "$ref": "#/definitions/time" + }, + "expires_at": { + "$ref": "#/definitions/time" + }, + "build_report": { + "$ref": "#/definitions/buildReport" + }, + "extensions": { + "$ref": "#/definitions/extensions" + } + } + }, + "check": { + "type": "object", + "required": ["outcome", "method", "observed_at", "evidence_digest"], + "additionalProperties": false, + "properties": { + "outcome": { + "enum": ["pass", "fail", "unavailable", "not_assessed"] + }, + "method": { + "anyOf": [ + { + "$ref": "#/definitions/nonemptyText" + }, + { + "type": "null" + } + ] + }, + "observed_at": { + "anyOf": [ + { + "$ref": "#/definitions/time" + }, + { + "type": "null" + } + ] + }, + "evidence_digest": { + "anyOf": [ + { + "$ref": "#/definitions/digest" + }, + { + "type": "null" + } + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "outcome": { + "const": "pass" + } + } + }, + "then": { + "properties": { + "observed_at": { + "$ref": "#/definitions/time" + }, + "evidence_digest": { + "$ref": "#/definitions/digest" + }, + "method": { + "$ref": "#/definitions/nonemptyText" + } + } + } + }, + { + "if": { + "properties": { + "outcome": { + "const": "not_assessed" + } + } + }, + "then": { + "properties": { + "method": { + "type": "null" + }, + "observed_at": { + "type": "null" + }, + "evidence_digest": { + "type": "null" + } + } + }, + "else": { + "properties": { + "method": { + "$ref": "#/definitions/nonemptyText" + } + } + } + } + ] + }, + "checks": { + "type": "object", + "required": [ + "host_authorization", + "key_possession", + "host_service_binding", + "active_version", + "configuration" + ], + "additionalProperties": false, + "properties": { + "host_authorization": { + "$ref": "#/definitions/check" + }, + "key_possession": { + "$ref": "#/definitions/check" + }, + "host_service_binding": { + "$ref": "#/definitions/check" + }, + "active_version": { + "$ref": "#/definitions/check" + }, + "configuration": { + "$ref": "#/definitions/check" + } + } + }, + "release": { + "type": "object", + "required": [ + "repository", + "tag", + "source_commit", + "asset_name", + "artifact_sha256", + "verification_evidence_digest" + ], + "additionalProperties": false, + "properties": { + "repository": { + "type": "string", + "maxLength": 256, + "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$", + "$comment": "Layer 1 owner/name identity; independently configured release eligibility remains mandatory." + }, + "tag": { + "$ref": "#/definitions/nonemptyText" + }, + "source_commit": { + "type": "string", + "pattern": "^[0-9a-f]{40}$" + }, + "asset_name": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "pattern": "^[^/\\\\\\s]+$", + "$comment": "Simple asset basename; dot and dot-dot names are rejected.", + "not": { + "enum": [".", ".."] + } + }, + "artifact_sha256": { + "$ref": "#/definitions/digest" + }, + "verification_evidence_digest": { + "$ref": "#/definitions/digest" + } + } + }, + "build": { + "type": "object", + "required": [ + "provenance", + "assurance", + "method", + "artifact_sha256", + "provider_files_hash", + "release", + "coverage" + ], + "additionalProperties": false, + "properties": { + "provenance": { + "enum": ["official", "custom", "unknown"] + }, + "assurance": { + "enum": ["artifact_bound", "fingerprint_only", "declared"] + }, + "method": { + "$ref": "#/definitions/nonemptyText" + }, + "artifact_sha256": { + "anyOf": [ + { + "$ref": "#/definitions/digest" + }, + { + "type": "null" + } + ] + }, + "provider_files_hash": { + "anyOf": [ + { + "type": "string", + "pattern": "^[0-9a-f]{128}$" + }, + { + "type": "null" + } + ] + }, + "release": { + "anyOf": [ + { + "$ref": "#/definitions/release" + }, + { + "type": "null" + } + ] + }, + "coverage": { + "enum": [ + "selected_executable_only", + "provider_package_fingerprint", + "declaration_only" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "provenance": { + "const": "official" + } + } + }, + "then": { + "properties": { + "assurance": { + "const": "artifact_bound" + }, + "artifact_sha256": { + "$ref": "#/definitions/digest" + }, + "release": { + "$ref": "#/definitions/release" + } + } + } + }, + { + "if": { + "properties": { + "assurance": { + "const": "artifact_bound" + } + } + }, + "then": { + "properties": { + "artifact_sha256": { + "$ref": "#/definitions/digest" + }, + "coverage": { + "const": "selected_executable_only" + } + } + } + }, + { + "if": { + "properties": { + "assurance": { + "const": "fingerprint_only" + } + } + }, + "then": { + "properties": { + "artifact_sha256": { + "type": "null" + }, + "provider_files_hash": { + "type": "string" + }, + "release": { + "type": "null" + }, + "coverage": { + "const": "provider_package_fingerprint" + } + } + } + }, + { + "if": { + "properties": { + "assurance": { + "const": "declared" + } + } + }, + "then": { + "properties": { + "artifact_sha256": { + "type": "null" + }, + "release": { + "type": "null" + }, + "coverage": { + "const": "declaration_only" + } + } + } + }, + { + "if": { + "properties": { + "provenance": { + "const": "custom" + } + } + }, + "then": { + "properties": { + "release": { + "type": "null" + } + } + } + } + ] + }, + "reasonCodes": { + "type": "array", + "maxItems": 16, + "items": { + "enum": [ + "malformed", + "signature_invalid", + "host_mismatch", + "environment_mismatch", + "key_unauthorized", + "key_compromised", + "nonce_mismatch", + "stale", + "binding_mismatch", + "release_ineligible", + "artifact_mismatch", + "artifact_binding_unproven", + "configuration_unobservable", + "provider_unavailable", + "observation_race", + "withdrawn", + "status_unavailable", + "policy_untrusted", + "clock_unavailable", + "operator_unauthorized", + "state_recovery_required", + "publisher_unauthorized" + ] + }, + "uniqueItems": true, + "$comment": "Lexicographic ordering is checked outside JSON Schema." + }, + "policy": { + "type": "object", + "required": ["id", "version", "digest"], + "additionalProperties": false, + "properties": { + "id": { + "$ref": "#/definitions/nonemptyText" + }, + "version": { + "$ref": "#/definitions/positiveInteger" + }, + "digest": { + "$ref": "#/definitions/digest" + } + } + }, + "assessment": { + "type": "object", + "required": [ + "schema", + "issuer", + "assessment_id", + "enrollment_id", + "binding", + "binding_id", + "evidence_digest", + "observed_from", + "observed_until", + "issued_at", + "expires_at", + "policy", + "result", + "checks", + "build", + "reason_codes", + "extensions" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-assessment-v1" + }, + "issuer": { + "$ref": "#/definitions/httpsIdentifier" + }, + "assessment_id": { + "$ref": "#/definitions/uuid" + }, + "enrollment_id": { + "$ref": "#/definitions/uuid" + }, + "binding": { + "$ref": "#/definitions/binding" + }, + "binding_id": { + "$ref": "#/definitions/hex256" + }, + "evidence_digest": { + "$ref": "#/definitions/digest" + }, + "observed_from": { + "$ref": "#/definitions/time" + }, + "observed_until": { + "$ref": "#/definitions/time" + }, + "issued_at": { + "$ref": "#/definitions/time" + }, + "expires_at": { + "$ref": "#/definitions/time" + }, + "policy": { + "$ref": "#/definitions/policy" + }, + "result": { + "enum": ["satisfied", "not_satisfied", "indeterminate"] + }, + "checks": { + "$ref": "#/definitions/checks" + }, + "build": { + "$ref": "#/definitions/build" + }, + "reason_codes": { + "$ref": "#/definitions/reasonCodes" + }, + "extensions": { + "$ref": "#/definitions/extensions" + } + }, + "allOf": [ + { + "if": { + "properties": { + "build": { + "properties": { + "provider_files_hash": { + "type": "string" + } + } + } + } + }, + "then": { + "properties": { + "binding": { + "properties": { + "platform": { + "properties": { + "provider": { + "const": "fastly" + }, + "staging": { + "type": "boolean" + } + } + } + } + }, + "checks": { + "properties": { + "host_service_binding": { + "properties": { + "outcome": { + "const": "pass" + } + } + }, + "active_version": { + "properties": { + "outcome": { + "const": "pass" + } + } + } + } + } + } + } + } + ] + }, + "status": { + "type": "object", + "required": [ + "schema", + "issuer", + "enrollment_id", + "assessment_id", + "binding_id", + "publisher_key_kid", + "sequence", + "assessment_status", + "enrollment_status", + "publisher_key_status", + "reason_codes", + "issued_at", + "expires_at", + "extensions" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-status-v1" + }, + "issuer": { + "$ref": "#/definitions/httpsIdentifier" + }, + "enrollment_id": { + "$ref": "#/definitions/uuid" + }, + "assessment_id": { + "$ref": "#/definitions/uuid" + }, + "binding_id": { + "$ref": "#/definitions/hex256" + }, + "publisher_key_kid": { + "$ref": "#/definitions/base64url32" + }, + "sequence": { + "$ref": "#/definitions/positiveInteger" + }, + "assessment_status": { + "enum": ["current", "withdrawn"] + }, + "enrollment_status": { + "enum": ["active", "withdrawn"] + }, + "publisher_key_status": { + "enum": ["active", "compromised", "retired"] + }, + "reason_codes": { + "$ref": "#/definitions/reasonCodes" + }, + "issued_at": { + "$ref": "#/definitions/time" + }, + "expires_at": { + "$ref": "#/definitions/time" + }, + "extensions": { + "$ref": "#/definitions/extensions" + } + } + }, + "keyDiscovery": { + "type": "object", + "required": ["schema", "host", "keys", "verifiers"], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-key-discovery-v1" + }, + "host": { + "$ref": "#/definitions/host" + }, + "keys": { + "type": "array", + "maxItems": 4, + "items": { + "$ref": "#/definitions/keyValidity" + }, + "minItems": 1 + }, + "verifiers": { + "type": "array", + "minItems": 0, + "maxItems": 16, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/httpsIdentifier" + }, + "$comment": "Untrusted HTTPS issuer discovery hints only. Never imports trust roots or enables a policy." + } + } + }, + "compactJws": { + "type": "string", + "maxLength": 32768, + "pattern": "^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]{85}[AQgw]$", + "$comment": "Explicit exception to the 256-byte string limit. Header/payload parsing, base64url canonicality and signature verification are external." + }, + "assessmentList": { + "type": "object", + "required": ["schema", "host", "environment", "assessments"], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-assessment-list-v1" + }, + "host": { + "$ref": "#/definitions/host" + }, + "environment": { + "$ref": "#/definitions/environment" + }, + "assessments": { + "type": "array", + "maxItems": 16, + "items": { + "$ref": "#/definitions/compactJws" + } + } + }, + "$comment": "Aggregate decoded HTTP body limit: 524288 bytes, checked before JSON Schema validation; each compact JWS is at most 32768 ASCII bytes." + }, + "enrollmentChallenge": { + "type": "object", + "required": [ + "schema", + "issuer", + "host", + "publisher_id", + "operator_id", + "deployment_id", + "provider", + "service_id", + "environment", + "publisher_key_kid", + "nonce", + "issued_at", + "expires_at" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-enrollment-challenge-v1" + }, + "issuer": { + "$ref": "#/definitions/httpsIdentifier" + }, + "host": { + "$ref": "#/definitions/host" + }, + "publisher_id": { + "$ref": "#/definitions/host" + }, + "operator_id": { + "anyOf": [ + { + "$ref": "#/definitions/httpsIdentifier" + }, + { + "type": "null" + } + ] + }, + "deployment_id": { + "$ref": "#/definitions/uuid" + }, + "provider": { + "enum": ["fastly", "axum"] + }, + "service_id": { + "anyOf": [ + { + "$ref": "#/definitions/nonemptyText" + }, + { + "type": "null" + } + ] + }, + "environment": { + "$ref": "#/definitions/environment" + }, + "publisher_key_kid": { + "$ref": "#/definitions/base64url32" + }, + "nonce": { + "$ref": "#/definitions/base64url32" + }, + "issued_at": { + "$ref": "#/definitions/time" + }, + "expires_at": { + "$ref": "#/definitions/time" + } + }, + "allOf": [ + { + "if": { + "properties": { + "provider": { + "const": "fastly" + } + } + }, + "then": { + "properties": { + "service_id": { + "$ref": "#/definitions/nonemptyText" + } + } + } + }, + { + "if": { + "properties": { + "provider": { + "const": "axum" + } + } + }, + "then": { + "properties": { + "service_id": { + "type": "null" + }, + "environment": { + "const": "development" + } + } + } + } + ], + "$comment": "issued_at < expires_at <= issued_at + 600 is checked outside JSON Schema." + }, + "keyMetadata": { + "type": "object", + "required": [ + "schema", + "kid", + "jwk", + "not_before", + "not_after", + "secret_name" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-key-metadata-v1" + }, + "kid": { + "$ref": "#/definitions/base64url32" + }, + "jwk": { + "$ref": "#/definitions/publicJwk" + }, + "not_before": { + "$ref": "#/definitions/time" + }, + "not_after": { + "$ref": "#/definitions/time" + }, + "secret_name": { + "type": "string", + "pattern": "^ts-publisher-[a-z][a-z0-9-]{0,47}$" + } + }, + "$comment": "Read metadata at ts-publisher-key- in existing trusted_server_config; secret_name must equal ts-publisher- in existing trusted_server_secrets. Cross-entry equality is external." + }, + "compactStatusJws": { + "type": "string", + "maxLength": 8192, + "pattern": "^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]{85}[AQgw]$", + "$comment": "Explicit exception to the 256-byte string limit. Header/payload parsing, base64url canonicality and signature verification are external." + }, + "keyHistory": { + "type": "object", + "required": ["schema", "host", "environment", "entries", "next_cursor"], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-key-history-v1" + }, + "host": { + "$ref": "#/definitions/host" + }, + "environment": { + "$ref": "#/definitions/environment" + }, + "entries": { + "type": "array", + "minItems": 0, + "maxItems": 16, + "items": { + "type": "object", + "required": ["assessment", "status"], + "additionalProperties": false, + "properties": { + "assessment": { + "$ref": "#/definitions/compactJws" + }, + "status": { + "anyOf": [ + { + "$ref": "#/definitions/compactStatusJws" + }, + { + "type": "null" + } + ] + } + } + } + }, + "next_cursor": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "minLength": 1, + "maxLength": 256, + "pattern": "^[\\x20-\\x7e]+$" + } + ] + } + }, + "$comment": "Untrusted transport of historical signed objects. Aggregate decoded HTTP body limit 524288 bytes is enforced externally before schema validation. Assessment JWS <=32768 ASCII bytes; status JWS <=8192 ASCII bytes. Pagination cursor is opaque printable ASCII; it confers no authority." + }, + "policyCheck": { + "type": "object", + "required": ["requirement", "methods"], + "additionalProperties": false, + "properties": { + "requirement": { + "enum": ["pass", "pass_or_not_assessed"] + }, + "methods": { + "anyOf": [ + { + "type": "array", + "maxItems": 16, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/nonemptyText" + } + }, + { + "type": "null" + } + ] + } + }, + "$comment": "methods null means a passing check must use a method in the consumer's independent allowlist. An array additionally restricts passing methods to its members, so both must allow the method; an empty array permits no passing method. A not-assessed check needs no method." + }, + "policyDocument": { + "type": "object", + "required": [ + "schema", + "id", + "version", + "checks", + "build", + "max_assessment_lifetime_seconds", + "extensions" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-policy-v1" + }, + "id": { + "$ref": "#/definitions/nonemptyText" + }, + "version": { + "$ref": "#/definitions/positiveInteger" + }, + "checks": { + "type": "object", + "required": [ + "host_authorization", + "key_possession", + "host_service_binding", + "active_version", + "configuration" + ], + "additionalProperties": false, + "properties": { + "host_authorization": { + "$ref": "#/definitions/policyCheck" + }, + "key_possession": { + "$ref": "#/definitions/policyCheck" + }, + "host_service_binding": { + "$ref": "#/definitions/policyCheck" + }, + "active_version": { + "$ref": "#/definitions/policyCheck" + }, + "configuration": { + "$ref": "#/definitions/policyCheck" + } + } + }, + "build": { + "type": "object", + "required": [ + "assurance", + "declared_methods", + "fingerprint_methods", + "artifact_methods" + ], + "additionalProperties": false, + "properties": { + "assurance": { + "type": "array", + "minItems": 1, + "maxItems": 3, + "uniqueItems": true, + "items": { + "enum": ["artifact_bound", "fingerprint_only", "declared"] + } + }, + "declared_methods": { + "anyOf": [ + { + "type": "array", + "maxItems": 16, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/nonemptyText" + } + }, + { + "type": "null" + } + ] + }, + "fingerprint_methods": { + "anyOf": [ + { + "type": "array", + "maxItems": 16, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/nonemptyText" + } + }, + { + "type": "null" + } + ] + }, + "artifact_methods": { + "anyOf": [ + { + "type": "array", + "maxItems": 16, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/nonemptyText" + } + }, + { + "type": "null" + } + ] + } + } + }, + "max_assessment_lifetime_seconds": { + "type": "integer", + "minimum": 1, + "maximum": 900 + }, + "extensions": { + "$ref": "#/definitions/extensions" + } + }, + "$comment": "Canonical built-in policy document. The assessment policy.digest is SHA-256 of JCS(this document)." + }, + "verifierMetadata": { + "type": "object", + "required": [ + "schema", + "issuer", + "publication_bases", + "keys", + "policies", + "live_status" + ], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-verifier-metadata-v1" + }, + "issuer": { + "$ref": "#/definitions/httpsIdentifier" + }, + "publication_bases": { + "type": "array", + "minItems": 1, + "maxItems": 4, + "uniqueItems": true, + "items": { + "$ref": "#/definitions/httpsIdentifier" + } + }, + "keys": { + "type": "array", + "minItems": 1, + "maxItems": 4, + "items": { + "$ref": "#/definitions/keyValidity" + } + }, + "policies": { + "type": "array", + "minItems": 0, + "maxItems": 16, + "items": { + "$ref": "#/definitions/policy" + } + }, + "live_status": { + "type": "boolean" + } + }, + "$comment": "Unsigned convenience document at {issuer}/v1/issuer. Untrusted until confirmed out of band and pinned; never changes trust by itself." + }, + "errorBody": { + "type": "object", + "required": ["schema", "error"], + "additionalProperties": false, + "properties": { + "schema": { + "const": "ts-publisher-error-v1" + }, + "error": { + "enum": [ + "invalid_request", + "unknown_kid", + "not_found", + "method_not_allowed", + "misdirected_request", + "conflict", + "rate_limited", + "unavailable" + ] + } + }, + "$comment": "Closed error body for publisher endpoints and registry read surfaces; at most 256 bytes." + } + } +} diff --git a/scripts/README.md b/scripts/README.md index a2e10e72a8..fea5ce7374 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -24,6 +24,7 @@ their documented orchestration handles a probe deliberately. | `docs-proposal/validate.sh` | Merge SHA, work dir from `propose.sh`; Node, `npm`; no write credentials | Applies the patch, rejects paths outside the allowlist, and runs the docs gates. Leaves the edits staged. | | `docs-proposal/publish.sh` | Merge SHA, work dir from `propose.sh`; `gh` with write `GH_TOKEN` | Re-checks the allowlist without building, pushes `docs/auto/` (`docs/auto//` off `main`) unless a maintainer added to it, opens or edits its PR, and reviews each new commit per hunk. An empty rerun deletes the branch under a lease, then closes the PR; a failed branch lookup stops the run. Never writes the target branch. | | `docs-proposal/test.sh` | Git, Node, and `jq` | Tests the helpers and the propose, validate, and publish flows against a temporary repository with stubbed `copilot`, `gh`, and `npm`; removes it on exit. | +| `publisher-attestation/generate.mjs` | Pinned Node; `cd docs && npm ci` for Ajv, ESLint and Prettier | Reconstructs synthetic signed fixtures from public test-only seeds and normative policy files; `--check` is read-only and checks exact derived bytes. Follow with `node --test scripts/publisher-attestation/*.test.mjs`, fixture ESLint and Prettier checks described in the [fixture README](publisher-attestation/README.md). | ## HTML EOF runtime checks diff --git a/scripts/publisher-attestation/README.md b/scripts/publisher-attestation/README.md new file mode 100644 index 0000000000..849b5206ef --- /dev/null +++ b/scripts/publisher-attestation/README.md @@ -0,0 +1,55 @@ +# Publisher attestation fixtures + +These are synthetic protocol examples for the proposed publisher attestation +contract. They do not implement a production verifier, run a publisher endpoint, +or qualify a provider method. + +Use the Node version in `.tool-versions`. Install the documentation dependencies +with `npm ci` in `docs/`, then run from the repository root: + +```bash +node scripts/publisher-attestation/generate.mjs --check +node --test scripts/publisher-attestation/*.test.mjs +docs/node_modules/.bin/eslint --config scripts/publisher-attestation/eslint.config.mjs "scripts/publisher-attestation/*.mjs" +docs/node_modules/.bin/prettier --config docs/.prettierrc --check "scripts/publisher-attestation/*.mjs" +``` + +The decoded payloads, unsigned documents and cases in +`docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json` are the +declarative inputs. After changing them, run the generator without `--check`, +which also formats the output with the documentation Prettier configuration. The generator +reconstructs keys from fixed public **test-only** seeds and regenerates signatures, +thumbprints, binding/projection/policy hashes, challenge TXT values and embedded +transport objects. Never use these seeds or keys for real identities or data. + +The fixture's `harness` member defines independent case contexts, RFC 7396 +merge-patch overrides, selected inputs, numbered validation stages and policy +documents. The test runner evaluates the bounded cases using synthetic state; +it has no network, live DNS, provider collection, durable signer or production +trust configuration. Evidence-only success is a self-declaration. Consumer +endorsement requires matching a separately signed assessment to a fresh response. + +Schema tests cover canonical DNS/HTTPS identifiers, provider fingerprint context, +environment invariants and every ordinary fixture object. Other tests check exact +signed bytes, derived hashes, raw duplicate-member encodings, strict point +admission and the declared acceptance/rejection cases. The CI documentation job +runs the same commands. These checks complement, rather than replace, independent +cryptographic interoperability and future runtime qualification. + +Normative policy documents live in `docs/superpowers/specs/policies/`; changing a +fixture copy does not change a policy. The synthetic official-shaped policy +qualifies no real provider method. Fixture member ordering remains input-preserved; +`--check` checks derivation and formatting, not a canonical top-level member order. +The node tests independently compare signature segments (including S) and the +enrollment TXT derivation, so corrupting those fields is detected without relying +on regeneration. The noncanonical R example necessarily overlaps small-order R; +a deliberately relaxed equation test proves it is not an arbitrary bad signature. + +Every signed case supplies its expected message type. Trusted key material and +validity come only from independent case context. Status cases verify exact signed +bytes and target identifiers. Known withdrawal/compromise is modeled explicitly +in expiry-only mode. The harness has one issuer candidate: threshold 1 can pass, +and thresholds above 1 fail. Multi-issuer aggregation and a structured consumer +scope output remain runtime-library acceptance obligations, not tested outputs +of this bounded runner. Publisher membership cases simulate verifier-held account +and site authority; they do not perform DNS proof or deploy a registry. diff --git a/scripts/publisher-attestation/eslint.config.mjs b/scripts/publisher-attestation/eslint.config.mjs new file mode 100644 index 0000000000..82b59cf980 --- /dev/null +++ b/scripts/publisher-attestation/eslint.config.mjs @@ -0,0 +1,20 @@ +import { createRequire } from 'node:module' + +const require = createRequire( + new URL('../../docs/package.json', import.meta.url) +) +const js = require('@eslint/js') + +export default [ + js.configs.recommended, + { + rules: { 'no-template-curly-in-string': 'error' }, + languageOptions: { + globals: Object.fromEntries( + ['Buffer', 'URL', 'TextDecoder', 'process', 'structuredClone'].map( + (name) => [name, 'readonly'] + ) + ), + }, + }, +] diff --git a/scripts/publisher-attestation/generate.mjs b/scripts/publisher-attestation/generate.mjs new file mode 100644 index 0000000000..658c57c7bc --- /dev/null +++ b/scripts/publisher-attestation/generate.mjs @@ -0,0 +1,416 @@ +// Public TEST-ONLY seeds. Never use these keys for real identities or data. +// Decoded fixture payloads and cases are inputs; signatures and digests are outputs. +import assert from 'node:assert/strict' +import { + createHash, + createPrivateKey, + createPublicKey, + sign, +} from 'node:crypto' +import { readFileSync, writeFileSync } from 'node:fs' +import { createRequire } from 'node:module' +import { fileURLToPath } from 'node:url' + +const fixture = new URL( + '../../docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json', + import.meta.url +) +export const canonical = (value) => + JSON.stringify(value, (_key, item) => + item && !Array.isArray(item) && typeof item === 'object' + ? Object.fromEntries( + Object.keys(item) + .sort() + .map((key) => [key, item[key]]) + ) + : item + ) +export const hash = (value) => createHash('sha256').update(value).digest('hex') +const digest = (value) => ({ algorithm: 'sha256', value: hash(value) }) +const encode = (value) => Buffer.from(value).toString('base64url') +const seeds = { + publisher: Buffer.from(Array.from({ length: 32 }, (_, index) => index)), + publisher_site: Buffer.from( + Array.from({ length: 32 }, (_, index) => index + 64) + ), + verifier: Buffer.from(Array.from({ length: 32 }, (_, index) => index + 32)), +} +const keys = Object.fromEntries( + Object.entries(seeds).map(([role, seed]) => [ + role, + createPrivateKey({ + key: Buffer.concat([ + Buffer.from('302e020100300506032b657004220420', 'hex'), + seed, + ]), + type: 'pkcs8', + format: 'der', + }), + ]) +) +const thumbprint = (jwk) => + encode(createHash('sha256').update(canonical(jwk)).digest()) +const replacements = (value, mapping) => { + if (typeof value === 'string') return mapping.get(value) ?? value + if (Array.isArray(value)) + return value.map((item) => replacements(item, mapping)) + if (value && typeof value === 'object') + return Object.fromEntries( + Object.entries(value).map(([key, item]) => [ + key, + replacements(item, mapping), + ]) + ) + return value +} +function signed( + header, + payload, + role = 'publisher', + rawPayload = canonical(payload) +) { + const protectedSegment = encode(canonical(header)) + const payloadSegment = encode(rawPayload) + const signatureSegment = encode( + sign(null, Buffer.from(`${protectedSegment}.${payloadSegment}`), keys[role]) + ) + const compact = `${protectedSegment}.${payloadSegment}.${signatureSegment}` + return { + protected: header, + payload, + protected_segment: protectedSegment, + payload_segment: payloadSegment, + signature_segment: signatureSegment, + compact_jws: compact, + compact_sha256: digest(compact), + } +} +function withSignature(object, signature) { + return { + compact_jws: `${object.protected_segment}.${object.payload_segment}.${encode(signature)}`, + } +} +const littleInteger = (bytes) => + BigInt(`0x${Buffer.from(bytes).reverse().toString('hex')}`) +const littleBytes = (value) => + Buffer.from(value.toString(16).padStart(64, '0'), 'hex').reverse() +const order = (1n << 252n) + 27742317777372353535851937790883648493n + +export function generate(input) { + let output = structuredClone(input) + const map = new Map() + for (const role of Object.keys(keys)) { + const jwk = createPublicKey(keys[role]).export({ format: 'jwk' }) + const kid = thumbprint(jwk) + map.set(output.public_keys[role].kid, kid) + map.set(output.public_keys[role].jwk.x, jwk.x) + } + for (const [name, document] of Object.entries(output.unsigned_objects)) { + if (document.schema !== 'ts-publisher-policy-v1') continue + output.unsigned_objects[name] = JSON.parse( + readFileSync( + new URL( + `../../docs/superpowers/specs/policies/${document.id}.json`, + import.meta.url + ) + ) + ) + } + for (const document of Object.values(output.unsigned_objects).filter( + (item) => item.schema === 'ts-publisher-policy-v1' + )) { + for (const object of Object.values(output.signed_objects)) { + if ( + object.payload.policy?.id === document.id && + object.payload.policy.version === document.version + ) { + map.set(object.payload.policy.digest.value, hash(canonical(document))) + } + } + } + output = replacements(output, map) + const objects = output.signed_objects + for (const object of Object.values(objects)) { + const payload = object.payload + if (!payload.binding) continue + payload.binding.configuration.digest = digest( + canonical(payload.binding.configuration.projection) + ) + const bindingId = hash(canonical(payload.binding)) + map.set(payload.binding_id, bindingId) + payload.binding_id = bindingId + } + output = replacements(output, map) + const result = output.signed_objects + for (const [name, object] of Object.entries(result)) { + if (object.payload.schema !== 'ts-publisher-evidence-v1') continue + const role = Object.keys(keys).find( + (role) => + output.public_keys[role].kid === + object.payload.binding.attestation_key.kid + ) + result[name] = signed(object.protected, object.payload, role) + } + for (const [name, object] of Object.entries(result)) { + const payload = object.payload + if (payload.schema !== 'ts-publisher-assessment-v1') continue + const collected = result[object.evidence_source ?? 'collected_evidence'] + payload.evidence_digest = collected.compact_sha256 + payload.checks.key_possession.evidence_digest = collected.compact_sha256 + payload.checks.configuration.evidence_digest = + payload.binding.configuration.digest + result[name] = signed(object.protected, payload, 'verifier') + if (object.evidence_source) + result[name].evidence_source = object.evidence_source + } + for (const [name, object] of Object.entries(result)) { + if (object.payload.schema !== 'ts-publisher-status-v1') continue + result[name] = signed(object.protected, object.payload, 'verifier') + } + const evidence = result.evidence + const negatives = {} + const statusSignature = Buffer.from( + result.status.signature_segment, + 'base64url' + ) + statusSignature[63] ^= 1 + negatives.tampered_status = withSignature(result.status, statusSignature) + negatives.legacy_algorithm = { + compact_jws: signed( + { ...evidence.protected, alg: 'EdDSA' }, + evidence.payload + ).compact_jws, + } + negatives.wrong_message_type = { + compact_jws: signed( + { ...evidence.protected, typ: 'ts-publisher-status+jws' }, + evidence.payload + ).compact_jws, + } + negatives.unknown_member = { + compact_jws: signed(evidence.protected, { + ...evidence.payload, + unexpected: true, + }).compact_jws, + } + const signature = Buffer.from(evidence.signature_segment, 'base64url') + const tampered = Buffer.from(signature) + tampered[0] ^= 1 + negatives.signature_tamper = withSignature(evidence, tampered) + negatives.non_canonical_s = withSignature( + evidence, + Buffer.concat([ + signature.subarray(0, 32), + littleBytes(littleInteger(signature.subarray(32)) + order), + ]) + ) + const identity = Buffer.alloc(32) + identity[0] = 1 + const expanded = createHash('sha512').update(seeds.publisher).digest() + expanded[0] &= 248 + expanded[31] &= 63 + expanded[31] |= 64 + const challenge = + littleInteger( + createHash('sha512') + .update( + Buffer.concat([ + identity, + Buffer.from(output.public_keys.publisher.jwk.x, 'base64url'), + Buffer.from( + `${evidence.protected_segment}.${evidence.payload_segment}` + ), + ]) + ) + .digest() + ) % order + negatives.small_order_r = withSignature( + evidence, + Buffer.concat([ + identity, + littleBytes( + (challenge * littleInteger(expanded.subarray(0, 32))) % order + ), + ]) + ) + // Noncanonical identity y = p + 1 necessarily overlaps the small-order-R case. + // Hash its original bytes and set S = k*a so relaxed point comparison accepts. + const nonCanonicalIdentity = littleBytes((1n << 255n) - 18n) + const nonCanonicalChallenge = + littleInteger( + createHash('sha512') + .update( + Buffer.concat([ + nonCanonicalIdentity, + Buffer.from(output.public_keys.publisher.jwk.x, 'base64url'), + Buffer.from( + `${evidence.protected_segment}.${evidence.payload_segment}` + ), + ]) + ) + .digest() + ) % order + negatives.non_canonical_r = withSignature( + evidence, + Buffer.concat([ + nonCanonicalIdentity, + littleBytes( + (nonCanonicalChallenge * littleInteger(expanded.subarray(0, 32))) % + order + ), + ]) + ) + // Public-key admission separately isolates encoding from small-order rejection. + const nonCanonical = littleBytes((1n << 255n) - 16n) + const fingerprint = structuredClone( + result.assessment_fingerprint_only.payload + ) + fingerprint.checks.active_version.outcome = 'not_assessed' + fingerprint.checks.active_version.method = null + fingerprint.checks.active_version.observed_at = null + fingerprint.checks.active_version.evidence_digest = null + negatives.fingerprint_without_active_version = { + compact_jws: signed( + result.assessment_fingerprint_only.protected, + fingerprint, + 'verifier' + ).compact_jws, + } + const contradiction = structuredClone(evidence.payload) + contradiction.binding.platform.staging = true + contradiction.binding_id = hash(canonical(contradiction.binding)) + negatives.contradictory_environment = { + compact_jws: signed(evidence.protected, contradiction).compact_jws, + } + // Each signed variant violates exactly one schema-valid cross-field rule. + const variant = (object, role, change, rebind = false) => { + const payload = structuredClone(object.payload) + change(payload) + if (rebind) payload.binding_id = hash(canonical(payload.binding)) + return { compact_jws: signed(object.protected, payload, role).compact_jws } + } + const assessment = result.assessment + const zeroDigest = { algorithm: 'sha256', value: '0'.repeat(64) } + Object.assign(negatives, { + evidence_lifetime_exceeded: variant(evidence, 'publisher', (payload) => { + payload.expires_at = payload.issued_at + 61 + }), + evidence_key_expires_first: variant( + evidence, + 'publisher', + (payload) => { + payload.binding.attestation_key.not_after = payload.expires_at - 1 + }, + true + ), + evidence_key_not_yet_valid: variant( + evidence, + 'publisher', + (payload) => { + payload.binding.attestation_key.not_before = payload.issued_at + 1 + }, + true + ), + status_lifetime_exceeded: variant(result.status, 'verifier', (payload) => { + payload.expires_at = payload.issued_at + 61 + }), + mismatched_key_possession_digest: variant( + assessment, + 'verifier', + (payload) => { + payload.checks.key_possession.evidence_digest = zeroDigest + } + ), + mismatched_configuration_digest: variant( + assessment, + 'verifier', + (payload) => { + payload.checks.configuration.evidence_digest = zeroDigest + } + ), + reversed_observation_interval: variant( + assessment, + 'verifier', + (payload) => { + payload.observed_from = payload.observed_until + 1 + } + ), + observation_after_issuance: variant(assessment, 'verifier', (payload) => { + payload.observed_until = payload.issued_at + 1 + }), + late_assessment_issuance: variant(assessment, 'verifier', (payload) => { + payload.observed_until = payload.issued_at - 61 + payload.observed_from = payload.observed_until - 8 + }), + assessment_lifetime_exceeded: variant(assessment, 'verifier', (payload) => { + payload.expires_at = payload.observed_from + 901 + }), + }) + const raw = canonical(evidence.payload) + for (const [name, text] of [ + [ + 'duplicate_member_top_level', + raw.replace( + /,"extensions":\{\},"issued_at"/, + ',"extensions":{},"extensions":{},"issued_at"' + ), + ], + [ + 'duplicate_member_nested', + raw.replace( + '"host":"news.example.com"', + '"host":"news.example.com","host":"news.example.com"' + ), + ], + ]) { + assert.notEqual(raw, text, `${name} must alter raw JSON`) + negatives[name] = { + compact_jws: signed( + evidence.protected, + evidence.payload, + 'publisher', + text + ).compact_jws, + } + } + output.negative_encodings = negatives + const discovery = structuredClone(output.unsigned_objects.key_discovery) + discovery.keys[0].jwk.x = encode(nonCanonical) + discovery.keys[0].kid = thumbprint(discovery.keys[0].jwk) + output.negative_unsigned_objects.non_canonical_public_key = discovery + output.unsigned_objects.assessment_list.assessments = [ + result.assessment.compact_jws, + ] + output.unsigned_objects.key_history.entries = [ + { + assessment: result.assessment.compact_jws, + status: result.status.compact_jws, + }, + ] + // TXT values and canonical policy digests are part of the executable fixture contract. + output.enrollment_txt_value = `ts-publisher-v1=${encode(createHash('sha256').update(canonical(output.unsigned_objects.enrollment_challenge)).digest())}` + return output +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + const current = readFileSync(fixture, 'utf8') + const output = generate(JSON.parse(current)) + const require = createRequire( + new URL('../../docs/package.json', import.meta.url) + ) + const { format } = require('prettier') + const config = JSON.parse( + readFileSync(new URL('../../docs/.prettierrc', import.meta.url)) + ) + const bytes = await format(JSON.stringify(output, null, 2), { + ...config, + parser: 'json', + }) + if (process.argv.includes('--check')) + assert.equal( + current, + bytes, + 'run generate.mjs to regenerate exact fixture bytes' + ) + else writeFileSync(fixture, bytes) +} diff --git a/scripts/publisher-attestation/harness.test.mjs b/scripts/publisher-attestation/harness.test.mjs new file mode 100644 index 0000000000..b2bd15fdad --- /dev/null +++ b/scripts/publisher-attestation/harness.test.mjs @@ -0,0 +1,566 @@ +// Bounded synthetic case runner. This is not the production consumer or collector. +import assert from 'node:assert/strict' +import { createPublicKey, verify } from 'node:crypto' +import { readFileSync } from 'node:fs' +import { createRequire } from 'node:module' +import test from 'node:test' +import { canonical, hash } from './generate.mjs' +import { strictPoint } from './point-admission.mjs' + +const require = createRequire( + new URL('../../docs/package.json', import.meta.url) +) +const Ajv = require('ajv') +const schema = JSON.parse( + readFileSync( + new URL( + '../../docs/superpowers/specs/schemas/publisher-attestation-v1.schema.json', + import.meta.url + ) + ) +) +const vectors = JSON.parse( + readFileSync( + new URL( + '../../docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json', + import.meta.url + ) + ) +) +const ajv = new Ajv({ allErrors: true }) +ajv.addSchema(schema, 'attestation') +const reject = (stage, reason_code) => { + throw { accept: false, stage, reason_code } +} +function merge(target, patch) { + if (!patch || typeof patch !== 'object' || Array.isArray(patch)) + return structuredClone(patch) + const result = + target && typeof target === 'object' && !Array.isArray(target) + ? structuredClone(target) + : {} + for (const [key, value] of Object.entries(patch)) { + if (value === null) delete result[key] + else result[key] = merge(result[key], value) + } + return result +} +function parse(raw) { + try { + const parsed = JSON.parse(raw) + const tokens = + raw.match( + /"(?:[^"\\]|\\.)*"|[{}[\]:,]|true|false|null|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g + ) ?? [] + let index = 0 + function value(depth = 0) { + const token = tokens[index++] + if (token !== '{' && token !== '[') return + if (depth >= 16) reject(1, 'malformed') + const keys = new Set() + const close = token === '{' ? '}' : ']' + while (tokens[index] !== close) { + if (token === '{') { + const key = JSON.parse(tokens[index++]) + if (keys.has(key)) reject(1, 'malformed') + keys.add(key) + index++ + } + value(depth + 1) + if (tokens[index] !== ',') break + index++ + } + index++ + } + value() + return parsed + } catch (error) { + if (error?.accept === false) throw error + reject(1, 'malformed') + } +} +function admittedKey(key, stage = 4, reason = 'malformed') { + try { + if ( + key.kid !== + Buffer.from(hash(canonical(key.jwk)), 'hex').toString('base64url') + ) + reject(stage, reason) + strictPoint(Buffer.from(key.jwk.x, 'base64url')) + } catch { + reject(stage, reason) + } +} +function decoded(compact, context, expectedType) { + const limits = { + 'ts-publisher-evidence+jws': 16384, + 'ts-publisher-assessment+jws': 32768, + 'ts-publisher-status+jws': 8192, + } + if (!limits[expectedType]) reject(2, 'signature_invalid') + const segments = compact.split('.') + if ( + segments.length !== 3 || + Buffer.byteLength(compact) > limits[expectedType] + ) + reject(1, 'malformed') + const [headerSegment, payloadSegment, signatureSegment] = segments + for (const segment of segments) + if ( + !segment || + Buffer.from(segment, 'base64url').toString('base64url') !== segment + ) + reject(1, 'malformed') + const json = (segment) => { + try { + return parse( + new TextDecoder('utf-8', { fatal: true }).decode( + Buffer.from(segment, 'base64url') + ) + ) + } catch (error) { + if (error?.accept === false) throw error + reject(1, 'malformed') + } + } + const header = json(headerSegment) + const payload = json(payloadSegment) + const evidence = expectedType === 'ts-publisher-evidence+jws' + if (!ajv.validate('attestation', header) || header.typ !== expectedType) + reject(2, 'signature_invalid') + if (!evidence && payload.issuer !== context.trusted_verifier.issuer) + reject(2, 'policy_untrusted') + const key = evidence + ? payload.binding?.attestation_key + : context.trusted_verifier + if ( + !key?.jwk || + header.kid !== key.kid || + key.kid !== + Buffer.from(hash(canonical(key.jwk)), 'hex').toString('base64url') || + (!evidence && + !(key.not_before <= context.now && context.now < key.not_after)) + ) + reject(2, 'signature_invalid') + admittedKey(key, 3, 'signature_invalid') + const signature = Buffer.from(signatureSegment, 'base64url') + if (signature.length !== 64) reject(3, 'signature_invalid') + try { + strictPoint(signature.subarray(0, 32)) + } catch { + reject(3, 'signature_invalid') + } + const scalar = BigInt( + `0x${Buffer.from(signature.subarray(32)).reverse().toString('hex')}` + ) + if (scalar >= (1n << 252n) + 27742317777372353535851937790883648493n) + reject(3, 'signature_invalid') + try { + if ( + !verify( + null, + Buffer.from(`${headerSegment}.${payloadSegment}`), + createPublicKey({ key: key.jwk, format: 'jwk' }), + signature + ) + ) + reject(3, 'signature_invalid') + } catch { + reject(3, 'signature_invalid') + } + const platform = payload.binding?.platform + if ( + platform?.provider === 'fastly' && + ((platform.staging === true && + payload.binding.environment === 'production') || + (platform.staging === false && payload.binding.environment === 'staging')) + ) + reject(4, 'environment_mismatch') + const payloadSchemas = { + 'ts-publisher-evidence+jws': 'ts-publisher-evidence-v1', + 'ts-publisher-assessment+jws': 'ts-publisher-assessment-v1', + 'ts-publisher-status+jws': 'ts-publisher-status-v1', + } + if ( + payload.schema !== payloadSchemas[expectedType] || + !ajv.validate('attestation', payload) + ) + reject(4, 'malformed') + if (payload.binding) { + if (payload.binding_id !== hash(canonical(payload.binding))) + reject(4, 'malformed') + if ( + payload.binding.configuration.digest.value !== + hash(canonical(payload.binding.configuration.projection)) + ) + reject(4, 'malformed') + } + if (!(payload.issued_at < payload.expires_at)) reject(4, 'malformed') + if (payload.schema === 'ts-publisher-assessment-v1') { + const sameDigest = (left, right) => canonical(left) === canonical(right) + if ( + !sameDigest( + payload.checks.key_possession.evidence_digest, + payload.evidence_digest + ) || + !sameDigest( + payload.checks.configuration.evidence_digest, + payload.binding.configuration.digest + ) || + !(payload.observed_from <= payload.observed_until) || + !(payload.observed_until <= payload.issued_at) || + payload.issued_at > payload.observed_until + 60 || + payload.expires_at > payload.observed_from + 900 + ) + reject(4, 'malformed') + } else if (payload.expires_at > payload.issued_at + 60) reject(4, 'malformed') + if (evidence) { + const window = payload.binding.attestation_key + if ( + !(window.not_before <= payload.issued_at) || + !(payload.expires_at <= window.not_after) + ) + reject(4, 'key_unauthorized') + } + return payload +} +function freshTime(payload, context) { + if (payload.nonce !== context.nonce) reject(6, 'nonce_mismatch') + if (payload.issued_at > context.now + 30 || context.now >= payload.expires_at) + reject(6, 'stale') +} +function evaluate(item) { + const context = merge(vectors.external_context, item.context_overrides) + if (item.unsigned_object) { + const object = + vectors.unsigned_objects[item.unsigned_object] ?? + vectors.negative_unsigned_objects[item.unsigned_object] + if (!ajv.validate('attestation', object)) + reject(item.pipeline === 'verifier_issuance' ? 1 : 4, 'malformed') + for (const key of object.keys ?? []) admittedKey(key) + return { accept: true, stage: 4 } + } + const compact = item.signed_object + ? vectors.signed_objects[item.signed_object].compact_jws + : vectors.negative_encodings[item.negative_encoding].compact_jws + const signedInput = (name) => + (vectors.signed_objects[name] ?? vectors.negative_encodings[name]) + .compact_jws + if (item.pipeline === 'verifier_issuance') { + let payload + try { + payload = decoded(compact, context, item.expected_type) + } catch (error) { + if (error?.accept === false) reject(1, error.reason_code) + throw error + } + issuance(payload, context) + return { accept: true, stage: 3 } + } + const assessment = item.expected_type === 'ts-publisher-assessment+jws' + // Each stage applies to the fresh evidence before the candidate assessment. + const fresh = assessment + ? decoded( + signedInput(context.fresh_evidence), + context, + 'ts-publisher-evidence+jws' + ) + : null + const payload = decoded(compact, context, item.expected_type) + return consumer(payload, fresh, context) +} +function issuance(payload, context) { + const publisher = context.publisher_authorization + if ( + payload.binding && + (!publisher?.authorization_record_active || + publisher.proof_verified !== true || + !Number.isSafeInteger(publisher.not_before) || + !Number.isSafeInteger(publisher.not_after) || + !(publisher.not_before < publisher.not_after) || + typeof context.authenticated_account !== 'string' || + !Array.isArray(publisher.authorized_sites) || + publisher.publisher_id !== payload.binding.publisher_id || + publisher.authenticated_account !== context.authenticated_account || + !publisher.authorized_sites.includes(payload.binding.host) || + context.now < publisher.not_before || + context.now >= publisher.not_after || + payload.expires_at > publisher.not_after) + ) + reject(2, 'publisher_unauthorized') + const authority = context.operator_authorization + if ( + payload.binding?.operator_id && + (!authority?.authorization_record_active || + authority.operator_id !== payload.binding.operator_id || + authority.authenticated_account !== context.authenticated_account || + authority.publisher_id !== payload.binding.publisher_id || + authority.deployment_id !== payload.binding.deployment_id || + !Number.isSafeInteger(authority.not_before) || + !Number.isSafeInteger(authority.not_after) || + !(authority.not_before < authority.not_after) || + context.now < authority.not_before || + !authority.operator_dns_account_proof_verified || + context.now >= authority.not_after || + payload.expires_at > authority.not_after) + ) + reject(2, 'operator_unauthorized') + if ( + !context.durable_authorization_history_available || + !context.signer_fence_current + ) + reject(3, 'state_recovery_required') +} +function consumer(payload, fresh, context) { + const assessment = payload.schema === 'ts-publisher-assessment-v1' + let policy + if (assessment) { + policy = context.trusted_verifier.policy_documents + .map((name) => vectors.unsigned_objects[name]) + .find( + (document) => + document.id === payload.policy.id && + document.version === payload.policy.version + ) + if ( + !policy || + !context.trusted_verifier.allowed_policies.includes(policy.id) || + hash(canonical(policy)) !== payload.policy.digest.value || + context.trusted_verifier.pinned_policy_digests[policy.id] !== + payload.policy.digest.value + ) + reject(5, 'policy_untrusted') + for (const [name, check] of Object.entries(payload.checks)) { + if ( + check.outcome === 'pass' && + check.method && + (!context.trusted_verifier.allowed_methods.includes(check.method) || + (policy.checks[name].methods !== null && + !policy.checks[name].methods.includes(check.method))) + ) + reject(5, 'policy_untrusted') + } + if ( + !context.trusted_verifier.allowed_methods.includes(payload.build.method) + ) + reject(5, 'policy_untrusted') + const methodField = { + declared: 'declared_methods', + fingerprint_only: 'fingerprint_methods', + artifact_bound: 'artifact_methods', + }[payload.build.assurance] + if ( + policy.build[methodField] !== null && + !policy.build[methodField].includes(payload.build.method) + ) + reject(5, 'policy_untrusted') + } + if (fresh) freshTime(fresh, context) + if (payload.schema === 'ts-publisher-evidence-v1') freshTime(payload, context) + if ( + payload.issued_at > context.now + 30 || + context.now >= payload.expires_at || + (assessment && + context.now >= + payload.observed_from + context.maximum_observation_age_seconds) + ) + reject(6, 'stale') + if (payload.binding) { + if (payload.binding.host !== context.expected_host) + reject(7, 'host_mismatch') + if (payload.binding.environment !== context.expected_environment) + reject(7, 'environment_mismatch') + if (assessment) { + if ( + fresh.binding_id !== payload.binding_id || + canonical(fresh.binding) !== canonical(payload.binding) + ) + reject(7, 'binding_mismatch') + } + } + if (assessment) { + if (payload.result !== 'satisfied') reject(8, 'policy_untrusted') + for (const [name, requirement] of Object.entries(policy.checks)) { + const outcome = payload.checks[name].outcome + if ( + outcome !== 'pass' && + !( + requirement.requirement === 'pass_or_not_assessed' && + outcome === 'not_assessed' + ) + ) + reject(8, 'malformed') + } + if ( + payload.result !== 'satisfied' || + !policy.build.assurance.includes(payload.build.assurance) || + payload.expires_at > + payload.observed_from + policy.max_assessment_lifetime_seconds + ) + reject(8, 'malformed') + if ( + payload.build.assurance === 'artifact_bound' && + !context.production_provider_qualification + ) + reject(9, 'artifact_binding_unproven') + } + if (context.known_key_compromise) reject(10, 'key_compromised') + if (context.known_withdrawal) reject(10, 'withdrawn') + let status + if (context.require_status || payload.schema === 'ts-publisher-status-v1') { + if (!context.status_available) reject(10, 'status_unavailable') + try { + status = + payload.schema === 'ts-publisher-status-v1' + ? payload + : decoded( + ( + vectors.signed_objects[context.status_object] ?? + vectors.negative_encodings[context.status_object] + ).compact_jws, + context, + 'ts-publisher-status+jws' + ) + } catch { + reject(10, 'status_unavailable') + } + const target = assessment + ? payload + : decoded( + vectors.signed_objects[context.target_assessment].compact_jws, + context, + 'ts-publisher-assessment+jws' + ) + if ( + status.issuer !== target.issuer || + status.enrollment_id !== target.enrollment_id || + status.assessment_id !== target.assessment_id || + status.binding_id !== target.binding_id || + status.publisher_key_kid !== target.binding.attestation_key.kid || + status.sequence < context.highest_status_sequence || + status.issued_at > context.now + 30 || + context.now >= + Math.min( + status.expires_at, + status.issued_at + context.configured_status_max_age + ) + ) + reject(10, 'status_unavailable') + if (status.publisher_key_status === 'compromised') + reject(10, 'key_compromised') + if (status.publisher_key_status === 'retired') + reject(10, 'key_unauthorized') + if ( + status.assessment_status !== 'current' || + status.enrollment_status !== 'active' || + status.publisher_key_status !== 'active' + ) + reject(10, 'withdrawn') + } + if ( + context.cache_action === 'reuse' && + context.cached_consumer_result?.required_status_dependencies.some( + (dependency) => dependency.sequence < context.highest_status_sequence + ) + ) + reject(10, 'status_unavailable') + if (context.distinct_issuer_threshold > 1) reject(11, 'policy_untrusted') + if (!assessment) return { accept: true, stage: payload.nonce ? 7 : 11 } + const deadlines = [ + payload.expires_at, + context.trusted_verifier.not_after, + payload.observed_from + context.maximum_observation_age_seconds, + ] + if (context.require_status) + deadlines.push( + status.expires_at, + status.issued_at + context.configured_status_max_age + ) + if (typeof context.maximum_cache_age_seconds === 'number') + deadlines.push(context.now + context.maximum_cache_age_seconds) + if (typeof context.trusted_key_accept_until === 'number') + deadlines.push(context.trusted_key_accept_until) + return { accept: true, stage: 11, valid_until: Math.min(...deadlines) } +} +for (const item of vectors.cases) { + test(`declared semantic case: ${item.id}`, () => { + let actual + try { + actual = evaluate(item) + } catch (error) { + if (!('accept' in error)) throw error + actual = error + } + for (const key of ['accept', 'stage', 'reason_code', 'valid_until']) + if (key in item.expected) + assert.equal(actual[key], item.expected[key], `${item.id}: ${key}`) + }) +} +test('merge patch preserves nested policy pins and removes null members', () => { + assert.deepEqual( + merge( + { policy: { pin: 1, method: 2 }, cache: 3 }, + { policy: { method: 4 }, cache: null } + ), + { policy: { pin: 1, method: 4 } } + ) +}) +test('strict JSON parser enforces depth, duplicate names, syntax and trailing-data bounds', () => { + assert.doesNotThrow( + () => parse('['.repeat(16) + '0' + ']'.repeat(16)), + '16 containers should be allowed' + ) + for (const raw of [ + '['.repeat(17) + '0' + ']'.repeat(17), + '{"x":1,"x":2}', + '{"x":{"y":1,"y":2}}', + '{} trailing', + '{"x":}', + '', + ]) + assert.throws( + () => parse(raw), + (error) => error.stage === 1 && error.reason_code === 'malformed', + `invalid JSON should reject at stage 1: ${raw}` + ) +}) +test('compact envelope bounds follow the independently expected message type', () => { + // Valid base64url/JSON up to stage 1; the empty header then fails stage 2. + const sized = (length) => { + for (let filler = Math.floor((length * 3) / 4); filler > 0; filler--) { + const head = `e30.${Buffer.from(JSON.stringify({ filler: 'x'.repeat(filler) })).toString('base64url')}.` + const remaining = length - Buffer.byteLength(head) + if (remaining >= 2 && remaining <= 4) return head + 'A'.repeat(remaining) + } + throw new Error(`cannot build a ${length}-byte envelope`) + } + for (const [type, limit] of [ + ['ts-publisher-evidence+jws', 16384], + ['ts-publisher-status+jws', 8192], + ['ts-publisher-assessment+jws', 32768], + ]) { + assert.throws( + () => decoded(sized(limit), vectors.external_context, type), + (error) => error.stage === 2, + `${type} should admit an envelope at its limit` + ) + assert.throws( + () => decoded(sized(limit + 1), vectors.external_context, type), + (error) => error.stage === 1, + `${type} should reject an envelope above its limit` + ) + } + for (const [compact, message] of [ + [ + 'e30.Iv8i.AA', + 'invalid UTF-8 inside a JSON string should reject at stage 1', + ], + ['e30=.e30.AA', 'padded base64url should reject at stage 1'], + ]) + assert.throws( + () => + decoded(compact, vectors.external_context, 'ts-publisher-evidence+jws'), + (error) => error.stage === 1, + message + ) +}) diff --git a/scripts/publisher-attestation/point-admission.mjs b/scripts/publisher-attestation/point-admission.mjs new file mode 100644 index 0000000000..80f71517d1 --- /dev/null +++ b/scripts/publisher-attestation/point-admission.mjs @@ -0,0 +1,42 @@ +// Test-only point admission for synthetic interoperability fixtures. +import assert from 'node:assert/strict' + +const prime = (1n << 255n) - 19n +const mod = (value) => ((value % prime) + prime) % prime +const power = (value, exponent) => { + let result = 1n + while (exponent) { + if (exponent & 1n) result = mod(result * value) + value = mod(value * value) + exponent >>= 1n + } + return result +} +const integer = (bytes) => + BigInt(`0x${Buffer.from(bytes).reverse().toString('hex')}`) +const inverse = (value) => power(value, prime - 2n) +const curve = mod(-121665n * inverse(121666n)) +function point(bytes) { + const encoded = integer(bytes) + const y = encoded & ((1n << 255n) - 1n) + assert.ok(y < prime, 'point encoding must be canonical') + const square = mod((y * y - 1n) * inverse(curve * y * y + 1n)) + let x = power(square, (prime + 3n) / 8n) + if (mod(x * x) !== square) x = mod(x * power(2n, (prime - 1n) / 4n)) + assert.equal(mod(x * x), square, 'point must lie on the curve') + const sign = encoded >> 255n + assert.ok(x !== 0n || sign === 0n, 'zero x cannot carry the sign bit') + if ((x & 1n) !== sign) x = prime - x + return [x, y] +} +export function strictPoint(bytes) { + assert.equal(bytes.length, 32, 'point must contain exactly 32 bytes') + let [x, y] = point(bytes) + for (let i = 0; i < 3; i++) { + const product = mod(curve * x * x * y * y) + const nextX = mod(2n * x * y * inverse(1n + product)) + y = mod((y * y + x * x) * inverse(1n - product)) + x = nextX + } + assert.ok(x !== 0n || y !== 1n, 'point must not have small order') +} diff --git a/scripts/publisher-attestation/schema.test.mjs b/scripts/publisher-attestation/schema.test.mjs new file mode 100644 index 0000000000..07a0a07268 --- /dev/null +++ b/scripts/publisher-attestation/schema.test.mjs @@ -0,0 +1,166 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { createRequire } from 'node:module' +import test from 'node:test' + +const require = createRequire( + new URL('../../docs/package.json', import.meta.url) +) +const Ajv = require('ajv') +const schema = JSON.parse( + readFileSync( + new URL( + '../../docs/superpowers/specs/schemas/publisher-attestation-v1.schema.json', + import.meta.url + ) + ) +) +const vectors = JSON.parse( + readFileSync( + new URL( + '../../docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json', + import.meta.url + ) + ) +) +const ajv = new Ajv({ allErrors: true, schemaId: 'auto' }) +ajv.addSchema(schema, 'attestation') +const validate = (name, value) => + ajv.validate({ $ref: `attestation#/definitions/${name}` }, value) + +for (const [name, object] of Object.entries(vectors.signed_objects)) { + test(`signed fixture schema: ${name}`, () => { + assert.ok( + ajv.validate('attestation', object.protected), + JSON.stringify(ajv.errors) + ) + assert.ok( + ajv.validate('attestation', object.payload), + JSON.stringify(ajv.errors) + ) + }) +} +for (const [name, object] of Object.entries(vectors.unsigned_objects)) { + test(`unsigned fixture schema: ${name}`, () => + assert.ok(ajv.validate('attestation', object), JSON.stringify(ajv.errors))) +} + +for (const host of [ + '127.0.0.1', + '0x7f.0.0.1', + '0x7f000001', + '1.2.3.0x4', + 'example.123', + 'example.0x', + '1.2.3.4.5', +]) { + test(`reject numeric host ${host}`, () => { + assert.equal( + validate('host', host), + false, + `${host}: DNS host should obey the expected admission rule` + ) + assert.equal( + validate('httpsIdentifier', `https://${host}/path`), + false, + `${host}: HTTPS identifier should obey the expected host admission rule` + ) + }) +} +for (const host of [ + 'news.example.com', + 'xn--nxasmq6b.example', + `${'a'.repeat(63)}.example`, +]) { + test(`preserve DNS host ${host}`, () => { + assert.equal( + validate('host', host), + true, + `${host}: DNS host should obey the expected admission rule` + ) + assert.equal( + validate('httpsIdentifier', `https://${host}/path`), + true, + `${host}: HTTPS identifier should obey the expected host admission rule` + ) + }) +} +test('reject identifier dot segments', () => { + for (const path of ['/./x', '/x/..', '/../x']) + assert.equal( + validate('httpsIdentifier', `https://example.com${path}`), + false, + `${path}: identifier should reject dot segments` + ) +}) +test('fingerprint requires a known staging flag', () => { + const assessment = structuredClone( + vectors.signed_objects.assessment_fingerprint_only.payload + ) + assessment.binding.platform.staging = null + assert.equal( + validate('assessment', assessment), + false, + 'fingerprint assessment should reject unknown staging' + ) +}) +for (const [staging, environment, valid] of [ + [true, 'production', false], + [false, 'staging', false], + [true, 'staging', true], + [false, 'production', true], + [true, 'development', true], + [false, 'development', true], +]) { + test(`Fastly staging=${staging}, environment=${environment}`, () => { + const binding = structuredClone( + vectors.signed_objects.assessment_fingerprint_only.payload.binding + ) + binding.platform.staging = staging + binding.environment = environment + assert.equal( + validate('binding', binding), + valid, + 'binding should enforce the provider/environment invariant' + ) + }) +} +test('Axum is development only', () => { + const binding = structuredClone( + vectors.signed_objects.evidence.payload.binding + ) + binding.platform = { + provider: 'axum', + service_id: null, + service_version: null, + staging: null, + } + binding.environment = 'production' + assert.equal( + validate('binding', binding), + false, + 'binding should enforce the provider/environment invariant' + ) + binding.environment = 'development' + assert.equal( + validate('binding', binding), + true, + 'binding should enforce the provider/environment invariant' + ) +}) +test('fingerprint-only assurance requires a provider package hash', () => { + const build = structuredClone( + vectors.signed_objects.assessment_fingerprint_only.payload.build + ) + assert.equal( + validate('build', build), + true, + 'fingerprint-only build with a provider hash should be valid' + ) + build.provider_files_hash = null + assert.equal( + validate('build', build), + false, + 'fingerprint-only build without a provider hash should be invalid' + ) +}) diff --git a/scripts/publisher-attestation/strict.test.mjs b/scripts/publisher-attestation/strict.test.mjs new file mode 100644 index 0000000000..70dc7955bf --- /dev/null +++ b/scripts/publisher-attestation/strict.test.mjs @@ -0,0 +1,189 @@ +// Fixture admission probes, not a production verifier. +import assert from 'node:assert/strict' +import { createHash, createPublicKey, verify } from 'node:crypto' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const vectors = JSON.parse( + readFileSync( + new URL( + '../../docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json', + import.meta.url + ) + ) +) +import { strictPoint } from './point-admission.mjs' +const prime = (1n << 255n) - 19n +const integer = (bytes) => + BigInt(`0x${Buffer.from(bytes).reverse().toString('hex')}`) + +test('ordinary fixture keys pass canonical point and small-order checks', () => { + for (const key of Object.values(vectors.public_keys)) + strictPoint(Buffer.from(key.jwk.x, 'base64url')) +}) +test('noncanonical public-key fixture isolates encoding from small-order admission', () => { + const key = vectors.negative_unsigned_objects.non_canonical_public_key.keys[0] + const bytes = Buffer.from(key.jwk.x, 'base64url') + assert.equal( + integer(bytes), + prime + 3n, + 'noncanonical public key should encode y = p + 3' + ) + assert.throws( + () => strictPoint(bytes), + /canonical/, + 'noncanonical public key should fail canonicality admission' + ) + const reduced = Buffer.alloc(32) + reduced[0] = 3 + assert.doesNotThrow( + () => strictPoint(reduced), + 'reduced public-key point should pass small-order admission' + ) +}) +test('small-order discovery key fails admission', () => { + const key = + vectors.negative_unsigned_objects.small_order_key_discovery.keys[0] + assert.throws( + () => strictPoint(Buffer.from(key.jwk.x, 'base64url')), + /small order/, + 'small-order discovery key should fail point admission' + ) +}) +test('R fixtures exercise overlapping canonicality and small-order rejection', () => { + const signature = (name) => + Buffer.from( + vectors.negative_encodings[name].compact_jws.split('.')[2], + 'base64url' + ) + assert.throws( + () => strictPoint(signature('non_canonical_r').subarray(0, 32)), + /canonical/, + 'noncanonical R should fail canonicality admission' + ) + assert.throws( + () => strictPoint(signature('small_order_r').subarray(0, 32)), + /small order/, + 'canonical identity R should fail small-order admission' + ) + for (const object of Object.values(vectors.signed_objects)) + strictPoint( + Buffer.from(object.signature_segment, 'base64url').subarray(0, 32) + ) +}) +test('duplicate-member examples carry genuine signatures over the raw invalid JSON', () => { + const key = createPublicKey({ + key: vectors.public_keys.publisher.jwk, + format: 'jwk', + }) + for (const name of [ + 'duplicate_member_top_level', + 'duplicate_member_nested', + ]) { + const [header, payload, signature] = + vectors.negative_encodings[name].compact_jws.split('.') + assert.equal( + verify( + null, + Buffer.from(`${header}.${payload}`), + key, + Buffer.from(signature, 'base64url') + ), + true, + `${name}: original duplicate-member bytes should have a valid signature` + ) + const raw = Buffer.from(payload, 'base64url').toString() + // A permissive JSON parser accepts these. Admission must inspect original bytes. + assert.doesNotThrow( + () => JSON.parse(raw), + `${name}: ordinary JSON.parse should accept the duplicate-member bytes` + ) + assert.notEqual( + JSON.stringify(JSON.parse(raw)), + raw, + `${name}: ordinary parsing should lose the original duplicate-member bytes` + ) + } +}) +test('point admission requires exactly 32 bytes', () => { + const valid = Buffer.from(vectors.public_keys.publisher.jwk.x, 'base64url') + for (const bytes of [ + Buffer.alloc(0), + valid.subarray(0, 31), + Buffer.concat([valid, Buffer.alloc(1)]), + Buffer.concat([valid, Buffer.alloc(3)]), + ]) + assert.throws( + () => strictPoint(bytes), + /32 bytes/, + `${bytes.length}-byte point must reject before arithmetic` + ) +}) + +// Deliberately relaxed affine equation, used only to distinguish these test vectors +// from arbitrary signature tampering. This is not a production verifier. +const mod = (value) => ((value % prime) + prime) % prime +const power = (value, exponent) => { + let result = 1n + while (exponent) { + if (exponent & 1n) result = mod(result * value) + value = mod(value * value) + exponent >>= 1n + } + return result +} +const inverse = (value) => power(value, prime - 2n) +const curve = mod(-121665n * inverse(121666n)) +function relaxedPoint(bytes) { + const encoded = integer(bytes) + const y = mod(encoded & ((1n << 255n) - 1n)) + const square = mod((y * y - 1n) * inverse(curve * y * y + 1n)) + let x = power(square, (prime + 3n) / 8n) + if (mod(x * x) !== square) x = mod(x * power(2n, (prime - 1n) / 4n)) + assert.equal(mod(x * x), square, 'relaxed test point must lie on the curve') + if ((x & 1n) !== encoded >> 255n) x = mod(-x) + return [x, y] +} +function add([x, y], [u, v]) { + const product = mod(curve * x * u * y * v) + return [ + mod((x * v + y * u) * inverse(1n + product)), + mod((y * v + x * u) * inverse(1n - product)), + ] +} +function multiply(point, scalar) { + let result = [0n, 1n] + while (scalar) { + if (scalar & 1n) result = add(result, point) + point = add(point, point) + scalar >>= 1n + } + return result +} +test('identity R negatives satisfy the relaxed cofactorless equation', () => { + const base = relaxedPoint( + Buffer.from( + '5866666666666666666666666666666666666666666666666666666666666666', + 'hex' + ) + ) + const key = Buffer.from(vectors.public_keys.publisher.jwk.x, 'base64url') + for (const name of ['small_order_r', 'non_canonical_r']) { + const [header, payload, encoded] = + vectors.negative_encodings[name].compact_jws.split('.') + const signature = Buffer.from(encoded, 'base64url') + const r = signature.subarray(0, 32) + const challenge = + integer( + createHash('sha512') + .update(Buffer.concat([r, key, Buffer.from(`${header}.${payload}`)])) + .digest() + ) % + ((1n << 252n) + 27742317777372353535851937790883648493n) + assert.deepEqual( + multiply(base, integer(signature.subarray(32))), + add(relaxedPoint(r), multiply(relaxedPoint(key), challenge)), + `${name}: relaxed point comparison must accept, so this is not ordinary signature tampering` + ) + } +}) diff --git a/scripts/publisher-attestation/vectors.test.mjs b/scripts/publisher-attestation/vectors.test.mjs new file mode 100644 index 0000000000..e209575a47 --- /dev/null +++ b/scripts/publisher-attestation/vectors.test.mjs @@ -0,0 +1,199 @@ +import assert from 'node:assert/strict' +import { createHash, createPublicKey, verify } from 'node:crypto' +import { readFileSync } from 'node:fs' +import test from 'node:test' +import { generate } from './generate.mjs' + +const vectors = JSON.parse( + readFileSync( + new URL( + '../../docs/superpowers/specs/fixtures/publisher-attestation-v1-vectors.json', + import.meta.url + ) + ) +) +const digest = (value) => createHash('sha256').update(value).digest('hex') +const canonical = (value) => + JSON.stringify(value, (_key, item) => + item && !Array.isArray(item) && typeof item === 'object' + ? Object.fromEntries( + Object.keys(item) + .sort() + .map((key) => [key, item[key]]) + ) + : item + ) + +for (const [name, object] of Object.entries(vectors.signed_objects)) { + test(`exact signed bytes and dependent hashes: ${name}`, () => { + const [header, payload, signature] = object.compact_jws.split('.') + assert.equal( + Buffer.from(header, 'base64url').toString(), + canonical(object.protected), + `${name}: protected segment should match canonical header bytes` + ) + assert.equal( + Buffer.from(payload, 'base64url').toString(), + canonical(object.payload), + `${name}: payload segment should match canonical payload bytes` + ) + assert.equal( + object.signature_segment, + signature, + `${name}: signature_segment matches the compact signature, including S` + ) + assert.equal( + object.compact_sha256.value, + digest(object.compact_jws), + `${name}: compact_sha256 matches the compact JWS` + ) + const role = + object.payload.schema === 'ts-publisher-evidence-v1' + ? 'publisher' + : 'verifier' + const key = createPublicKey({ + key: + object.payload.schema === 'ts-publisher-evidence-v1' + ? object.payload.binding.attestation_key.jwk + : vectors.public_keys[role].jwk, + format: 'jwk', + }) + assert.equal( + verify( + null, + Buffer.from(`${header}.${payload}`), + key, + Buffer.from(signature, 'base64url') + ), + true, + `${name}: signature should verify over the original compact segments` + ) + const binding = object.payload.binding + if (binding) { + assert.equal( + object.payload.binding_id, + digest(canonical(binding)), + `${name}: binding_id should match the canonical binding digest` + ) + assert.equal( + binding.configuration.digest.value, + digest(canonical(binding.configuration.projection)), + `${name}: configuration digest should match the canonical projection` + ) + } + }) +} +test('consumer challenges differ from the verifier collection', () => { + const { + evidence, + collected_evidence: collected, + assessment, + } = vectors.signed_objects + assert.notEqual( + evidence.payload.nonce, + collected.payload.nonce, + 'consumer and verifier collection nonces should differ' + ) + assert.notEqual( + evidence.compact_sha256.value, + collected.compact_sha256.value, + 'separately challenged evidence should have distinct compact digests' + ) + assert.equal( + evidence.payload.binding_id, + collected.payload.binding_id, + 'consumer and collection evidence should describe the same binding' + ) + assert.equal( + assessment.payload.evidence_digest.value, + collected.compact_sha256.value, + 'assessment should reference the verifier-collected evidence digest' + ) +}) +test('every accepted evidence case answers its configured consumer nonce', () => { + for (const item of vectors.cases) { + if (!item.expected.accept || !item.signed_object) continue + const payload = vectors.signed_objects[item.signed_object].payload + if (payload.schema === 'ts-publisher-evidence-v1') + assert.equal( + payload.nonce, + item.context_overrides.nonce ?? vectors.external_context.nonce, + `${item.id}: evidence should echo its configured consumer nonce` + ) + } +}) +test('generator loads normative policies rather than redefining them from fixtures', () => { + const changed = structuredClone(vectors) + changed.unsigned_objects.policy_publisher_declaration_v1.max_assessment_lifetime_seconds = 800 + const regenerated = generate(changed) + assert.deepEqual( + regenerated.unsigned_objects.policy_publisher_declaration_v1, + vectors.unsigned_objects.policy_publisher_declaration_v1, + 'normative declaration policy must override altered fixture input' + ) +}) +test('immutable assessment identities are unique', () => { + const objects = Object.values(vectors.signed_objects).filter( + (item) => item.payload.schema === 'ts-publisher-assessment-v1' + ) + assert.equal( + new Set(objects.map((item) => item.payload.assessment_id)).size, + objects.length, + 'each immutable assessment should have a distinct assessment_id' + ) +}) +test('every context override has a declared default', () => { + const known = Object.keys(vectors.external_context) + for (const item of vectors.cases) + for (const key of Object.keys(item.context_overrides)) + assert.ok(known.includes(key), `${item.id}: ${key}`) + assert.equal( + 'trusted_publisher_kid' in vectors.external_context, + false, + 'consumer context should not supply an independently trusted publisher key' + ) + for (const item of vectors.cases) + assert.equal( + typeof item.expected.stage, + 'number', + `${item.id}: expected validation stage should be numeric` + ) +}) +test('strict parsing negatives retain exact duplicate-member signed bytes', () => { + for (const name of [ + 'duplicate_member_top_level', + 'duplicate_member_nested', + ]) { + const raw = Buffer.from( + vectors.negative_encodings[name].compact_jws.split('.')[1], + 'base64url' + ).toString() + assert.ok( + name === 'duplicate_member_top_level' + ? raw.includes('"extensions":{},"extensions":{}') + : raw.includes('"host":"news.example.com","host":"news.example.com"'), + `${name}: signed raw JSON should retain its own duplicate-member pattern` + ) + } +}) + +test('enrollment TXT derives from the exact canonical challenge', () => { + assert.equal( + vectors.enrollment_txt_value, + `ts-publisher-v1=${Buffer.from(digest(canonical(vectors.unsigned_objects.enrollment_challenge)), 'hex').toString('base64url')}`, + 'enrollment TXT must bind the complete canonical enrollment challenge' + ) +}) + +test('malformed operator identifier is the only invalid challenge field', () => { + const negative = structuredClone( + vectors.negative_unsigned_objects.non_canonical_operator_identifier + ) + negative.operator_id = + vectors.unsigned_objects.enrollment_challenge.operator_id + assert.deepEqual( + negative, + vectors.unsigned_objects.enrollment_challenge, + 'repairing only operator_id should recover the valid enrollment challenge' + ) +})