diff --git a/specs/ceremony-common.md b/specs/ceremony-common.md index 96b87133..e918492f 100644 --- a/specs/ceremony-common.md +++ b/specs/ceremony-common.md @@ -9,7 +9,7 @@ more platform ceremonies: the Authorization Digest, OAuth request serialization, the PKCE construction, notarized-transcript extraction, client binding, and evidence-time rules. Each platform profile owns its endpoints, ordered fields, authenticated response locations, canonical user-ID encoding, and -proof-validity ceiling. The browser protocol owns redirect transport, +proof-validity rule. The browser protocol owns redirect transport, persistence, continuation, and application control flow and code composition. ## 2. Terminology @@ -19,31 +19,53 @@ Authorization Digest: The 32-byte value binding one authorization to the in §5. Authorized Transaction Data: Opaque canonical bytes carried in the - Authorization Digest and decoded by the Consumer into one transaction's - expected arguments. + Authorization Digest and decoded by the Consumer into one + transaction's expected arguments. Authorization Nonce: Fresh 32-byte randomness that makes each Authorization Digest unique. Proof Verifier: The component on the Consumer Chain that every Consumer calls - to verify a libID proof. Its caller names the identity platform and the Platform Verifier - Version; it selects the Platform Verifier registered for that pair and - returns the verified result. It is not the party that produces proofs. - -Platform Verifier: The component on the Consumer Chain registered for one - identity platform and one Platform Verifier Version. It checks that platform's - fields and obtains attestation authenticity from the Notary Service for - each attestation its Platform Profile requires, which is no attestation at - all where that profile carries none. - -Platform Verifier Version: The unsigned 16-bit version of one identity - platform's proof statement. It is not a version of the ceremony process or - a mutable verifier-authority revision. - -Supported Version Set: The identity-platform and Platform Verifier Version - pairs a Proof Verifier currently accepts. More than one version of one + to verify a libID proof. Its caller names the identity platform and the + Verifier Version; it selects the Platform Verifier registered for that + pair, forwards the Submission Payload and native value to it unread, and + returns the result unchanged. It is not the party that produces proofs. + +Platform Verifier: The component on the Consumer Chain registered under one + identity platform and one Verifier Version. It implements exactly one + Platform Ceremony Version: it decodes that version's Submission Payload, + recomputes the Authorization Digest, validates the evidence, and returns + its verified identity outputs. Different Consumer Chains may use different + Platform Verifier implementations for the same Platform Ceremony Version. + It obtains attestation authenticity from the Notary Service for each + attestation its Platform Profile requires, which is no attestation at all + where that profile carries none. + +Platform Ceremony Version: The unsigned 16-bit `platformCeremonyVersion` + selecting one identity platform's immutable Authorization Digest + construction, OAuth construction, and platform-specific proof statement. + The digest binds it, so it is the same number on every Consumer Chain. It + identifies no Platform Verifier implementation and is not a routing key. + +Verifier Version: The unsigned 16-bit key under which a Consumer Chain's + Verifier Governance Process registers one Platform Verifier for one + identity platform. It is local to that Consumer Chain, assigned on its own + cadence, and not bound in the Authorization Digest. Two Verifier Versions + of one platform may implement the same Platform Ceremony Version. + +Supported Version Set: The identity-platform and Verifier Version pairs a + Proof Verifier currently accepts. More than one Verifier Version of one platform can be supported at the same time. +Submission Payload: The opaque byte string a Consumer passes through the Proof + Verifier to the selected Platform Verifier. Only that Platform Verifier + fixes its encoding and reads it. For every profile it carries the Platform + Ceremony Version it was built for, the operation domain, the Authorization + Nonce, the Authorized Transaction Data, the proof, the platform's + attestations in the notary's own format, and any further value the + Platform Profile requires the caller to supply. It carries no Chain ID and + no Verifier Version. + Consumer Chain: The chain whose canonical state transition consumes a libID proof. @@ -51,7 +73,7 @@ Consumer: The deterministic contract, program, module, or native transition handler on the Consumer Chain that submits a libID proof for verification and applies the Authorized Transaction Data it gets back. -Transaction Author: The Consumer-Chain principal whose authenticated authority +Transaction Author: The Consumer Chain principal whose authenticated authority permits the transaction. It can be an account, multisignature contract, program, module, or equivalent chain principal. @@ -59,7 +81,8 @@ Fee Payer: The principal economically charged for a transaction. It can differ from the Transaction Author. Transaction Submitter: The principal that delivers a transaction to the - Consumer Chain. Submission alone grants no transaction authority. + Consumer Chain. Delivering a transaction alone grants no transaction + authority. Chain ID: The 32-byte keccak256 of a Consumer Chain's canonical chain identifier. That identifier takes whatever form the chain gives it — a @@ -74,47 +97,44 @@ Chain Profile: The normative mapping from one Consumer Chain to its Chain ID, Data encoding. Submission: The complete input a Consumer passes to the Proof Verifier for - one verification: the identity platform, the Platform Verifier Version, the - operation domain, the Authorization Nonce, the Authorized Transaction Data, - the proof, the platform's attestations, `pkceNonce` where the profile uses - the PKCE construction of §7, and any further value the Platform Profile - requires the caller to supply. + one verification: the identity platform, the Verifier Version, the native + value the quotation returns, and the Submission Payload. -Platform Ceremony: The complete operation that turns one platform - authorization into a local claim preview and the exact Submission the - Consumer Chain verifies. The preview is not authority. +Ceremony: The complete off-chain process that authenticates a user's selected + identity-platform account through its platform-specific OAuth flow, derives + its canonical user ID and handle, and locally generates the exact Submission + for a Consumer. Consumer verification and the resulting state transition + are outside the Ceremony. Platform Profile: The immutable, independently versioned definition of one identity platform's ceremony: its endpoints, ordered request fields, revealed ranges, authenticated response locations, proof-validity inputs, - and, where its Attestation Count is nonzero, its pinned Notary Service and - attestation format. A profile whose Attestation Count is zero pins neither - of those two. Its constants are fixed in the Platform Verifier registered - for that platform and version, not in the Consumer. + parameter keys and rules, and, where its Attestation Count is nonzero, its + attestation protocol and format. A profile whose Attestation Count is zero + defines neither of those two. Every Platform Verifier registered for that + platform and version MUST enforce the same profile, but its implementation + and deployment are ledger-specific. The Consumer holds none of the profile + constants. Proving Circuit: The zero-knowledge circuit whose proof a Platform Verifier checks. It proves only what cannot be read from authenticated evidence. -Redirect Runtime: The immutable browser code served at a registered redirect - URI, which receives an authorization response and hands it to the Canonical - Runtime. +Redirect Runtime: The immutable browser component served at a registered + redirect URI, which receives an authorization response and delivers it + across the application's live ceremony channel. Verifier Governance Process: The authority over the verification path: the - Proof Verifier's Supported Version Set, each Platform Verifier's pinned - constants and trust roots, and the protocol parameters. It is not the - Consumer's governance. + Proof Verifier's Supported Version Set and the Verifier Version each entry + is registered under, each Platform Verifier's pinned constants and trust + roots, and the protocol parameters. It is not the Consumer's governance. Identity Platform: Google, X, GitHub, or a future source of authenticated identity evidence. "Provider" is reserved for the formal OIDC term and for the EIP-1193 wallet provider. -Token-Exchange Service: The confidential-client component that performs a - token exchange requiring a client secret, inside a notarized TLS session, - and returns the resulting attestation. - -Canonical Runtime: The immutable browser release that constructs +Canonical Runtime: The browser-side implementation that constructs Authorization Digests, performs the required local evidence checks, and - builds proofs and claim previews. + builds Submissions and derives their local identity fields. Notary Service: The role that observes a TLS session and signs the resulting attestation, and that answers whether an attestation is authentic. Its @@ -125,20 +145,23 @@ Notary Service: The role that observes a TLS session and signs the resulting inside the bytes it signed, so that signature is what binds them to the session it observed. It knows nothing of any Platform Profile: which ranges a profile expects, and what the revealed bytes must contain, are - proof-specific and belong to the Platform Verifier. A Platform Profile - whose Attestation Count is nonzero pins the exact Notary Service and the - attestation format it accepts; a profile whose Attestation Count is zero - reaches no Notary Service and pins neither. ASM-NOTARY-01 fixes what its - signature is trusted for. + proof-specific and belong to the Platform Verifier. A Platform Profile whose + Attestation Count is nonzero defines the required attestation protocol, + format, and security properties. The Verifier Governance Process selects + the exact compatible Notary Service trusted for each supported platform and + version pair; that mutable selection is not part of the Platform Ceremony + Version. A profile whose Attestation Count is zero reaches no Notary + Service. ASM-NOTARY-01 fixes what a selected service's signature is trusted + for. Notary Fee: The fixed amount a Notary Service charges for one verification, - denominated in the Consumer Chain's native asset. One submission carries + denominated in the Consumer Chain's native asset. One Submission carries one such fee for each attestation its Platform Profile requires, and no fee where that profile requires no attestation. Attestation Count: The number of entries in the closed attestation list a Platform Profile fixes under REQ-COMMON-41 — the attestations the Platform - Verifier must have verified before it accepts a submission. It is derived + Verifier must have verified before it accepts a Submission. It is derived from that list and never stated beside it, so the two cannot disagree. It is zero where the platform's evidence is a signed platform token, and two for each launch TLSNotary profile. @@ -187,20 +210,22 @@ Attestation Count: The number of entries in the closed attestation list a comparison against the current `maxFutureAttestationSkew` parameter, not part of this assumption. - ASM-PROOF-01: - A proof accepted under a profile's selected verifier artifact satisfies - that profile's complete proof statement. Verifier governance does not - replace an artifact without selecting a new profile or verifier revision. + A proof accepted under the verifier artifact selected for its platform and + version pair satisfies that Platform Profile's complete proof statement. + Verifier governance MAY replace an artifact only with one that enforces the + same statement; changing the statement requires a new Platform Ceremony + Version. - ASM-BROWSER-01: The Canonical Runtime executes unmodified, and the user agent enforces the same-origin policy over authorization responses. ## 4. Security properties -The properties below survive a malicious application operator and, where -present, a malicious Token-Exchange Service under their cited assumptions. They -assume an unmodified Canonical Runtime, the selected verifier artifact, the -Consumer, and verifier configuration. Compromise of the applicable -identity-platform signing root, notary key, proof verifier, verifier governance, +The properties below survive a malicious application operator under their +cited assumptions. They assume an unmodified Canonical Runtime, the selected +verifier artifact, the Consumer, and verifier configuration. Compromise of the +applicable +identity-platform signing root, notary key, Platform Verifier, verifier governance, browser supply chain, or Consumer Chain invalidates the properties that depend on it. @@ -247,7 +272,7 @@ of a string. ```text authorizationPreimage = operationDomain // 32 bytes - || U16BE(platformVerifierVersion) // 2 bytes + || U16BE(platformCeremonyVersion) // 2 bytes || chainId // 32 bytes || authorizationNonce // 32 bytes || U32BE(BYTE_LENGTH(transactionData)) // 4 bytes @@ -262,7 +287,7 @@ bytes plus the Authorized Transaction Data. - REQ-COMMON-01 (upholds SP-BIND-01): The Canonical Runtime MUST construct every Authorization Digest as the keccak256 of exactly the byte concatenation above. The Canonical Runtime - MUST encode `platformVerifierVersion` in exactly two bytes and the + MUST encode `platformCeremonyVersion` in exactly two bytes and the Authorized Transaction Data byte length in exactly four bytes, rejecting a value which does not fit its field. The Canonical Runtime MUST encode `operationDomain`, `chainId`, and `authorizationNonce` as exactly 32 bytes @@ -281,43 +306,53 @@ bytes plus the Authorized Transaction Data. transaction-data semantics uses a new domain string, not another digest field. -`platformVerifierVersion` identifies the complete proof statement accepted by -the selected Platform Verifier, including this Authorization Digest layout. +`platformCeremonyVersion` identifies the complete platform ceremony boundary: +this Authorization Digest layout, the platform's OAuth construction, and its +platform-specific proof statement. It is the only version the digest binds. +The Verifier Version a Consumer Chain routes on is not in the digest, so a +proof made for one ceremony version is acceptable at every Platform Verifier +implementing it. - REQ-COMMON-01B (upholds SP-BIND-01): - The Proof Verifier MUST reject a submission whose identity platform and - `platformVerifierVersion` pair lies outside its Supported Version Set. A - platform-proof change bumps that platform's version; a common Authorization - Digest change bumps every affected platform's version. An implementation - change which preserves the complete accepted statement does not. + The Proof Verifier MUST reject a Submission whose identity platform and + Verifier Version pair lies outside its Supported Version Set. The Platform + Verifier MUST reject a Submission Payload whose `platformCeremonyVersion` + differs from the one it implements. A change to any part of the ceremony + boundary bumps the affected platform's `platformCeremonyVersion`; a common + Authorization Digest change bumps every affected platform's + `platformCeremonyVersion`. A verifier implementation or deployment change + which preserves the complete boundary does not bump it; which Verifier + Version it is registered under is the Verifier Governance Process's + decision. `chainId` identifies the Consumer Chain. - REQ-COMMON-01C (upholds SP-BIND-01, SP-REPLAY-01): - The Chain Profile MUST fix the exact canonical identifier of its Consumer - Chain and the exact bytes that identifier contributes. The Chain Profile + The Chain Profile MUST fix the exact canonical identifier of its Consumer Chain and the exact bytes that identifier contributes. The Chain Profile MUST derive `chainId` as the keccak256 of those bytes, which is 32 bytes wide whatever form the identifier took. Necessity: chains identify themselves incompatibly — a number here, a string there, a genesis hash elsewhere, and some too wide for 64 bits — so the digest commits a hash of the identifier rather than the identifier itself. The Chain Profile author MUST ensure those canonical bytes differ from every other Consumer Chain on which the - same operation domains may accept libID submissions. This specification + same operation domains may accept libID Submissions. This specification supplies no global chain-identifier registry; reusing the bytes forfeits - cross-chain replay separation. The Canonical Runtime MUST take the Chain - ID it commits in the Authorization Digest from the Chain Profile of the - Consumer Chain the ceremony authorizes, sourcing it from its execution - environment where that chain exposes a chain identity to a deployed program - and from immutable deployment configuration where it does not. The Proof - Verifier MUST take the Chain ID of its digest recomputation from that same - Chain Profile value, sourced the same way. The Canonical Runtime MUST NOT - take the Chain ID from caller-controlled input. The Proof Verifier MUST NOT - take the Chain ID from Authorized Transaction Data or any other - caller-controlled input. Necessity: the runtime constructs the digest and - the verifier recomputes it, so the two must read one value or the - recomputation never matches; several Consumer Chains expose no chain - identity at execution time, so environment-sourcing cannot be required - universally; what must hold everywhere is that a caller cannot choose it. + cross-chain replay separation. The application composition MUST select the + Consumer Chain's Chain Profile and supply its canonical Chain ID to the + Canonical Runtime for each ceremony. The Canonical Runtime MUST validate and + commit that exact 32-byte value. Selecting a Chain Profile is destination + selection, not proof authority: the Platform Verifier MUST independently + take the Chain ID of its digest recomputation from its own Consumer Chain + environment or immutable deployment configuration. The Platform Verifier + MUST NOT take the Chain ID from the Submission Payload, Authorized + Transaction Data, or any other caller-controlled input. The Chain Profile + MUST expose the digest recomputation as one construction that reads the + Chain ID itself and accepts none as an argument. Necessity: an Application can select a + destination chain just as it selects the operation and its transaction data, + while independent Consumer Chain recomputation makes a proof constructed for + any other chain unusable there. Several Consumer Chains expose no + intrinsic chain identifier at execution time, so environment-sourcing + cannot be required of the browser universally. - REQ-COMMON-01D (upholds SP-BIND-01, SP-FRESH-01): The Chain Profile MUST define how the Consumer Chain authenticates the Transaction Author and supplies Block Time. The Consumer MUST obtain both @@ -336,7 +371,7 @@ its own replay nullifier. bytes. - REQ-COMMON-01F (upholds SP-BIND-01): - The Chain Profile and Consumer protocol MUST fix one exact Authorized + The Chain Profile and the Consumer's protocol MUST fix one exact Authorized Transaction Data encoding for each transaction kind. The Consumer MUST decode `transactionData` into that format and reject trailing bytes, noncanonical encodings, and any other argument shape. @@ -356,10 +391,13 @@ requirement to compare one is unsatisfiable on those paths; Google carries no compare there. Neither method is optional, and no profile uses both. - REQ-COMMON-02 (upholds SP-BIND-01): - The Proof Verifier MUST recompute the Authorization Digest from the - caller-supplied operation domain and Platform Verifier Version, its observed - Chain ID, and the `authorizationNonce` and Authorized Transaction Data - carried in the submission. + The Platform Verifier MUST recompute the Authorization Digest from the + operation domain, `authorizationNonce`, and Authorized Transaction Data it + decoded from the Submission Payload, the Platform Ceremony Version it + implements, and its observed Chain ID. Necessity: only the Platform + Verifier can read the payload. The recomputed digest is a commitment the + evidence has to match under REQ-COMMON-02A or REQ-COMMON-02B, so a caller + who changes any input produces a digest no proof opens against. - REQ-COMMON-02A (upholds SP-BIND-01): Where a Platform Profile exposes the Authorization Digest as a public proof input, the Platform Verifier MUST reject a proof whose Authorization Digest @@ -382,7 +420,7 @@ compare there. Neither method is optional, and no profile uses both. The Consumer MUST reject an Authorization Digest it has already recorded. Necessity: recording belongs to the party the operation authorizes. Recording at the Proof Verifier instead would let anyone observing a - submission call the Proof Verifier first, consume the digest, and leave the + Submission call the Proof Verifier first, consume the digest, and leave the Consumer nothing to apply — a denial of service costing the attacker only a fee. A digest is spendable once at each Consumer that accepts its operation domain, and REQ-COMMON-01A leaves domain choice with the Consumer that @@ -394,13 +432,14 @@ compare there. Neither method is optional, and no profile uses both. Submitter as the Transaction Author unless the Chain Profile authenticates them as the same principal. -The Platform Ceremony remains reusable because it proves the Authorization -Digest rather than interpreting the operation domain or Authorized Transaction -Data. Transaction Author, Fee Payer, and Transaction Submitter remain separate -roles. No platform identifier or user identifier appears in the digest. +The Ceremony remains independent of transaction semantics because it proves +the Authorization Digest rather than interpreting the operation domain or +Authorized Transaction Data. Transaction Author, Fee Payer, and Transaction +Submitter remain separate roles. No platform identifier or user identifier +appears in the digest. Conformance vector, for `operationDomain = -keccak256(UTF8("libid.claim-identity"))`, `platformVerifierVersion = 1`, +keccak256(UTF8("libid.claim-identity"))`, `platformCeremonyVersion = 1`, `chainId = keccak256(UTF8("example:1"))`, `authorizationNonce = 0x5555…5555`, and `transactionData = 0x00010203`: @@ -417,21 +456,23 @@ in §7. ### 5.1 Verification path -A Consumer never verifies a libID proof itself. Verification is four roles +An Consumer never verifies a libID proof itself. Verification is four roles on the Consumer Chain, each answering to the one above it: ```text -Consumer names the platform and version, pays the quoted fees, - records the digest, authorizes the transaction it decodes +Consumer names the platform and the Verifier Version, pays the + quoted fees, records the digest, authorizes the + transaction it decodes | v -Proof Verifier selects the Platform Verifier for that pair, recomputes the - Authorization Digest, hands it and the submission down, - returns the result +Proof Verifier selects the Platform Verifier for that pair, forwards the + Submission Payload and the value unread, returns the + result unchanged | v -Platform Verifier checks that platform's fields, verifies the proof under the - artifact selected for that pair, then calls the Notary +Platform Verifier decodes the payload, recomputes the Authorization Digest, + checks that platform's fields, verifies the proof under + the artifact selected for it, then calls the Notary Service once per attestation its profile requires — zero times for a profile carrying none | @@ -440,23 +481,37 @@ Notary Service authenticates one notary signature and charges one fee (X and GitHub only) ``` -Only the Consumer knows what the transaction means; only the Notary Service -knows whether the notary signed. Everything between them is dispatch and -field checking. The Supported Version Set lives in the Proof Verifier, and -every platform constant — endpoints, revealed ranges, trust roots, parameters -— lives in the Platform Verifier registered for that platform and version. -The Consumer holds none of them. +Only the Consumer knows what the transaction means; only the Platform +Verifier knows what the payload is; only the Notary Service knows whether the +notary signed. Everything between them is dispatch. The Supported Version Set +lives in the Proof Verifier and is keyed by Verifier Version. The Platform +Profile defines every immutable platform constant — endpoints, revealed +ranges, attestation format, validity rules, and parameter keys — and each +Consumer Chain's Platform Verifier enforces that profile and fixes the +encoding of its own Submission Payload. Verifier governance owns the mutable +verifier artifact, Notary Service, trust roots, fees, parameter values, and +the Verifier Version each verifier is registered under. The Consumer holds +none of those constants. + +Two versions travel this path, deliberately unrelated. The Platform Ceremony +Version is inside the payload and the digest, fixed by the Canonical Runtime +before the ceremony starts. The Verifier Version is beside the payload and in +no digest, learned by the Consumer from its deployment. Binding the second +into the digest would tie the Canonical Runtime to each Consumer Chain's +upgrade cadence and end a proof's validity at a verifier upgrade instead of at +the Consumer's nullifier. The last hop is conditional. A Platform Profile whose evidence is a signed platform token reaches no Notary Service at all: Google's Attestation Count is zero, so its path stops at the Platform Verifier and costs nothing. X and GitHub each verify two attestations — a token or token-exchange session and -an identity session — so one submission on either path pays two fees. +an identity session — so one Submission on either path pays two fees. - REQ-COMMON-05: The Consumer MUST call the Proof Verifier with the identity platform, the - Platform Verifier Version, the submission, and the native value the - quotation of REQ-COMMON-06E returns. That value covers one Notary Fee of + Verifier Version, the Submission Payload, and the native value the + quotation of REQ-COMMON-06E returns. The Consumer MUST NOT decode the + Submission Payload. That value covers one Notary Fee of §9.1 for each attestation the selected profile requires, and is zero where its Attestation Count is zero. Necessity: cross-component interoperability of one verification entry point serving every Consumer. @@ -464,38 +519,49 @@ an identity session — so one submission on either path pays two fees. The Proof Verifier MUST select the Platform Verifier its Supported Version Set registers for that pair. The Proof Verifier MUST NOT accept a caller-supplied verifier address. Necessity: a caller-selected verifier - verifies nothing. + verifies nothing. Each Consumer Chain selects its own implementations and + assigns its own Verifier Versions; neither is part of the Platform Ceremony + Version. The Proof Verifier MUST NOT decode the Submission Payload. - REQ-COMMON-05B: - The Proof Verifier MUST support more than one Platform Verifier Version of - one identity platform concurrently. Necessity: concurrent support is what - lets a deployment run a new version beside the one it replaces while holders - migrate. When a version leaves the Supported Version Set is the Verifier - Governance Process's decision under REQ-COMMON-05C, and this specification - fixes no minimum overlap: a ceremony stranded by a removal is recoverable, - because its holder can run the ceremony again under a supported version. + The Proof Verifier MUST support more than one Verifier Version of one + identity platform concurrently, including two that implement the same + Platform Ceremony Version. Necessity: concurrent support lets a deployment + run a new verifier beside the one it replaces, and a proof is acceptable at + every Verifier Version implementing its ceremony version, so an upgrade + strands no ceremony in flight. When a Verifier Version leaves the Supported + Version Set is the Verifier Governance Process's decision under + REQ-COMMON-05C; this specification fixes no minimum overlap, because a + stranded ceremony can be run again under a supported version. - REQ-COMMON-05C: The Verifier Governance Process MUST own every addition to and removal from the Supported Version Set. Necessity: the set decides which proof statements - the chain accepts, so it is authority, not configuration. + the Consumer Chain accepts, so it is authority, not configuration. - REQ-COMMON-05D (upholds SP-EXCHANGE-01): The Platform Verifier MUST check every field its Platform Profile assigns to it under REQ-COMMON-19E. The Platform Verifier MUST obtain attestation authenticity from the Notary Service once for each attestation its Platform Profile requires. The - Platform Verifier MUST treat each of those decisions as final. The Platform - Verifier MUST NOT call the Notary Service where its Platform Profile + Platform Verifier MUST treat each of those decisions as final. The Platform Verifier MUST NOT call + the Notary Service where its Platform Profile requires no attestation. - REQ-COMMON-05E (upholds SP-CLIENT-01): - The Platform Verifier MUST return its verified fields: the client + The Platform Verifier MUST return its verified fields: the Authorization + Digest it recomputed, the operation domain and Authorized Transaction Data + it decoded, the Platform Ceremony Version it implements, the client identifier, the canonical `userId`, the raw handle bytes, and `metadataObservedAt`. Necessity: an authenticated `userId`, handle, and - observation time are what the ceremony exists to produce, and the Consumer - has no other authenticated source for them. + observation time are what the ceremony exists to produce; the digest is the + Consumer's replay nullifier, which it cannot recompute without reading the + payload. The Consumer trusts these fields as it trusts the Platform Verifier + the Verifier Governance Process installed. - REQ-COMMON-45 (upholds SP-BIND-01, SP-EXCHANGE-01): - The Platform Verifier MUST verify the proof carried in the submission under - the exact verifier artifact the Verifier Governance Process selected for - the submitted identity platform and Platform Verifier Version. The Platform - Verifier MUST reject a submission whose proof does not verify under that + The Platform Verifier MUST verify the proof carried in the Submission + Payload under the exact verifier artifact the Verifier Governance Process + selected for it. The Verifier Governance Process MAY select a different + artifact for each Consumer Chain and each Verifier Version. The Verifier + Governance Process MUST select only artifacts that enforce the proof + statement of the Platform Ceremony Version the verifier implements. The Platform Verifier + MUST reject a Submission whose proof does not verify under that artifact. The Platform Verifier MUST NOT accept a caller-supplied artifact, verifying key, or externally computed verification result. Necessity: ASM-PROOF-01 states what an accepted proof means and presupposes that some @@ -503,46 +569,49 @@ an identity session — so one submission on either path pays two fees. role is obliged to run it, and every public input the surrounding rules compare is then a number the caller wrote down. - REQ-COMMON-46 (upholds SP-BIND-01): - The Proof Verifier MUST pass the digest it recomputed under REQ-COMMON-02, - together with the complete submission, to the Platform Verifier it - selected. The Platform Verifier MUST take the digest that REQ-COMMON-02A - and REQ-COMMON-15A compare against from that forwarded value and from - nothing else. Necessity: both of those rules compare something against a - digest recomputed one hop above them, and a Platform Verifier left to - rebuild it or to receive it another way would compare against a digest the - caller could choose. - -The operation domain travels in the submission and is authenticated by digest -recomputation rather than trusted: a submission naming a domain other than -the one the ceremony committed produces a different digest, which fails -whichever binding check of REQ-COMMON-02A and REQ-COMMON-02B its profile -uses. The Proof Verifier therefore returns the domain it authenticated, and -the Consumer decides whether that domain is its own. + The Proof Verifier MUST pass the Submission Payload and the native value to + the Platform Verifier it selected without decoding either. The Platform + Verifier MUST take the digest that REQ-COMMON-02A and REQ-COMMON-15A + compare against from its own recomputation under REQ-COMMON-02 and from + nothing else. Necessity: a digest received from the caller is a digest the + caller chose; one recomputed from the decoded payload is a commitment the + evidence has to match. + +The operation domain travels inside the Submission Payload and is +authenticated by digest recomputation rather than trusted: a payload naming a +domain other than the one the ceremony committed produces a different digest, +which fails whichever binding check of REQ-COMMON-02A and REQ-COMMON-02B its +profile uses. The Platform Verifier therefore returns the domain it +authenticated, the Proof Verifier forwards it, and the Consumer decides +whether that domain is its own. - REQ-COMMON-06 (upholds SP-BIND-01): - The Proof Verifier MUST return the authenticated operation domain, the - Authorized Transaction Data, and every field REQ-COMMON-05E lists to the - Consumer on acceptance. The Proof Verifier MUST return nothing but the - rejection on rejection. + The Proof Verifier MUST return the Platform Verifier's result to the + Consumer unchanged on acceptance: every field REQ-COMMON-05E lists. The + Proof Verifier MUST return nothing but the rejection on rejection. - REQ-COMMON-06A (upholds SP-BIND-01): The Consumer MUST reject a returned operation domain it does not own. The Consumer MUST select its transaction handler by that domain before decoding the Authorized Transaction Data under REQ-COMMON-01F. - REQ-COMMON-06B: The Proof Verifier MUST NOT decode, interpret, or apply the Authorized - Transaction Data. Necessity: transaction semantics belong to the Consumer - that fixed the operation domain. + Transaction Data, nor any other part of the Submission Payload. Necessity: + transaction semantics belong to the Consumer; the payload's shape belongs to + the Platform Verifier. - REQ-COMMON-06C (upholds SP-BIND-01): - The Proof Verifier MUST take the Chain ID of the digest recomputation of - REQ-COMMON-02 from the Chain ID it observes under ASM-CHAIN-02. The Proof - Verifier MUST NOT read a Chain ID from the submission. The Proof Verifier - MUST dispatch on the Platform Verifier Version the submission names. - Necessity: the chain the evidence was authorized for and the proof - statement that verifies it are both bound in the digest, and recomputing - that digest is the whole check on either; the submission carries no Chain - ID for anything to compare against, and the dispatched version cannot - disagree with the submitted one because dispatch reads it from the - submission in the first place. + The Platform Verifier MUST take the Chain ID of the digest recomputation of + REQ-COMMON-02 from the Chain ID it observes under ASM-CHAIN-02. Neither the + Proof Verifier nor the Platform Verifier MAY read a Chain ID from the + Submission or its payload. The Proof Verifier MUST dispatch on the Verifier + Version the Submission names. The Platform Verifier MUST reject a payload + whose Platform Ceremony Version is not the one it implements, before any + Notary Fee is delivered. Necessity: the Consumer Chain the evidence was + authorized for and the proof statement that verifies it are both bound in + the digest, and recomputing that digest is the whole check on either. The + Verifier Version is not in the digest and is checked against nothing: it + only selects which registered Platform Verifier answers. Refusing the wrong + ceremony version by name, first, keeps that refusal from surfacing as a + digest mismatch after the fees were paid. The Notary Fees of §9.1 are charged at the bottom of this path, so native value passes down it and stops where the work is done. A path with no @@ -552,17 +621,20 @@ attestation to verify carries no value at all. The Proof Verifier and the Platform Verifier MUST each reject a call whose native value differs from the value that role currently requires, read from the quotation of REQ-COMMON-06E before forwarding. The Proof Verifier MUST - forward exactly the value the Platform Verifier requires. The Platform - Verifier MUST deliver exactly one Notary Fee with each attestation + forward exactly the value the Platform Verifier requires. The Platform Verifier MUST deliver exactly + one Notary Fee with each attestation verification its Platform Profile requires, and no value at all where that profile requires none. Necessity: exact value at every hop needs no refund path, so no partial-failure or reentrancy rule is required and no value can be captured in transit. - REQ-COMMON-06E: The Proof Verifier MUST expose a fee quotation for an identity platform and - Platform Verifier Version covering the whole verification path, quoting one - Notary Fee for each attestation that pair's Platform Profile requires and - zero where it requires none. Necessity: a Consumer cannot attach a correct + Verifier Version covering the whole verification path, quoting one Notary + Fee for each attestation the registered Platform Verifier's profile + requires and zero where it requires none. The Proof Verifier MUST NOT + derive the quotation from the Submission Payload. Necessity: a price read + from the payload would let a caller deliver less than the path forwards, + and there is no refund path. Necessity: a Consumer cannot attach a correct fee if quoting requires knowing the path's internal topology, and a profile verifying two attestations costs two fees while one verifying none costs nothing. @@ -574,7 +646,7 @@ attestation to verify carries no value at all. `application/x-www-form-urlencoded` serializer, taking UTF-8 input, encoding space as `+`, and using uppercase hexadecimal percent escapes. Necessity: byte-exact request reproduction across implementations, without which the - fixed range layout of §9 does not hold. This is a runtime serialization + fixed range layout of §9 does not hold. This is a browser-side serialization rule; no circuit re-verifies it. The Canonical Runtime MUST compare a revealed raw form-value range with the exact value bytes this serializer emits for the expected unencoded value. The Canonical Runtime MUST NOT @@ -599,8 +671,9 @@ attestation to verify carries no value at all. The Deployment MUST register with each Identity Platform only redirect URIs whose origins it controls. - REQ-COMMON-30 (upholds SP-DELIVERY-01): - The Canonical Runtime MUST forward an authorization response only to the - compiled application origin. + The Canonical Runtime MUST forward an authorization response only over a live + browser channel authenticated to an exact origin in the deployment-configured + allowed application-origin set. The set MAY contain more than one origin. - REQ-COMMON-31 (upholds SP-DELIVERY-01): The Canonical Runtime MUST ignore a forwarding target supplied in the redirect request. @@ -631,59 +704,50 @@ label=A+B&redirect_uri=https%3A%2F%2Fredirect.example%2Foauth%2Fredirect&state=_ X and GitHub bind the Authorization Digest through S256 PKCE. ```text -PKCE_DOMAIN = keccak256("libid.identity.pkce") -pkceBinding = PKCE_DOMAIN || authorizationDigest || pkceNonce -verifierHash = SHA256(pkceBinding) +verifierHash = SHA256(authorizationDigest || authorizationNonce) code_verifier = BASE64URL_NOPAD(verifierHash) code_challenge = BASE64URL_NOPAD(SHA256(ASCII(code_verifier))) ``` - REQ-COMMON-12 (upholds SP-BIND-01): - The Canonical Runtime MUST derive `code_verifier` from `PKCE_DOMAIN`, the - Authorization Digest, and `pkceNonce` as shown above. `PKCE_DOMAIN` carries - no version: the Authorization Digest already binds - `platformVerifierVersion`, and a change to this construction is a change to - the proof statement, which bumps that version and its Platform Verifier. -- REQ-COMMON-13 (upholds SP-BIND-01): - The Canonical Runtime MUST draw `pkceNonce` freshly per authorization - attempt from a cryptographically secure random source. Necessity: the nonce - becomes public at submission, so a nonce reused across attempts of one - Authorization Digest would publish the verifier of an earlier attempt whose - code may still be live. + The Canonical Runtime MUST derive `code_verifier` from the exact 64-byte + concatenation of the Authorization Digest and the same + `authorizationNonce` committed by that digest, as shown above. A change to + this construction is a change to the platform ceremony boundary, which bumps + `platformCeremonyVersion` for every profile that uses it. - REQ-COMMON-14 (upholds SP-BIND-01): - The Canonical Runtime MUST NOT emit `pkceNonce` before its token exchange - completes, as a platform parameter, a redirect value, or a log field. - Necessity: until the code is redeemed, whoever learns the nonce can derive - the verifier and redeem an intercepted code; afterwards the code is spent - and the nonce protects nothing. + For a PKCE profile, the Canonical Runtime MUST NOT emit the raw + `authorizationNonce` before its token exchange completes, as a platform + parameter, a redirect value, or a log field. Necessity: until the code is + redeemed, whoever learns the nonce can derive the verifier and redeem an + intercepted code; afterwards the code is spent and the nonce protects + nothing. - REQ-COMMON-15 (upholds SP-BIND-01): The Platform Profile MUST reveal the `code_verifier` range of its token - request. The Consumer MUST carry `pkceNonce` in the submission. + request. - REQ-COMMON-15A (upholds SP-BIND-01): The Platform Verifier MUST recompute `code_verifier` from the Authorization - Digest and the submitted `pkceNonce`. The Platform Verifier MUST reject a - submission whose revealed verifier differs byte for byte. Necessity: this is what binds the - digest to the token exchange. Retargeting an attestation to another digest - would require a second-preimage of the revealed verifier. - -A fresh `pkceNonce` per attempt gives a retry of the same Authorization Digest -a distinct verifier, and keeps `code_verifier` unpredictable to anyone holding -only the public digest for as long as that matters — until the code is -redeemed. The verifier and the nonce are published afterwards, which is what -lets the Platform Verifier check this binding itself instead of trusting a -proof statement about values it cannot see. Both verifier and challenge are exactly 43 unpadded -base64url characters. `PKCE_DOMAIN` separates this hash from any other -construction over the same digest; it costs nothing, because a 64-byte and a -96-byte preimage both occupy two SHA-256 blocks. - -Conformance vector, for the Authorization Digest of §5 and -`pkceNonce = 0x4444444444444444444444444444444444444444444444444444444444444444`: + Digest and the submitted `authorizationNonce`. The Platform Verifier MUST + reject a Submission whose revealed verifier differs byte for byte. + Necessity: this is what binds the digest to the token exchange. Retargeting + an attestation to another digest would require a second-preimage of the + revealed verifier. + +The fresh `authorizationNonce` gives each ceremony both a unique Authorization +Digest and an unpredictable `code_verifier`. A new OAuth attempt is a new +ceremony and therefore receives a new nonce, digest, and verifier. The verifier +and nonce are published after the exchange, which is what lets the Platform Verifier check +this binding itself instead of trusting a proof statement about +values it cannot see. Both verifier and challenge are exactly 43 unpadded +base64url characters. + +Conformance vector, using the Authorization Digest of §5 and +`authorizationNonce = 0x5555555555555555555555555555555555555555555555555555555555555555`: ```text -PKCE_DOMAIN = 0x3961dfe56cd0f2d94e72a15b96df889fbb46968cdb37518830fc0077b0730a01 -verifierHash = 0x88c493361ea0424467046958d5cd0c50eb03ecc08ee06f02ee9875fe0219b392 -code_verifier = iMSTNh6gQkRnBGlY1c0MUOsD7MCO4G8C7ph1_gIZs5I -code_challenge = BhFqYIY1YnHafYOrrblUswFnjxFF97UvGjSgqugPQvA +verifierHash = 0xe6d7810e5e9ccf853beda170795e4f6cc84127f94416fe8b2cd2b3aa70c8e65a +code_verifier = 5teBDl6cz4U77aFweV5PbMhBJ_lEFv6LLNKzqnDI5lo +code_challenge = c8HLMaJOzc8OUoRYc7AocL5ioAkXVtAOmoGxoSY60IQ ``` ## 8. Client binding @@ -691,9 +755,9 @@ code_challenge = BhFqYIY1YnHafYOrrblUswFnjxFF97UvGjSgqugPQvA The OAuth client that issued the evidence is authenticated evidence in its own right, and it reaches the Consumer through §5.1. The Canonical Runtime also compares it against the exact client fixed by the immutable ceremony -profile before returning a local claim preview. Client admission is -permissionless: any OAuth application can produce acceptable evidence, and no -Consumer-Chain registration of clients exists. +profile before returning the locally derived identity fields. Client admission +is permissionless: any OAuth application can produce acceptable evidence, and +no Consumer Chain registration of clients exists. Every platform returns the client identifier the same way: its exact authenticated bytes. How those bytes are authenticated differs, because the @@ -701,7 +765,7 @@ evidence differs. | Identity platform | Authenticated source | How the Platform Verifier authenticates the bytes | |---|---|---| -| Google | signed ID-Token `aud` | the submission carries the bytes; the Platform Verifier hashes them and requires the digest to equal the proof's audience public input | +| Google | signed ID-Token `aud` | the Submission carries the bytes; the Platform Verifier hashes them and requires the digest to equal the proof's audience public input | | X | `client_id` in the notarized token request | the bytes are a revealed range of an attestation the Notary Service accepted | | GitHub | `client_id` in the notarized token exchange | the bytes are a revealed range of an attestation the Notary Service accepted | @@ -716,7 +780,7 @@ and returns the readable value rather than a digest of it. compare, key, and display the identifier without knowing which platform produced it. - REQ-COMMON-16A (upholds SP-CLIENT-01): - The Platform Verifier MUST reject a submission whose supplied client + The Platform Verifier MUST reject a Submission whose supplied client identifier bytes are not authenticated by that platform's evidence, by the method its row above fixes. Necessity: bytes a caller supplies and nothing checks are the caller's claim, not the platform's. @@ -729,23 +793,24 @@ and returns the readable value rather than a digest of it. client-identifier representation; accepting percent-encoded bytes would return the serialization rather than the identifier. -A Consumer that wants a fixed-size key derives one itself, as the keccak256 of +An Consumer that wants a fixed-size key derives one itself, as the keccak256 of the returned bytes. Deriving is cheap and lossless; returning only a digest is not, because the readable value cannot be recovered from it. - REQ-COMMON-17 (upholds SP-CLIENT-01): - The Canonical Runtime MUST reject a Submission whose authenticated client - identifier differs byte for byte from the client fixed by the selected - immutable ceremony profile. + The Canonical Runtime MUST reject a Submission whose authenticated + client identifier differs byte for byte from the client fixed by the + selected immutable ceremony profile. - REQ-COMMON-17C (upholds SP-CLIENT-01): The Proof Verifier and the Platform Verifier MUST NOT require the exposed - client identifier to belong to a registered set. The Consumer MAY read the exposed client identifier for its + client identifier to belong to a registered set. The Consumer + MAY read the exposed client identifier for its own semantics. Necessity: client selection is permissionless application policy; authoritative transaction permission comes from the Consumer's Transaction Author predicate over the proof-bound Authorized Transaction Data. Redirect origin, frontend origin, and application authorization remain -browser-local and produce no Consumer-Chain effect. +browser-local and produce no Consumer Chain effect. ## 9. Notarized transcripts and attestation verification @@ -754,14 +819,15 @@ three disjoint places. The Proving Circuit checks the fields the profile needs inside evidence that stays hidden — and only those fields, never the whole template. The Platform Verifier checks the fields carried in revealed attestation bytes, which it reads for itself. The Canonical Runtime checks the -ceremony state that exists only in the browser and reaches no proof and no -chain. One role's extraction of each field is the authoritative one — the +ceremony state that exists only in the browser and reaches no proof and +produces no Consumer Chain effect. One role's extraction of each field is +the authoritative one — the Proving Circuit's where the bytes stay hidden, the Platform Verifier's where -they are revealed. The Canonical Runtime may repeat an authoritative -extraction over the same bytes so the browser can preview what the exact -Submission asks the Consumer Chain to bind, and nothing on chain depends on -that repeat. A comparison on an -already-extracted value may happen in a different role again. A JSON string check matches the full `"field":"` delimiter, +they are revealed. The Canonical Runtime may repeat an authoritative extraction +over the same bytes to derive local identity fields from the exact Submission, +and nothing on the Consumer Chain depends on that repeat. A comparison on an +already-extracted value may happen in a different role again. A JSON string +check matches the full `"field":"` delimiter, the value, and its closing quote. JSON unsigned integers and booleans use the typed local matches of REQ-COMMON-19D. A form-field check asserts a field boundary, the exact ASCII name and `=`, the value, and the next `&` or body end. @@ -797,13 +863,15 @@ exposes a minimal set of public inputs, which never includes a credential. The Implementation MUST redact every byte outside the ranges its profile lists. - REQ-COMMON-18 (upholds SP-EXCHANGE-01): - The Platform Profile whose Attestation Count is nonzero MUST pin the exact - Notary Service and the attestation format it accepts. The Implementation - MUST use that format's native commitment for every hidden range of such a - profile. The Proving Circuit MUST open each hidden range whose value that - profile checks. Such a profile pinning neither is ineligible. A profile - whose Attestation Count is zero verifies no attestation, so it pins - neither and this rule does not reach it. + The Platform Profile whose Attestation Count is nonzero MUST fix the + attestation protocol, format, and required security properties. The Verifier + Governance Process MUST select an exact compatible Notary Service before it + registers a Platform Verifier for that platform on a Consumer Chain. The + Implementation MUST use that format's native commitment for every hidden + range of such a profile. The Proving Circuit MUST open each hidden range + whose value that profile checks. A profile whose Attestation Count is zero + verifies no attestation, so it defines none of those requirements and this + rule does not reach it. - REQ-COMMON-38: The Platform Profile MUST pin the hash algorithm of every range commitment its pinned attestation format carries. The Platform Verifier MUST reject an @@ -837,8 +905,8 @@ notarized request that commits a credential carried in an HTTP TLSNotary profile, and nothing else — X's `/2/users/me` request and GitHub's `/user` request. Such a request carries a signed transcript length, covers that length exactly, and admits exactly one anchored occurrence of the -credential header. The committed range is then the only region the Platform -Verifier cannot read, and its offset and length follow from the revealed +credential header. The committed range is then the only region the Platform Verifier +cannot read, and its offset and length follow from the revealed ranges around it. A credential committed in a request body is a different case and keeps its @@ -871,8 +939,7 @@ and no `authorization` needle to count. normalize the revealed request bytes by ASCII-lowercasing them and removing every space and horizontal tab. The Platform Verifier MUST leave carriage-return and line-feed bytes in - place. The Platform - Verifier MUST require exactly one occurrence of the normalized, + place. The Platform Verifier MUST require exactly one occurrence of the normalized, line-anchored credential header needle `\r\nauthorization:bearer` across all revealed request bytes, counting the region before the committed range and the region after it together. Necessity: HTTP field names and @@ -935,24 +1002,26 @@ and no `authorization` needle to count. proof statement and no Consumer Chain component reads. The Canonical Runtime MAY repeat an extraction another role owns, over the same bytes by the same algorithm, for display and local checks. The Canonical Runtime MUST return - that repeat only with the exact Submission whose bytes it read. The Canonical - Runtime MUST discard and rederive the preview if any proof, attestation, - identity platform, Platform Verifier Version, or other Submission field - changes. The Canonical Runtime MUST NOT label the preview verified before - the Consumer accepts that Submission; only the Consumer's result is - authoritative. + that repeat only with the exact Submission whose bytes it read. The + Canonical Runtime MUST discard and rederive the local identity fields if any + proof, attestation, identity platform, Platform Ceremony Version, or other + Submission field changes. The Canonical Runtime MUST NOT label those fields + authoritative before the Consumer accepts that Submission; only + the Consumer's result is authoritative. The Platform Profile MUST NOT let a proof statement or a Consumer Chain component depend on that repeat. A comparison performed on an already-extracted value is not an extraction, and the Platform Profile MAY assign it to a different role. Necessity: two authoritative extractions of one field are two answers, each side able to - assume the other checked it; the runtime's repeat is what lets the browser - preview the identity the exact Submission asks the Consumer Chain to bind, - so allowing the preview and Submission to diverge would reopen the gap where - the display names one account and the Submission binds another; and an + assume the other checked it; the Canonical Runtime's repeat is what lets the + browser derive the identity the exact Submission asks the Consumer Chain + to bind, so allowing the local identity fields and Submission to diverge + would reopen the gap where the display names one account and the Submission + binds another; and an authoritative extraction owned by a role that cannot see the bytes is a check nobody performs. The Google audience, extracted in circuit - and compared on chain, is the ordinary case the comparison sentence allows. + and compared on the Consumer Chain, is the ordinary case the comparison + sentence allows. - REQ-COMMON-19C (upholds SP-BIND-01, SP-EXCHANGE-01): The Proving Circuit extracting a field from an `application/x-www-form-urlencoded` request MUST assert that the match begins @@ -1003,7 +1072,7 @@ constant. and `redirect_uri` from its immutable deployment profile. Neither value is a Consumer input. Necessity: media type selects the platform's request parser, while redirect URI is application delivery configuration rather than - Consumer-Chain identity authority. + Consumer Chain identity authority. - REQ-COMMON-21C (upholds SP-CLIENT-01): The Proving Circuit MUST NOT embed a deployment-configured value, including a client identifier, client secret, or `redirect_uri`, as a compiled @@ -1071,7 +1140,7 @@ below governs one attestation a profile does require. censorship of a permissionless service. - REQ-COMMON-34D: The Notary Service MUST expose its current fee for reading before a - submission is constructed. Necessity: a fee that cannot be read cannot be + Submission is constructed. Necessity: a fee that cannot be read cannot be bounded. - REQ-COMMON-34E: The Notary Service MUST reject a verification whose native value differs @@ -1080,12 +1149,11 @@ below governs one attestation a profile does require. rejecting a mismatch fails the transaction visibly rather than silently overcharging the Fee Payer, and leaves no overpayment to refund. - REQ-COMMON-42: - The Platform Verifier MUST deliver the fees of one submission's attestation + The Platform Verifier MUST deliver the fees of one Submission's attestation verifications so that they all take effect together or none of them does. The Platform Verifier MUST leave no fee delivered once it rejects the - submission. The Chain Profile MUST define the mechanism by which a rejected - call leaves no value transferred and no state changed on its Consumer - Chain. Necessity: a profile verifying two attestations pays the first + Submission. The Chain Profile MUST define the mechanism by which a rejected + call leaves no value transferred and no state changed in its Consumer. Necessity: a profile verifying two attestations pays the first before it asks for the second, so a rejection at the second would otherwise keep a fee for work the Fee Payer never received. @@ -1117,7 +1185,7 @@ Service. - REQ-COMMON-26 (upholds SP-FRESH-01): The Platform Verifier MUST derive `proofValidUntil` from the platform profile's authenticated validity input and any current protocol parameter that profile - names. The Platform Verifier MUST reject a submission where + names. The Platform Verifier MUST reject a Submission where `Block Time >= proofValidUntil`. - REQ-COMMON-27 (upholds SP-FRESH-01): The Platform Verifier MUST NOT accept a caller-supplied validity bound. @@ -1127,22 +1195,23 @@ Service. ## 11. Conformance -Roles: Canonical Runtime, Redirect Runtime, Token-Exchange Service, Proving -Circuit, Proof Verifier, Platform Verifier, Notary Service, Consumer. The +Roles: Canonical Runtime, Redirect Runtime, Proving Circuit, Proof Verifier, +Platform Verifier, Notary Service, Consumer. The Implementation claiming a role MUST pass the vectors covering the constructions that role implements. - TEST-COMMON-01 (exercises REQ-COMMON-01, REQ-COMMON-01A, REQ-COMMON-01B, REQ-COMMON-01C, REQ-COMMON-01D, REQ-COMMON-01E, REQ-COMMON-01F, REQ-COMMON-02, REQ-COMMON-02A): The §5 digest vector reproduces `authorizationDigest` exactly. - TEST-COMMON-02 (exercises REQ-COMMON-01A, REQ-COMMON-01F): - A submission carrying a foreign operation domain, or Authorized Transaction + An Submission carrying a foreign operation domain, or Authorized Transaction Data with trailing bytes, a noncanonical encoding, or an argument shape other than the transaction kind's exact format, is rejected. - TEST-COMMON-02A (exercises REQ-COMMON-01C, REQ-COMMON-01D, REQ-COMMON-04): - The Proof Verifier refuses an empty, malformed, or caller-substituted Chain - ID; a Canonical Runtime and Proof Verifier reading one Chain Profile agree - on the Chain ID, and a runtime committing any other value produces a digest - the recomputation rejects; two Chain Profiles which accept the same operation + A Canonical Runtime accepts the canonical Chain ID selected from either of two + Chain Profiles and produces distinct digests; the Proof Verifier accepts the + proof for its own Chain Profile and rejects the proof constructed for the + other, and neither the Submission nor Authorized Transaction Data can + override its Chain ID. Two Chain Profiles which accept the same operation domains are ineligible when they reuse the same canonical identifier bytes; the Consumer rejects a caller-substituted Block Time; and a Transaction Submitter that cannot satisfy @@ -1151,28 +1220,28 @@ the constructions that role implements. Resubmitting a recorded Authorization Digest is rejected. - TEST-COMMON-04 (exercises REQ-COMMON-01B, REQ-COMMON-01E): Two ceremonies over identical Authorized Transaction Data yield distinct - digests, and a digest carrying a foreign `platformVerifierVersion` is + digests, and a digest carrying a foreign `platformCeremonyVersion` is rejected. - TEST-COMMON-05 (exercises REQ-COMMON-07, REQ-COMMON-08, REQ-COMMON-10): The §6 serializer vector reproduces byte for byte. - TEST-COMMON-06 (exercises REQ-COMMON-09, REQ-COMMON-11): A request carrying an appended caller parameter is rejected, and a redirected notarized request is abandoned. -- TEST-COMMON-07 (exercises REQ-COMMON-12, REQ-COMMON-13, REQ-COMMON-15, REQ-COMMON-15A): +- TEST-COMMON-07 (exercises REQ-COMMON-12, REQ-COMMON-15, REQ-COMMON-15A): The §7 PKCE vector reproduces `code_verifier` and `code_challenge` exactly; a token attestation that hides its `code_verifier` range is rejected; and a - submission whose `pkceNonce` does not reproduce the revealed verifier from - the Authorization Digest is rejected. + Submission whose `authorizationNonce` does not reproduce the revealed + verifier from the Authorization Digest is rejected. - TEST-COMMON-08 (exercises REQ-COMMON-14): No artifact, log, or platform parameter emitted before the token exchange - completes contains `pkceNonce`. Verification: inspection of the emitted - artifacts. + completes contains the raw `authorizationNonce` of a PKCE profile. + Verification: inspection of the emitted artifacts. - TEST-COMMON-09 (exercises REQ-COMMON-16, REQ-COMMON-16A, REQ-COMMON-16B, REQ-COMMON-17, REQ-COMMON-17C, REQ-COMMON-22A): - The Canonical Runtime rejects a Submission carrying a client identifier other - than its immutable profile's client, while a proof carrying a client + The Canonical Runtime rejects a Submission carrying a client identifier + other than its immutable profile's client, while a proof carrying a client identifier registered nowhere remains acceptable to the Platform Verifier. Every platform returns the identifier as exact bytes, never a digest, and a - submission whose supplied bytes its evidence does not authenticate is + Submission whose supplied bytes its evidence does not authenticate is rejected. X and GitHub reject an empty identifier and every identifier byte outside `[A-Za-z0-9*._-]` rather than returning a form serialization. - TEST-COMMON-10 (exercises REQ-COMMON-17A, REQ-COMMON-17B, REQ-COMMON-18, REQ-COMMON-18A, REQ-COMMON-19, REQ-COMMON-19A, REQ-COMMON-19B, REQ-COMMON-19C, REQ-COMMON-19E, REQ-COMMON-20, REQ-COMMON-22): @@ -1188,11 +1257,12 @@ the constructions that role implements. ineligible; a profile whose Canonical Runtime repeats an extraction the Platform Verifier owns, and one extracting a field in one role and comparing it in another, both stay eligible. Replacing any field of the exact - Submission after deriving the local preview discards that preview and - requires derivation from the replacement Submission. A profile whose - Attestation Count is nonzero and which pins no - Notary Service or no attestation format is ineligible, while a profile - whose Attestation Count is zero stays eligible pinning neither. + Submission after deriving the local identity fields discards those fields + and requires derivation from the replacement Submission. A profile whose + Attestation Count is nonzero and which defines no attestation protocol, + format, or required security properties is invalid. A destination chain + cannot support it without selecting a compatible Notary Service. A profile + whose Attestation Count is zero remains valid without either. - TEST-COMMON-11 (exercises REQ-COMMON-21, REQ-COMMON-21A, REQ-COMMON-21B, REQ-COMMON-21C): The Platform Verifier rejects an authenticated foreign authority, method, or path. The request constructor refuses a media type or `redirect_uri` @@ -1203,26 +1273,33 @@ the constructions that role implements. an evidence time taken from an HTTP `Date` header or a local clock rather than the platform-profile value is rejected. - TEST-COMMON-13 (exercises REQ-COMMON-25A, REQ-COMMON-26, REQ-COMMON-27, REQ-COMMON-28): - A submission at or after `proofValidUntil` is rejected, a caller-supplied + An Submission at or after `proofValidUntil` is rejected, a caller-supplied validity bound has no effect, and reverse-order older or equal-conflicting metadata does not change the newer stored metadata or watermark while the otherwise valid authority operation succeeds. - TEST-COMMON-14 (exercises REQ-COMMON-30, REQ-COMMON-31): - A redirect request carrying a forwarding target forwards to the - compiled application origin instead. + Each of two configured application origins can complete its own authenticated + live channel; an unlisted origin is rejected, and a redirect request carrying + a forwarding target cannot change either result. - TEST-COMMON-15 (exercises REQ-COMMON-29): Every redirect URI registered against each production client resolves to an origin the deployment controls. Verification: audit of the platform client configuration. - TEST-COMMON-16 (exercises REQ-COMMON-05, REQ-COMMON-05A, REQ-COMMON-05B, REQ-COMMON-05C, REQ-COMMON-05D, REQ-COMMON-05E, REQ-COMMON-06, REQ-COMMON-06A, REQ-COMMON-06B, REQ-COMMON-06C, REQ-COMMON-06D, REQ-COMMON-06E): - A platform and version pair outside the Supported Version Set is rejected; - a caller-supplied verifier address has no effect; two supported versions of - one platform both verify; a submission naming an operation domain other + A platform and Verifier Version pair outside the Supported Version Set is + rejected; a caller-supplied verifier address has no effect; two supported + Verifier Versions of one platform both verify, and two implementing the same + Platform Ceremony Version accept the same Submission Payload and produce the + same digest, which the Consumer spends once; a payload naming a Platform + Ceremony Version other than the one the selected verifier implements is + rejected before any fee moves; a payload naming an operation domain other than the one the ceremony committed fails digest recomputation; a Consumer receiving a domain it does not own rejects the result; the recomputation - takes its Chain ID from the Proof Verifier's observed environment, so the - same submission presented on another chain fails it and no caller-supplied - Chain ID reaches it; a rejected verification returns no transaction data; + takes its Chain ID from the Platform Verifier's observed environment, so the + same Submission presented on another chain fails it and no caller-supplied + Chain ID reaches it; the Proof Verifier forwards the payload byte for byte + and returns the result unchanged; a rejected verification returns no + transaction data; an accepted verification returns the client identifier; the quotation covers the whole path and quotes one Notary Fee for each attestation the selected profile requires; a profile whose Attestation Count is zero quotes zero, reaches no Notary Service, and @@ -1232,7 +1309,7 @@ the constructions that role implements. An attestation carrying a foreign notary signature is rejected; a verification whose fee was not delivered is rejected; the charged fee is identical across differing attested content, authors, payers, and - submitters; the current fee is readable before submission; and a + submitters; the current fee is readable before the Submission is submitted; and a verification whose native value differs from the current fee is rejected. - TEST-COMMON-18 (exercises REQ-COMMON-35, REQ-COMMON-36, REQ-COMMON-39, REQ-COMMON-40, REQ-COMMON-43): @@ -1260,28 +1337,29 @@ the constructions that role implements. independent-blinder rule. - TEST-COMMON-20 (exercises REQ-COMMON-02A, REQ-COMMON-02B, REQ-COMMON-02C): A Google proof whose Authorization Digest public input differs from the - recomputed digest is rejected; an X or GitHub submission binds the same + recomputed digest is rejected; an X or GitHub Submission binds the same digest through the revealed verifier of REQ-COMMON-15A while its proof carries no Authorization Digest public input, and a proof adding one is rejected; and a profile binding the digest by neither method is ineligible. - TEST-COMMON-21 (exercises REQ-COMMON-41, REQ-COMMON-42): - A profile publishing no attestation list is ineligible; a submission on a + A profile publishing no attestation list is ineligible; a Submission on a zero-count profile is quoted nothing, charged nothing, and reaches no - Notary Service; a submission on a two-count profile is quoted and charged - exactly two fees; and a submission whose second attestation verification + Notary Service; a Submission on a two-count profile is quoted and charged + exactly two fees; and a Submission whose second attestation verification rejects leaves no fee delivered for the first. - TEST-COMMON-22 (exercises REQ-COMMON-45): - A submission whose proof does not verify under the artifact selected for - its identity platform and Platform Verifier Version is rejected; a proof - verifying only under another platform's or another version's artifact is - rejected; and a caller-supplied artifact, verifying key, or precomputed + A Submission whose proof does not verify under the artifact selected for + the Platform Verifier registered under its identity platform and Verifier + Version is rejected; a proof verifying only under another platform's or + another ceremony version's artifact is rejected; and a caller-supplied artifact, verifying key, or precomputed verification result changes no decision. -- TEST-COMMON-23 (exercises REQ-COMMON-46): - The Platform Verifier receives the digest the Proof Verifier recomputed - together with the complete submission, and the comparisons of - REQ-COMMON-02A and REQ-COMMON-15A run against that forwarded digest; a - Platform Verifier taking the digest from any other source rejects the - submission. +- TEST-COMMON-23 (exercises REQ-COMMON-02, REQ-COMMON-46): + The Platform Verifier recomputes the digest from the operation domain, + nonce and transaction data it decoded, the ceremony version it implements, + and its observed Chain ID, and the comparisons of REQ-COMMON-02A and + REQ-COMMON-15A run against that value; changing any decoded digest input + in the payload fails the binding check; a digest supplied in or beside the + payload changes no decision; the Proof Verifier decodes nothing. ## 12. Security Considerations @@ -1291,9 +1369,10 @@ SP-FRESH-01, and SP-REPLAY-01 under the assumptions of §3. Replay within one Consumer deployment is prevented by `authorizationNonce` and REQ-COMMON-03. Replay across Consumer Chains whose Chain Profiles use distinct canonical identifier bytes is prevented by the Chain ID in the -digest; a profile collision forfeits that separation. Replay across Platform -Verifier Versions is prevented by -`platformVerifierVersion`. The digest does not prevent cross-deployment replay +digest; a profile collision forfeits that separation. Replay across Platform Ceremony Versions is prevented by +`platformCeremonyVersion`. The digest does not bind the Verifier Version, so +a proof is acceptable at every Verifier Version implementing its ceremony +version; REQ-COMMON-03 prevents replay among them. The digest does not prevent cross-deployment replay because it binds no Consumer identifier. Every Consumer transaction kind therefore defines an authorization predicate over the authenticated Transaction Author and the proof-bound Authorized Transaction Data. A copied @@ -1301,7 +1380,7 @@ proof creates no authority for a submitter that cannot satisfy that predicate. Client binding rejects evidence issued to a client other than the one whose ceremony the Canonical Runtime opened. The check is local because independent -application deployments own different OAuth clients. The Consumer +Application deployments own different OAuth clients. The Consumer authenticates the proof-bound transaction and Transaction Author instead; it does not maintain an OAuth-client allowlist or admit applications on the Consumer Chain. @@ -1316,7 +1395,7 @@ contain that case. The ceremony layer defines no extra confirmation page. The registered redirect URI list and the origins on it are therefore trust-bearing configuration. -The verification path of §5.1 concentrates authority. A Consumer accepts the +The verification path of §5.1 concentrates authority. An Consumer accepts the Proof Verifier's decision, operation domain, and Authorized Transaction Data without rechecking them, so a compromised Proof Verifier authorizes arbitrary transactions at every Consumer at once; a compromised Platform Verifier does @@ -1332,10 +1411,11 @@ published deliberately. A binding exists to be read, and each of these values is already discoverable from the identity platform, so the protocol treats none of them as confidential. Only the bearer, the client secret, and the transcript bytes outside a profile's revealed ranges stay withheld for good. -`pkceNonce` is withheld only until the token exchange completes, per -REQ-COMMON-14; the submission publishes it afterwards, which is what lets the -Platform Verifier recompute the revealed `code_verifier` for itself under -REQ-COMMON-15A. +For a PKCE profile, the raw `authorizationNonce` is withheld until the token +exchange completes, per REQ-COMMON-14. The Submission publishes it afterwards +as the same nonce already required to recompute the Authorization Digest, +which also lets the Platform Verifier recompute the revealed `code_verifier` +under REQ-COMMON-15A. Input validation, denial of service, trust-anchor lifecycle, and browser origin, storage, and credential boundaries are owned by the browser diff --git a/specs/libid.md b/specs/libid.md index 01d66700..3fb01673 100644 --- a/specs/libid.md +++ b/specs/libid.md @@ -20,21 +20,21 @@ applies to one proof-bound transaction: ```text User -> Identity Platform -> Canonical Runtime -> Proving Circuit -> Consumer - | | - +-> Token-Exchange Service v - (GitHub only) Proof Verifier | - Platform Verifier + v + Proof Verifier | - Notary Service - (X and GitHub, - once per - attestation) + Platform Verifier + | + Notary Service + (X and GitHub, + once per + attestation) ``` -The Consumer never verifies evidence itself. It calls the Proof -Verifier, which selects the Platform Verifier registered for the named -identity platform and Platform Verifier Version, which in turn obtains +The Consumer never verifies evidence itself. It calls the Proof Verifier, +which selects the Platform Verifier registered for the named identity platform +and Platform Ceremony Version, which in turn obtains attestation authenticity from the Notary Service once for each attestation that profile carries. Google carries none, so its path reaches no Notary Service and pays no fee; X and GitHub carry two each. The result travels @@ -44,13 +44,12 @@ what that transaction means. [Common §5.1](ceremony-common.md#51-verification-p owns this path. The application operator controls its frontend, redirect deployment, OAuth -clients, and any Token-Exchange Service, but is not trusted to choose identity -fields, change the proof-bound operation, or widen proof validity. The identity -platform controls the authenticated account response. The notary authenticates -X/GitHub transcripts and their creation times. Verifier governance selects -accepted verifier artifacts, trust roots, and protocol parameters. The -Consumer Chain authenticates the Transaction Author and supplies its Chain ID -and Block Time. +clients, and GitHub Token Service, but is not trusted to choose identity fields, +change the proof-bound operation, or widen proof validity. The identity platform +controls the authenticated account response. The notary authenticates X/GitHub +transcripts and their creation times. Verifier governance selects accepted +verifier artifacts, trust roots, and protocol parameters. The Consumer Chain +authenticates the Transaction Author and supplies its Chain ID and Block Time. | Principal | Knows and can | Trusted for | Not trusted for | |---|---|---|---| @@ -61,35 +60,37 @@ and Block Time. | Verifier governance administrator | activates verifier artifacts, trust roots, parameters, and the Supported Version Set | correct authority lifecycle | user consent | The principal trust roots are Google's active signing moduli, the active -X/GitHub notary keys, the selected proof-verifier artifacts, the Proof -Verifier that dispatches to them, the Platform Verifiers it selects, Verifier -governance, and Consumer Chain consensus. The Proof Verifier is the most concentrated of these: every -Consumer takes its accept-or-reject decision, operation domain, and -Authorized Transaction Data from that one component, so its compromise +X/GitHub notary keys, the selected proof-verifier artifacts, the Proof Verifier +that dispatches to them, the Platform Verifiers it selects, Verifier governance, +and Consumer Chain consensus. The Proof Verifier is the most concentrated of +these: every Consumer takes its accept-or-reject decision, operation domain, +and Authorized Transaction Data from that one component, so its compromise authorizes arbitrary transactions at every Consumer at once. A compromised -Platform Verifier does the same for one platform and version, because it is -the role that verifies the proof and binds the digest. Replacing or retiring a root stops future -acceptance after the change takes effect; it does not undo bindings or sessions -already committed. Loss of an application deployment is a liveness failure. -Compromise of the browser release or its supply chain defeats local client and -operation construction. Compromise of a platform signing root, notary key, or -selected proof verifier can mint future evidence for the affected profiles. -Compromise of Verifier governance can change every accepted root and verifier. +Platform Verifier does the same for one platform and version, because it is the +role that verifies the proof and binds the digest. Replacing or retiring a root +stops future acceptance after the change takes effect; it does not undo bindings +or sessions already committed. Loss of an application deployment is a liveness +failure. Compromise of the Canonical Runtime build or its supply chain defeats +local client and operation construction. Compromise of a platform signing root, +notary key, or selected Platform Verifier can mint future evidence for the +affected profiles. Compromise of Verifier governance can change every accepted +root and verifier. | Subject | Single normative owner | |---|---| | Authorization Digest, PKCE, extraction, client binding, evidence time | [Common ceremony rules](ceremony-common.md) | | Chain ID, Transaction Author, Block Time, and transaction-data encoding | consumer protocol Chain Profile | | Platform endpoints, fields, trust roots, and proof projections | [Identity-platform ceremonies](platform-ceremonies.md) | -| Redirect transport, persistence, resume, and UI control flow | browser protocol | -| Transaction dispatch and author authentication | consumer protocol | +| Redirect transport, interruption behavior, and UI control flow | browser architecture | +| Transaction dispatch and author authentication | Consumer protocol | | Verification dispatch, replay recording, trust roots, and version governance | [Common ceremony rules](ceremony-common.md) | The linked ceremony chapters specify the ceremony layer. The browser and consumer protocol specifications do not redefine its proof fields or security assumptions. A profile is implementable only when its exact proving artifacts -and Notary Services are published and selected by the Verifier Governance -Process. +are published. It is usable on a destination chain only while that chain's +Verifier Governance Process selects a conforming verifier artifact and, where +required, a compatible Notary Service. ## Enforceable guarantees and accepted boundaries @@ -104,16 +105,16 @@ evidence, which is Google's signature relation and, on X and GitHub, that one hidden bearer opens both sessions' commitments. The Consumer enforces replay rejection by recording every Authorization Digest it accepts before applying an effect (REQ-COMMON-03, REQ-COMMON-03A). The Canonical Runtime -locally enforces the selected OAuth client and redirect profile. The protocol assumes the named identity-platform parser, +locally enforces the selected OAuth client and redirect profile. The protocol +assumes the named identity-platform parser, PKCE, delivery, notary, browser, verifier-soundness, and chain behaviors. It does not enforce human understanding of a platform consent screen, prevent cross-deployment presentation of the same proof, or make mutable display metadata authoritative. -Collusion sanity check — non-exhaustive: application plus Token-Exchange Service -control can withhold but cannot retarget valid evidence; application plus a -malicious identity-platform operator defeats identity authenticity for that -platform but not Authorization Digest binding; any pair containing compromised +Collusion sanity check — non-exhaustive: application plus a malicious +identity-platform operator defeats identity authenticity for that platform but +not Authorization Digest binding; any pair containing compromised Verifier governance, a selected verifier, or the applicable platform/notary trust root inherits that single-root compromise. This does not model adaptive or three-party compromise, shared key custody, browser supply-chain compromise, or @@ -141,7 +142,7 @@ outstanding X/GitHub proof. Current trust-root membership remains required. |---|---:|---| | `proofLifetime[x]` | 3600 | maximum age of the X token attestation | | `proofLifetime[github]` | 3600 | maximum age of the GitHub token-exchange attestation | -| `maxFutureAttestationSkew` | 300 | maximum X/GitHub attestation lead over chain time | +| `maxFutureAttestationSkew` | 300 | maximum X/GitHub attestation lead over Block Time | - REQ-PARAM-01: The Verifier Governance Process MUST reject an unknown parameter key and a diff --git a/specs/platform-ceremonies.md b/specs/platform-ceremonies.md index 3d63325e..6d8f41ac 100644 --- a/specs/platform-ceremonies.md +++ b/specs/platform-ceremonies.md @@ -5,13 +5,13 @@ Part of the [libID protocol specification](libid.md). ## 1. Scope This document is the normative owner of each platform's OAuth profile, -authenticated identity fields, evidence composition, proof-validity ceiling, +authenticated identity fields, evidence composition, proof-validity rule, exchange service, and platform-specific failure behavior. The [common ceremony rules](ceremony-common.md) own the Authorization Digest, serialization, PKCE, transcript extraction, client binding, and evidence -time. The consumer protocol owns transaction dispatch and authorization. -The browser protocol owns browsing contexts, redirect transport, storage, -resume, and runtime handoff. +time. The Consumer's protocol owns transaction dispatch and authorization. +The browser architecture owns browsing contexts, redirect transport, +interruption behavior, and application handoff. Google returns a signed OIDC ID Token directly to the redirect fragment. X and GitHub use the OAuth authorization-code flow and notarized transcripts of @@ -22,47 +22,64 @@ Terms are imported from ## 2. Ceremony profiles -Each platform ceremony has an independently versioned immutable profile: -`google/v1`, `x/v1`, `github/v1`. Every profile fixes the exact Platform -Verifier Version carried in its Authorization Digest and submission; launch -profiles use `platformVerifierVersion = 1`. +Each platform ceremony has an independently versioned immutable profile. Its +Platform Ceremony Version is carried in its Authorization Digest and in the +Submission Payload. Each platform section defines its own launch version. A +version covers the digest, OAuth construction, and platform-specific proof +statement, not any Consumer Chain's verifier implementation. A Consumer Chain +routes on its own Verifier Version (common §5.1). + +A profile is selected by the pair `(identityPlatform, +platformCeremonyVersion)`. Each platform section defines its exact canonical +lowercase ASCII `identityPlatform`; the version is a separate integer and is +never appended to that string. Each profile also fixes the attestation list of common REQ-COMMON-41 and the -digest-binding method of common REQ-COMMON-02C. `google/v1` verifies no -attestation and binds the digest as a public proof input; `x/v1` and -`github/v1` each verify two attestations — a token or token-exchange session -and an identity session — and bind the digest through the revealed -`code_verifier` of common REQ-COMMON-15A. +digest-binding method of common REQ-COMMON-02C. Google verifies no attestation +and binds the digest as a public proof input; X and GitHub each verify two +attestations — a token or token-exchange session and an identity session — and +bind the digest through the revealed `code_verifier` of common +REQ-COMMON-15A. - REQ-PLAT-01: - The Canonical Runtime MUST record in the ceremony state the exact profile it - selected. The Canonical Runtime MUST NOT substitute another profile on - resume. Necessity: a resumed ceremony that changed profile would produce + The Canonical Runtime MUST select and retain one exact profile for the live + ceremony. The Canonical Runtime MUST NOT substitute another profile after + authorization starts. Necessity: changing profiles mid-ceremony would produce evidence the selected verifier cannot check. - REQ-PLAT-01A (upholds SP-BIND-01): - The Verifier Governance Process MUST select an exact proof-verifier - artifact for every eligible profile and an exact Notary Service for every - TLSNotary profile. Necessity: a profile name without its - verifier artifacts does not identify one proof statement. + Each Platform Ceremony Version MUST identify one exact proof statement and + set of semantic public inputs independently of any Platform Verifier artifact + or Notary Service deployment. Before accepting that platform and version + pair, the Verifier Governance Process MUST select a conforming Platform + Verifier artifact and, for a TLSNotary profile, a compatible Notary Service. + Necessity: chain artifacts implement the versioned ceremony boundary; they + do not define it. +- REQ-PLAT-01B: + The Canonical Runtime MUST identify a profile with the exact + `(identityPlatform, platformCeremonyVersion)` pair its platform section + defines. It MUST NOT append the version to `identityPlatform` or accept a + presentation alias in its place. Necessity: every component that selects a + profile must do so on one canonical pair. A Consumer Chain's Verifier + Version selects an implementation of a profile, not the profile. - REQ-PLAT-02: The Canonical Runtime MUST treat a profile as ineligible until the application's authenticated profile lists it and the generated deployment contains every fixed route it requires. Necessity: cross-component - interoperability between runtime and deployment. + interoperability between the Canonical Runtime build and server deployment. - REQ-PLAT-03 (upholds SP-CLIENT-01): - The Canonical Runtime MUST construct the local claim preview exclusively from - the Platform Profile's canonical source in the exact Submission it returns. - The preview is not an authority decision; only the Consumer's acceptance of - that exact Submission is. For X and GitHub, the Canonical Runtime MUST parse - the exact revealed identity-response bytes that the Platform Verifier - extracts, using the same canonical extraction and normalization rules. The - Canonical Runtime MUST reject a detached proof output, sidecar value, or - caller value that supplies or overrides `userId`, handle, or - `metadataObservedAt`. - -This is a data-source invariant, not a browser-flow requirement. It defines the -preview returned to callers and used by any composition-owned UI; it does not -create a ceremony-owned confirmation page. + The Canonical Runtime MUST derive the local identity fields exclusively from + the Platform Profile's canonical source in the exact Submission it + returns. + Those fields are not an authority decision; only the Consumer's + acceptance of that exact Submission is. For X and GitHub, the Canonical Runtime MUST parse the exact revealed identity-response bytes that the + Platform Verifier extracts, using the same canonical extraction and + normalization rules. The Canonical Runtime MUST reject a detached proof + output, sidecar value, or caller value that supplies or overrides `userId`, + handle, or `metadataObservedAt`. + +This is a data-source invariant, not a browser-flow requirement. It defines +the identity fields returned to callers and used by any composition-owned UI; +it does not create a ceremony-owned confirmation page. ### 2.1 Canonical platform user identifiers @@ -139,7 +156,7 @@ such table is ineligible. - TEST-PLAT-20 (exercises REQ-PLAT-08A, REQ-PLAT-08B, REQ-PLAT-08C): Every implementation reproduces the shared handle vector table byte for byte; a caller-supplied normalized handle or pre-hashed key is rejected; - and identity bytes transformed anywhere before Consumer-side derivation + and identity bytes transformed before derivation by the Consumer fail conformance. ### 2.2 Metadata ordering and validity ceilings @@ -178,9 +195,14 @@ block an otherwise valid authority operation. ## 3. Google OIDC ceremony -Google uses direct authentication-only OIDC. The ceremony has no token -exchange, client secret, PKCE, token-exchange service, or server-side state. -Identity evidence is the signed ID Token delivered in the redirect fragment. +```text +identityPlatform = "google" +platformCeremonyVersion = 1 +``` + +Google uses direct authentication-only OIDC and has no server-side token +exchange. Identity evidence is the signed ID Token delivered in the redirect +fragment. ### 3.1 Authorization request @@ -209,14 +231,15 @@ operational guidance for obtaining a token whose signed claims satisfy The Canonical Runtime MUST request only `response_type=id_token` with `response_mode=fragment`. The Canonical Runtime MUST NOT request an authorization code or access token. Necessity: the signed identity evidence - reaches the local redirect runtime without introducing a confidential + reaches the local Redirect Runtime without introducing a confidential backend or bearer capability. - REQ-PLAT-12 (upholds SP-DELIVERY-01): - The Redirect Runtime MUST copy the bounded fragment into memory, clear the - fragment before storage or network access, and require exactly one `state` - plus exactly one `id_token` XOR `error`. The Redirect Runtime MUST reject - duplicate, additional authoritative, mixed, or malformed fields. The Redirect - Runtime MUST scrub ignored diagnostic fields. + The Redirect Runtime MUST copy the bounded query and fragment into memory and + clear both before storage or network access. The Canonical Runtime MUST require + an empty query and a fragment carrying exactly one `state` plus exactly one + `id_token` XOR `error`. The Canonical Runtime MUST reject duplicate, additional + authoritative, mixed-transport, or malformed fields and MUST ignore + diagnostic fields. - REQ-PLAT-13 (upholds SP-DELIVERY-01): The Canonical Runtime MUST match `state` to exactly one live local ceremony and consume it once before accepting the ID Token. No server-side state or @@ -236,7 +259,7 @@ Google nonce = sxj7VZ4WoXm4U-0oU1ds2hYDLZOwg5u4GlUTXTNMCvU The Canonical Runtime MUST reject an ID Token whose `nonce` differs from the Authorization Digest it constructed. - REQ-PLAT-15: - The Redirect Runtime MUST reject a Google response carrying `code` or + The Canonical Runtime MUST reject a Google response carrying `code` or `access_token`. Necessity: neither artifact belongs to this authentication-only profile. @@ -259,7 +282,8 @@ require a verifier that dispatches on the header `alg`; none exists here. - REQ-PLAT-16A (upholds SP-CLIENT-01): The Proving Circuit MUST expose the exact RSA modulus used for REQ-PLAT-16 - as a public proof input, in the limb encoding its verifier artifact fixes. + as a public proof input. Its internal field or limb representation belongs to + the proving and verifier artifacts, not the Platform Profile. The Proving Circuit MUST NOT decide trusted-set membership or take the active set as an input. The Platform Verifier alone checks the modulus under REQ-PLAT-23. JWK decoding and canonical-encoding validation happen where a @@ -291,7 +315,7 @@ require a verifier that dispatches on the header `alg`; none exists here. client-binding public input. - REQ-PLAT-19A (upholds SP-CLIENT-01): The Platform Verifier MUST require `SHA256` of the `aud` bytes carried in - the submission to equal that public input. The Platform Verifier MUST return those bytes + the Submission to equal that public input. The Platform Verifier MUST return those bytes as the client identifier of common REQ-COMMON-16. Necessity: the digest authenticates the bytes without the circuit packing a variable-length string into public inputs, and the Consumer still receives the readable value. Admission stays @@ -350,9 +374,10 @@ Launch fixes X's `/2/oauth2/token` and `/2/users/me` sessions and GitHub's probe, failure, or retry select Browser MPC or switch transport within a launch ceremony. - REQ-PLAT-28 (upholds SP-DELIVERY-01): - The Redirect Runtime MUST require the X or GitHub authorization redirect to - carry exactly one `code` and exactly one `state`, or exactly one `error`. - The Redirect Runtime MUST reject duplicate, mixed, additional + The Canonical Runtime MUST require the X or GitHub authorization redirect to + carry an empty fragment and a query containing exactly one `state` plus + exactly one `code` XOR `error`. The Canonical Runtime MUST reject duplicate, + mixed-transport, additional authoritative, and malformed fields. The single accepted `code` is the code consumed at redirect ingress that REQ-PLAT-29 and REQ-PLAT-46 compare against. @@ -367,11 +392,17 @@ Browser MPC is a deferred protocol alternative. It may remove the notary-to-platform path assumption and notary egress exposure, but it requires qualification of browser bandwidth, latency, memory, battery, WebSocket-to-TCP bridging, and mobile suspension. Adopting it requires a new -ceremony profile whenever it changes the verifier, artifacts, or security -assumptions; it is not deployment configuration under `x/v1` or `github/v1`. +ceremony profile whenever it changes the proof statement, attestation format, +ceremony behavior, or security assumptions; it is not deployment configuration +under the X or GitHub profile. ## 5. X ceremony +```text +identityPlatform = "x" +platformCeremonyVersion = 1 +``` + X uses a public client with S256 PKCE and two browser-owned TLSNotary sessions. @@ -427,7 +458,7 @@ attestation format: | `client_id` | yes | the Platform Verifier reads and returns it | | `code` | yes | compared to the code consumed at redirect ingress | | `redirect_uri` | yes | the Canonical Runtime compares its immutable profile; no chain or circuit value | -| `code_verifier` | yes | the Platform Verifier recomputes it from the digest and `pkceNonce` per common REQ-COMMON-15A | +| `code_verifier` | yes | the Platform Verifier recomputes it from the digest and `authorizationNonce` per common REQ-COMMON-15A | | attestation timestamp | not a range | the attestation's own signed creation time, which derives the authenticated validity ceiling per §2.2 | | `"access_token":"` and the closing quote immediately around the bearer value | yes | anchor the committed bearer range as that field's value, per common REQ-COMMON-18A | | bearer range | committed | a blinded commitment, opened only in circuit | @@ -447,7 +478,7 @@ range indistinguishable from a `refresh_token` value. Those reveals and the in-circuit `code_verifier` opening of REQ-COMMON-15 reduce the hidden request surface, but revealing a range does not reject a form -delimiter inside it. `x/v1` therefore retains ASM-PROV-07 as a soundness +delimiter inside it. The X profile therefore retains ASM-PROV-07 as a soundness dependency. - REQ-PLAT-29A (upholds SP-CLIENT-01): @@ -530,8 +561,8 @@ byte stays behind a range commitment of the pinned attestation format: | `"username":"`, the `data.username` value, and its closing quote | yes | the Platform Verifier extracts the raw handle bytes from these bytes per REQ-PLAT-31 | | everything else | no | status line, headers, display name, and every other response field | -Each revealed range carries its own full delimiter, so the value the Platform -Verifier reads is that field's value rather than a substring of a neighboring +Each revealed range carries its own full delimiter, so the value the Platform Verifier +reads is that field's value rather than a substring of a neighboring one. Every committed range of this direction is bounded by a revealed delimiter on each side that faces one, and by the signed transcript boundary of common REQ-COMMON-36 at the two ends, which is the anchoring common @@ -601,19 +632,32 @@ copy of any of them, because a fact that can be checked in the open does not belong in a proof. - REQ-PLAT-33 (upholds SP-FRESH-01): - The Canonical Runtime MUST complete the token request within X's - authorization-code deadline of 30 seconds. The Canonical Runtime MUST abandon - the ceremony otherwise. + The Canonical Runtime MUST complete the request direction of X's first + notarized token session before X's 30-second authorization-code deadline. + The deadline ends when X has received the complete token request; receiving + or notarizing the response, running the identity session, proving, and proof + delivery are outside it. The Canonical Runtime MUST abandon the ceremony if it + cannot complete that request direction in time. ## 6. GitHub ceremony +```text +identityPlatform = "github" +platformCeremonyVersion = 1 +``` + +GitHub Token Service: The deployment-owned confidential-client component that +performs the GitHub token exchange inside a notarized TLS session and returns +the bearer, its opening, and the resulting attestation through the semantic +interface of §6.3. + GitHub uses a confidential client, a deployment-owned token-exchange TLSNotary session, and a browser-owned `/user` TLSNotary session. The structure matches X exactly: two attestations, both verified by the Notary Service, one hidden bearer linking them, and one proof binding that link to the Authorization Digest. The exchange runs server-side because the client is -confidential, which makes the Token-Exchange Service the notarized party for +confidential, which makes the GitHub Token Service the notarized party for that session. It produces an attestation, not a proof. ### 6.1 Authorization request @@ -680,45 +724,40 @@ revealed `client_id` something other than the credential GitHub authenticated. the Consumer Chain acts on them; the Canonical Runtime MAY check them locally. -### 6.3 Token-exchange service +### 6.3 GitHub token service boundary -The Deployment exposes one stateless Token-Exchange Service at the fixed -`/oauth/github/token-exchange` route on the redirect origin. +The Deployment exposes one GitHub Token Service to its isolated prover. This +specification defines the semantic call, not its endpoint or wire encoding: -```ts -interface TokenExchangeRequestV1 { - schema: 1 - code: string - codeVerifier: string -} - -interface TokenExchangeResponseV1 { - schema: 1 - accessToken: string - tokenAttestation: string // canonical unpadded base64url - bearerOpening: string // canonical unpadded base64url; private witness +```text +githubTokenExchange( + authorizationCode, + codeVerifier, +) -> { + bearer, + bearerOpening, + tokenAttestation, } ``` +These identifiers name protocol values, not serialized field names. The +browser and deployment specifications own endpoint naming, transport framing, +serialization, parsing bounds, caller authentication, and cache policy. Those +choices MUST preserve the semantic interface and security requirements below. + - REQ-PLAT-37: - The Implementation MUST reject a `code` that is empty, carries whitespace or - control characters, is not printable ASCII, or exceeds `MAX_GITHUB_CODE_BYTES - = 1024`. Necessity: bounded parsing. + The Canonical Runtime MUST invoke the GitHub Token Service with the exact + authorization code consumed from the redirect and the exact PKCE verifier + derived for that ceremony. The service MUST use those values as the `code` + and `code_verifier` of the token request in §6.2 and MUST NOT substitute + another value. - REQ-PLAT-38: - The Implementation MUST require `codeVerifier` to match `[A-Za-z0-9_-]{43}`. - Necessity: cross-component interoperability with the PKCE construction. -- REQ-PLAT-39: - The Implementation MUST reject an `accessToken` exceeding - `MAX_GITHUB_ACCESS_TOKEN_BYTES = 4096`, a decoded `bearerOpening` exceeding - `MAX_GITHUB_BEARER_OPENING_BYTES = 256`, a decoded token attestation exceeding - `MAX_GITHUB_TOKEN_ATTESTATION_BYTES = 2 MiB`, and a response body exceeding - `MAX_GITHUB_TOKEN_EXCHANGE_RESPONSE_BYTES = 3 MiB`. Necessity: bounded parsing. -- REQ-PLAT-40: - The Implementation MUST reject duplicate, missing, additional, differently - typed, and malformed fields on both interfaces. Necessity: cross-component - interoperability. + On success, the GitHub Token Service MUST return the exact bearer committed + by the token-exchange attestation, the opening for that bearer commitment, + and that attestation. All three values MUST come from the same notarized + session. On failure, it MUST return no partial result. - REQ-PLAT-54: - The Token-Exchange Service MUST return in `bearerOpening` the blinder that + The GitHub Token Service MUST return in `bearerOpening` the blinder that opens the committed bearer range of the attestation it returns in the same response. Necessity: the Proving Circuit opens that commitment under REQ-PLAT-52, and the blinder is prover-private material generated inside @@ -728,41 +767,36 @@ interface TokenExchangeResponseV1 { - REQ-PLAT-55 (upholds SP-CLIENT-01): The Canonical Runtime MUST treat `bearerOpening` as private witness material for the Proving Circuit. The Canonical Runtime MUST NOT place - `bearerOpening` in a submission. The Canonical Runtime MUST NOT publish it, + `bearerOpening` in a Submission. The Canonical Runtime MUST NOT publish it, log it, or transmit it anywhere outside the browser. Necessity: the opening and the commitment together reveal the committed bearer, so a published opening publishes the credential its commitment exists to hide. - REQ-PLAT-41 (upholds SP-EXCHANGE-01): - The Token-Exchange Service MUST use only its compiled client identifier, client - secret, redirect URI, token endpoint, and notary configuration. The - Token-Exchange Service MUST NOT accept a caller-selected action, job, client, - redirect, endpoint, return URL, or operation. + The GitHub Token Service MUST use only its compiled client identifier, client + secret, redirect URI, token endpoint, and notary configuration. The GitHub + Token Service MUST NOT accept a caller-selected action, job, client, redirect, + endpoint, return URL, or operation. - REQ-PLAT-42: - The Token-Exchange Service MUST persist no code, verifier, bearer, proof, - result, or progress state. The Token-Exchange Service MUST expose no polling or + The GitHub Token Service MUST persist no code, verifier, bearer, proof, + result, or progress state. The GitHub Token Service MUST expose no polling or result route. Necessity: the service holds ceremony credentials, so retention creates a compromise target with no protocol purpose. - REQ-PLAT-43: - The Token-Exchange Service MUST accept only the compiled redirect-runtime origin. - Necessity: limits accidental browser disclosure; it is not caller - authentication. -- REQ-PLAT-43A: - The Token-Exchange Service MUST answer the CORS preflight for that origin. - Necessity: cross-component interoperability with the Canonical Runtime. + The deployment transport MUST make the GitHub Token Service callable only by + its authenticated isolated prover boundary, not by an application frontend + or unrelated origin. The browser and deployment specifications define the + concrete enforcement mechanism. - REQ-PLAT-43B: - The Token-Exchange Service MUST reject redirects. Necessity: a followed redirect + The GitHub Token Service MUST reject redirects. Necessity: a followed redirect would notarize a session other than the pinned token endpoint. -- REQ-PLAT-43C: - The Token-Exchange Service MUST emit `Cache-Control: no-store`. Necessity: the - response carries a bearer token. ### 6.4 Disclosure and verification -The Token-Exchange Service, which holds the client secret, runs the exchange +The GitHub Token Service, which holds the client secret, runs the exchange inside a notarized TLS session and returns the resulting attestation. The `client_secret` range stays redacted behind that attestation's range commitment, so the browser never receives the secret. The attestation is -verified under the pinned `github/v1` Notary Service, exactly +verified by the compatible Notary Service selected for the GitHub profile, exactly as the `/user` attestation is. The token-exchange attestation reveals exactly the ranges needed to bind it to @@ -770,14 +804,14 @@ the local ceremony and to the later `/user` attestation. The separately returned `accessToken` and the `bearerOpening` of REQ-PLAT-54 are the only additional response values. Both stay inside the browser: the opening is witness material for the circuit, and REQ-PLAT-55 keeps it out of every -submission and every published artifact. +Submission and every published artifact. | Range | Revealed | Why | |---|---|---| -| `client_id` | yes | the Platform Verifier reads and returns it; the runtime checks its profile | +| `client_id` | yes | the Platform Verifier reads and returns it; the Canonical Runtime checks its profile | | `code` | yes | the Canonical Runtime compares it to the code it consumed | | `redirect_uri` | yes | the Canonical Runtime compares its immutable profile | -| `code_verifier` | yes | the Platform Verifier recomputes it from the digest and `pkceNonce` per common REQ-COMMON-15A | +| `code_verifier` | yes | the Platform Verifier recomputes it from the digest and `authorizationNonce` per common REQ-COMMON-15A | | `"access_token":"` and the closing quote immediately around the bearer value | yes | anchor the committed bearer range as that field's value, per common REQ-COMMON-18A | | bearer range | committed | a blinded commitment, opened only in circuit to link this attestation to `/user` | | attestation timestamp | not a range | the attestation's own signed creation time, which derives the authenticated validity ceiling per §2.2 | @@ -802,11 +836,11 @@ data itself, which is why common REQ-COMMON-25 can forbid inferring it from a response header. Revealing more would widen exposure without adding a check. - REQ-PLAT-43D (upholds SP-EXCHANGE-01): - The Token-Exchange Service MUST reveal no range outside the seven rows - marked `yes` above. The Token-Exchange Service MUST commit the bearer range + The GitHub Token Service MUST reveal no range outside the seven rows + marked `yes` above. The GitHub Token Service MUST commit the bearer range rather than reveal it. - REQ-PLAT-58 (upholds SP-EXCHANGE-01): - The Token-Exchange Service MUST reveal the `"access_token":"` delimiter + The GitHub Token Service MUST reveal the `"access_token":"` delimiter bytes immediately preceding that committed range and the closing quote byte immediately following it. The Platform Verifier MUST reject a token-exchange attestation whose committed range is not framed by exactly @@ -822,15 +856,15 @@ response header. Revealing more would widen exposure without adding a check. - REQ-PLAT-44 (upholds SP-EXCHANGE-01): The Canonical Runtime MUST verify the returned token-exchange attestation - locally against the `github/v1` profile's pinned notary key and attestation + locally against the GitHub profile's pinned notary key and attestation format before using the bearer. Necessity: the browser checks what it got back before spending a `/user` session on it; the Notary Service decision - the chain relies on is separate. + the Consumer Chain relies on is separate. - REQ-PLAT-45 (upholds SP-EXCHANGE-01): - The Token-Exchange Service MUST return an attestation carrying the + The GitHub Token Service MUST return an attestation carrying the configured notary's signature and revealing the token request's method and path. The Platform Verifier MUST compare those two revealed values with the - `github/v1` profile. The Platform Verifier MUST compare the authority that + GitHub profile. The Platform Verifier MUST compare the authority that attestation authenticates with the same profile, per common REQ-COMMON-21A. Necessity: the authority is never a revealed range, because the transcript carries it only in a prover-composed `Host` header. @@ -855,8 +889,8 @@ response header. Revealing more would widen exposure without adding a check. `bearerOpening`, to open the bearer commitment of the token-exchange attestation. - REQ-PLAT-50 (upholds SP-EXCHANGE-01): - The Canonical Runtime MUST discard the response and start neither `/user` nor - a resume record when any check in REQ-PLAT-44 through REQ-PLAT-49 fails. + The Canonical Runtime MUST discard the response and start no `/user` request + when any check in REQ-PLAT-44 through REQ-PLAT-49 fails. Verifying only arbitrary byte substrings is insufficient: a prover that composes the request could otherwise witness one `code` or `code_verifier` @@ -864,7 +898,7 @@ while GitHub consumes a duplicate. The layout tiling accounts for every transcript byte, every body range other than the secret is revealed, and the opened secret is delimiter-free per REQ-PLAT-35. These checks reduce hidden surface but do not prove the decoded form grammar or reject duplicates inside -revealed values. `github/v1` therefore retains ASM-PROV-07 as a soundness +revealed values. The GitHub profile therefore retains ASM-PROV-07 as a soundness dependency. The server-produced token-exchange attestation carries the commitments while keeping the client secret from the browser. @@ -899,8 +933,8 @@ byte stays behind a range commitment of the pinned attestation format: | `"login":"`, the `login` value, and its closing quote | yes | the Platform Verifier extracts the raw handle bytes from these bytes per REQ-PLAT-51 | | everything else | no | status line, headers, and every other response field | -Each revealed range carries its own full delimiter, so the value the Platform -Verifier reads is that field's value rather than a substring of a neighboring +Each revealed range carries its own full delimiter, so the value the Platform Verifier +reads is that field's value rather than a substring of a neighboring one. Every committed range of this direction is bounded by a revealed delimiter on each side that faces one, and by the signed transcript boundary of common REQ-COMMON-36 at the two ends, which is the anchoring common @@ -919,7 +953,7 @@ REQ-COMMON-18A requires. response bytes by their full field delimiters, rejecting a transcript in which either delimiter matches at more than one position, per common REQ-COMMON-19A. The Platform Verifier MUST reject a noncanonical `id` - encoding. The `github/v1` profile fixes the structural byte following the + encoding. The GitHub profile fixes the structural byte following the `id` integer token, which common REQ-COMMON-19D leaves to the profile, as `,` or `}` and no other byte. The Platform Verifier MUST reject any other following byte. Necessity: the terminator is what proves the revealed digits @@ -935,9 +969,9 @@ REQ-COMMON-18A requires. - REQ-PLAT-52 (upholds SP-EXCHANGE-01): The Proving Circuit MUST assert that one private bearer value opens the bearer commitment of the token-exchange attestation and the - `Authorization` bearer commitment of the `/user` attestation. The Platform - Verifier MUST compare the method and path revealed in each attestation, and - the authority each attestation authenticates, with the `github/v1` profile. + `Authorization` bearer commitment of the `/user` attestation. The Platform Verifier MUST + compare the method and path revealed in each attestation, and + the authority each attestation authenticates, with the GitHub profile. - REQ-PLAT-52A (upholds SP-EXCHANGE-01): The Proving Circuit MUST expose exactly these two GitHub public inputs: @@ -959,16 +993,15 @@ REQ-COMMON-18A requires. this the circuit could prove a link between two attestations other than the ones submitted. -Changing the pinned API version is a profile and verifier revision, not -runtime configuration. The granted scope is no proof property at all: +Changing the pinned API version requires a new Platform Ceremony Version; it +is not mutable configuration. The granted scope is no proof property at all: REQ-PLAT-36 leaves the exchange response unverified beyond the opened bearer range, and the Canonical Runtime's local reading of `scope` and `token_type` binds nothing on the Consumer Chain. The bearer is never disclosed by the proof. - REQ-PLAT-53: - The Token-Exchange Service MUST NOT promise idempotency or replay. The Canonical - Runtime MUST start a fresh ceremony when GitHub consumed the code but no + The GitHub Token Service MUST NOT promise idempotency or replay. The Canonical Runtime MUST start a fresh ceremony when GitHub consumed the code but no response reached it. Necessity: the exchange is a single-use, non-recoverable step. @@ -981,13 +1014,13 @@ observation ordering; client portability or a bounded client family; exact authorization and redirect transport; every authenticated request and response field with its provenance; how the Authorization Digest is carried through that platform's authorization; its authenticated client-binding source; an -authenticated proof-validity ceiling; its trust-root lifecycle; -browser and deployment data exposure, retry, resume, and withholding +authenticated proof-validity rule and parameter keys; its trust-root lifecycle; +browser and deployment data exposure, retry, interruption, and withholding behavior; and conformance vectors. ## 8. Conformance -Roles: Canonical Runtime, Token-Exchange Service, Proving Circuit, +Roles: Canonical Runtime, GitHub Token Service, Proving Circuit, Platform Verifier, Notary Service, Consumer. - TEST-PLAT-01 (exercises REQ-PLAT-10, REQ-PLAT-18): @@ -998,8 +1031,9 @@ Platform Verifier, Notary Service, Consumer. is rejected. - TEST-PLAT-03 (exercises REQ-PLAT-11, REQ-PLAT-12): A Google authorization request not using the exact direct-ID-token fragment - profile is rejected, and a fragment carrying duplicate `state`, both - `id_token` and `error`, `code`, or `access_token` is rejected. + profile is rejected. A nonempty query, mixed query/fragment response, or + fragment carrying duplicate `state`, both `id_token` and `error`, `code`, or + `access_token` is rejected. - TEST-PLAT-04 (exercises REQ-PLAT-13, REQ-PLAT-14): A fragment whose `state` has no unique live local ceremony, and an ID Token whose `nonce` is not the constructed digest, are rejected. No backend state @@ -1016,7 +1050,7 @@ Platform Verifier, Notary Service, Consumer. under any other algorithm or key fails the fixed verification relation. Header, payload, signature, or public-output substitution is rejected. A cryptographically valid proof under an inactive signing modulus passes - circuit verification but is rejected by the Platform Verifier. A submission + circuit verification but is rejected by the Platform Verifier. An Submission whose supplied `aud` bytes do not hash to the audience public input is rejected, and an accepted one returns those exact bytes as the client identifier. @@ -1057,7 +1091,10 @@ Platform Verifier, Notary Service, Consumer. different bearers is rejected. No proof statement covers `token_type` or the granted scope. - TEST-PLAT-11 (exercises REQ-PLAT-33): - A token request issued after the 30-second deadline is abandoned. + The complete request direction of X's first notarized session reaches X + before the authorization-code deadline in the success case; delaying its + completion past the deadline abandons the ceremony, while delaying only the + response or later proof work does not trigger that deadline. - TEST-PLAT-12 (exercises REQ-PLAT-34, REQ-PLAT-35, REQ-PLAT-35A, REQ-PLAT-35B, REQ-PLAT-35C): An authorization request carrying a scope other than `read:user` is rejected; no public proof input derives from the client secret; an exchange @@ -1065,22 +1102,24 @@ Platform Verifier, Notary Service, Consumer. accepted one returns those exact bytes as the client identifier; and the configured secret contains neither `&` nor `=`. Verification: inspection of the configured credential for the secret rule. -- TEST-PLAT-13 (exercises REQ-PLAT-37, REQ-PLAT-38, REQ-PLAT-39, REQ-PLAT-40): - Each over-limit, malformed, duplicate, and missing field on both token-exchange - interfaces is rejected. -- TEST-PLAT-14 (exercises REQ-PLAT-41, REQ-PLAT-42, REQ-PLAT-43, REQ-PLAT-43A, REQ-PLAT-43B, REQ-PLAT-43C, REQ-PLAT-43D, REQ-PLAT-43E): +- TEST-PLAT-13 (exercises REQ-PLAT-37, REQ-PLAT-38): + The service receives the exact redirect authorization code and ceremony + verifier. A successful result returns the bearer, opening, and attestation + from one notarized session; substitution, a mixed-session tuple, and a + partial result on failure are rejected. +- TEST-PLAT-14 (exercises REQ-PLAT-41, REQ-PLAT-42, REQ-PLAT-43, REQ-PLAT-43B, REQ-PLAT-43D, REQ-PLAT-43E): A request selecting an endpoint, client, or return URL is rejected; no state - survives the call; a foreign origin is refused; the CORS preflight for the - compiled origin is answered; every response carries `Cache-Control: - no-store`; an attestation revealing a range outside the seven marked rows, - or revealing the bearer range instead of committing it, is rejected; and no proof exposes the bearer or a value it can be recovered - from. + survives the call; a caller outside the authenticated isolated-prover + boundary is refused; a redirected token exchange is rejected; an attestation + revealing a range outside the seven marked rows, or revealing the bearer + range instead of committing it, is rejected; and no proof exposes the bearer + or a value from which it can be recovered. - TEST-PLAT-15 (exercises REQ-PLAT-44, REQ-PLAT-45, REQ-PLAT-47, REQ-PLAT-48, REQ-PLAT-48A, REQ-PLAT-49, REQ-PLAT-50): A token-exchange attestation with a bad notary signature, a foreign endpoint, a foreign client, a foreign `code_verifier`, a foreign serialized `redirect_uri`, or a bearer that does not open the commitment under the returned `bearerOpening` is discarded in each - case, and no resume record is written. + case, and no `/user` request starts. - TEST-PLAT-15A (exercises REQ-PLAT-52, REQ-PLAT-52A, REQ-PLAT-52B): A GitHub proof whose bearer commitment public input differs from the commitment in either submitted attestation is rejected; substituting one @@ -1094,25 +1133,30 @@ Platform Verifier, Notary Service, Consumer. rejected. - TEST-PLAT-16 (exercises REQ-PLAT-53): A ceremony whose exchange response was lost restarts from authorization. -- TEST-PLAT-17 (exercises REQ-PLAT-01, REQ-PLAT-01A, REQ-PLAT-02, REQ-PLAT-03): - A resume that substitutes a newer profile is rejected, an unlisted profile is - ineligible, a profile missing its verifier artifact or, for a TLSNotary - profile, its Notary Service is ineligible, and no preview field originates - outside proof public inputs and the exact revealed attestation bytes carried - by its Submission. Only the Consumer's acceptance of that exact Submission - makes the claim authoritative. +- TEST-PLAT-17 (exercises REQ-PLAT-01, REQ-PLAT-01A, REQ-PLAT-01B, REQ-PLAT-02, REQ-PLAT-03): + The launch profile pairs are exactly `("google", 1)`, `("x", 1)`, and + `("github", 1)`; a suffixed platform string is not one of those profiles. A + live ceremony that substitutes a newer profile is rejected, an unlisted + profile is ineligible, and the profile identifies the same proof statement + and semantic public inputs across two chains using different conforming + verifier artifacts. A destination chain does not support the pair without a + conforming artifact or, for a TLSNotary profile, a compatible Notary Service. + No local identity field originates outside proof public inputs and the exact + revealed attestation bytes carried by its Submission. Only the Consumer's + acceptance of that exact Submission makes the claim authoritative. - TEST-PLAT-17A (exercises REQ-PLAT-03, REQ-PLAT-31A, REQ-PLAT-51A): Pair authenticated X or GitHub identity-response bytes for account B with a - detached `userId`, handle, or metadata value for account A. The runtime - rejects the extra representation; without it, the runtime and the Platform - Verifier both derive account B byte for byte. Replacing the proof, - attestation, platform, or version after deriving the preview discards it and - requires rederivation from the replacement Submission. + detached `userId`, handle, or metadata value for account A. The Canonical Runtime + rejects the extra representation; without it, the Canonical Runtime and the + Platform Verifier both derive account B byte for byte. Replacing the proof, + attestation, platform, or version after deriving the local identity fields + discards them and requires rederivation from the replacement Submission. - TEST-PLAT-18 (exercises REQ-PLAT-25, REQ-PLAT-26, REQ-PLAT-27, REQ-PLAT-28, REQ-PLAT-28A): Launch uses Proxy mode, rejects application or request selection of Browser MPC, uses no application-controlled platform egress, and carries no partial - transcript state into a retry. A redirect carrying two `code` fields, two - `state` fields, both `code` and `error`, or a malformed field is rejected + transcript state into a retry. A nonempty fragment, mixed query/fragment + response, or redirect carrying two `code` fields, two `state` fields, both + `code` and `error`, or a malformed field is rejected before any token request starts, as is a redirect whose `state` matches no live local ceremony or a ceremony already consumed. - TEST-PLAT-19 (exercises REQ-COMMON-32; supports ASM-PROV-07): @@ -1126,8 +1170,8 @@ Platform Verifier, Notary Service, Consumer. open the committed bearer range and build the GitHub proof; a response omitting that field, or carrying an opening that does not open the attestation's bearer commitment, is discarded and no proof is built; and no - submission, log, or published artifact contains the opening. Verification: - inspection of the submission fields and the emitted artifacts for the + Submission, log, or published artifact contains the opening. Verification: + inspection of the Submission fields and the emitted artifacts for the disclosure rule. - TEST-PLAT-22 (exercises REQ-PLAT-57, REQ-PLAT-58, REQ-PLAT-59, REQ-PLAT-60): An X token attestation and a GitHub token-exchange attestation whose @@ -1169,7 +1213,7 @@ reduce hidden request surface but do not replace that parser assumption. TEST-PLAT-19 exercises it continuously; a failed probe makes the affected profile ineligible for new ceremonies. -A malicious Token-Exchange Service cannot rebind a ceremony to other +A malicious GitHub Token Service cannot rebind a ceremony to other Authorized Transaction Data while ASM-PROV-07 holds, because the Authorization Digest fixes that data before the platform is contacted and the service cannot make GitHub redeem a `code_verifier` other than the one proven. Should @@ -1178,16 +1222,16 @@ exploit it: it legitimately holds the user's code and verifier. It can withhold, and it can attempt to substitute a token obtained under a separately arranged authorization; REQ-PLAT-46 rejects that substitution by requiring the proven code to be the one this ceremony consumed. A proof -built outside the Canonical Runtime performs no such check, so a submission -of that proof is bounded by the Transaction Author rule stated in +built outside the Canonical Runtime performs no such check, so a Submission +carrying it is bounded by the Transaction Author rule stated in [common §12](ceremony-common.md#12-security-considerations). The notary key is a trust root for X and GitHub evidence. Its compromise mints fresh evidence until the key is removed, and does not revoke authority already committed. -Google has no Token-Exchange Service or deployment-visible authorization -response. Its signed ID Token reaches the redirect fragment, is cleared before +Google has no server-side token exchange. Its signed ID Token reaches the +redirect fragment, is cleared before other work, and is bound to the local ceremony by `state`, signed `nonce`, and signed `aud`. A deployment backend can withhold the static redirect document but cannot substitute an ID Token through a server exchange that does not