diff --git a/specs/ceremony-common.md b/specs/ceremony-common.md index 96b87133..853944b1 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,102 +19,123 @@ 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 Identity Integration 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. +Verifier Dispatcher: The component on the Execution Ledger that every + Identity Integration calls to verify an OAuth Proof. Its caller names the + identity platform and Platform Ceremony Version; it selects the LOPPV registered for + that pair and returns the verified result. + It is not the party that produces proofs. + +Ledger OAuth Platform Proof Verifier (LOPPV): The component on the Execution + Ledger registered for one identity platform and one Platform Ceremony + Version. It validates that version's OAuth Proof and returns its verified + identity outputs. Different Execution Ledgers may use different LOPPV + 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. It + identifies no verifier contract, verifier implementation, or mutable + verifier-authority revision. + +Supported Version Set: The identity-platform and Platform Ceremony Version + pairs a Verifier Dispatcher currently accepts. More than one version of one + platform can be supported at the same time. -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. +Execution Ledger: The ordered ledger whose canonical state transition consumes + an OAuth Proof. -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. +Application: A user-facing product that starts Ceremonies and requests + transactions through an Identity Integration. -Supported Version Set: The identity-platform and Platform Verifier Version - pairs a Proof Verifier currently accepts. More than one version of one - platform can be supported at the same time. +Application Deployment: One concrete deployment of an Application: its + frontend origins, backend services, OAuth client registrations, redirect + endpoint, and proving assets. -Consumer Chain: The chain whose canonical state transition consumes a libID - proof. +Identity Integrator: The party that deploys or maintains an Identity + Integration. One Identity Integration can serve multiple Applications and + Application Deployments. -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. +Identity Integration: The deterministic contract, program, module, or native + transition handler on the Execution Ledger through which an Identity + Integrator applies verified identity to application-specific state + changes. It submits an OAuth Proof for verification and applies the + Authorized Transaction Data returned to it. -Transaction Author: The Consumer-Chain principal whose authenticated authority +Transaction Author: The Execution Ledger principal whose authenticated authority permits the transaction. It can be an account, multisignature contract, - program, module, or equivalent chain principal. + program, module, or equivalent ledger principal. 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. + Execution Ledger. 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 - number, a string, or a hash — and the Chain Profile fixes the exact bytes +Ledger ID: The 32-byte keccak256 of an Execution Ledger's canonical ledger + identifier. That identifier takes whatever form the ledger gives it — a + number, a string, or a hash — and the Ledger Profile fixes the exact bytes hashed. -Block Time: The Consumer Chain's consensus-provided integer Unix time in +Block Time: The Execution Ledger's consensus-provided integer Unix time in seconds, bounded by an unsigned 64-bit integer. -Chain Profile: The normative mapping from one Consumer Chain to its Chain ID, +Ledger Profile: The normative mapping from one Execution Ledger to its Ledger ID, Transaction Author authentication, Block Time, and Authorized Transaction 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. +OAuth Proof: The complete input an Identity Integration passes to the Verifier + Dispatcher for one verification: the identity platform, the Platform Ceremony Version, + the operation domain, the Authorization Nonce, the + Authorized Transaction Data, the proof, the platform's attestations, and + any further value the Platform Profile requires the caller to supply. It is + neither the Execution Ledger transaction nor the zero-knowledge proof + alone. -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 OAuth + Proof for an Identity Integration. Identity Integration 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. - -Proving Circuit: The zero-knowledge circuit whose proof a Platform Verifier + 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 LOPPV registered for that platform and + version MUST enforce the same profile, but its implementation and deployment + are ledger-specific. The Identity Integration holds none of the profile + constants. + +Proving Circuit: The zero-knowledge circuit whose proof a LOPPV 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. +Ceremony Popup: 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 + Verifier Dispatcher's Supported Version Set, each LOPPV's pinned constants and trust roots, and the protocol parameters. It is not the - Consumer's governance. + Identity Integration'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 +Ceremony Client: The browser-side implementation that constructs Authorization Digests, performs the required local evidence checks, and - builds proofs and claim previews. + builds OAuth Proofs 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,32 +146,35 @@ 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 LOPPV. 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 Execution Ledger's native asset. One OAuth Proof 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 + Platform Profile fixes under REQ-COMMON-41 — the attestations the LOPPV must + have verified before it accepts an OAuth Proof. 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. ## 3. Assumptions -- ASM-CHAIN-01: - The Consumer Chain authenticates the Transaction Author and supplies Block +- ASM-LEDGER-01: + The Execution Ledger authenticates the Transaction Author and supplies Block Time within tolerance of real time. -- ASM-CHAIN-02: - The Proof Verifier obtains exactly one canonical Chain ID: from its - execution environment where the Consumer Chain exposes one, and from +- ASM-LEDGER-02: + The Verifier Dispatcher obtains exactly one canonical Ledger ID: from its + execution environment where the Execution Ledger exposes one, and from immutable deployment configuration where it does not. - ASM-PROV-01: An Identity Platform delivers an authorization response only to a redirect @@ -187,35 +211,37 @@ 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 + The Ceremony Client 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, -browser supply chain, or Consumer Chain invalidates the properties that depend +The properties below survive a malicious Application Deployment operator under their +cited assumptions. They assume an unmodified Ceremony Client, the selected +verifier artifact, the Identity Integration, and verifier configuration. Compromise of the +applicable +identity-platform signing root, notary key, LOPPV, verifier governance, +browser supply chain, or Execution Ledger invalidates the properties that depend on it. - SP-BIND-01: Evidence produced by a ceremony discharges only for the Authorized Transaction Data committed in its Authorization Digest. Depends on ASM-PROV-02, ASM-PROV-05, ASM-PROV-06, - ASM-PROV-07, ASM-NOTARY-01, ASM-PROOF-01, ASM-CHAIN-01. Evidence: + ASM-PROV-07, ASM-NOTARY-01, ASM-PROOF-01, ASM-LEDGER-01. Evidence: conformance tests (supporting, not proving) plus the collision resistance of SHA-256 and keccak256. - SP-CLIENT-01: - The Canonical Runtime rejects evidence issued to an OAuth client other than + The Ceremony Client rejects evidence issued to an OAuth client other than the one fixed by its immutable ceremony profile. Depends on ASM-PROV-04, ASM-PROV-05, ASM-PROV-07, ASM-NOTARY-01, ASM-PROOF-01, and ASM-BROWSER-01. - Evidence: checked invariant in the Canonical Runtime, plus conformance tests + Evidence: checked invariant in the Ceremony Client, plus conformance tests (supporting). - SP-DELIVERY-01: An authorization response for one OAuth client reaches only an origin @@ -230,12 +256,12 @@ on it. (supporting, not proving). - SP-FRESH-01: Evidence older than its authenticated ceiling is rejected. Depends on - ASM-CHAIN-01, ASM-NOTARY-01, ASM-PROV-05, ASM-PROOF-01. Evidence: checked - invariant in the Platform Verifier. + ASM-LEDGER-01, ASM-NOTARY-01, ASM-PROV-05, ASM-PROOF-01. Evidence: checked + invariant in the LOPPV. - SP-REPLAY-01: - Within one Consumer deployment, one ceremony authorizes at most - one authoritative effect. Depends on ASM-CHAIN-01, ASM-CHAIN-02. Evidence: - checked invariant in the Consumer. + Within one Identity Integration deployment, one ceremony authorizes at most + one authoritative effect. Depends on ASM-LEDGER-01, ASM-LEDGER-02. Evidence: + checked invariant in the Identity Integration. ## 5. Authorization digest @@ -247,8 +273,8 @@ of a string. ```text authorizationPreimage = operationDomain // 32 bytes - || U16BE(platformVerifierVersion) // 2 bytes - || chainId // 32 bytes + || U16BE(platformCeremonyVersion) // 2 bytes + || ledgerId // 32 bytes || authorizationNonce // 32 bytes || U32BE(BYTE_LENGTH(transactionData)) // 4 bytes || transactionData // variable @@ -260,12 +286,12 @@ Every field but `transactionData` is fixed width, so the preimage is 102 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 + The Ceremony Client MUST construct every Authorization Digest as the + keccak256 of exactly the byte concatenation above. The Ceremony Client + 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 + value which does not fit its field. The Ceremony Client MUST encode + `operationDomain`, `ledgerId`, and `authorizationNonce` as exactly 32 bytes each, with no length prefix. Necessity: only `transactionData` varies in length, so every other field is at a fixed offset and the preimage cannot be reinterpreted by shifting a boundary. @@ -273,54 +299,56 @@ bytes plus the Authorized Transaction Data. `operationDomain` is the operation identifier and libID domain separator. - REQ-COMMON-01A (upholds SP-BIND-01): - The Consumer MUST fix one libID-namespaced ASCII operation-domain string for + The Identity Integration MUST fix one libID-namespaced ASCII operation-domain string for each transaction kind and derive - `operationDomain = keccak256(UTF8(domainString))`. The Consumer MUST NOT assign the same + `operationDomain = keccak256(UTF8(domainString))`. The Identity Integration MUST NOT assign the same operation domain to transaction kinds that can produce different authoritative effects. A new operation or a change to one operation's 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. - 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 Verifier Dispatcher MUST reject an OAuth Proof whose identity platform and + `platformCeremonyVersion` pair lies outside its Supported Version Set. A + change to any part of that boundary bumps the affected platform's version; + a common Authorization Digest change bumps every affected platform's + version. A verifier implementation or deployment change which preserves the + complete boundary does not. -`chainId` identifies the Consumer Chain. +`ledgerId` identifies the Execution Ledger. - 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 - MUST derive `chainId` as the keccak256 of those bytes, which is 32 bytes - wide whatever form the identifier took. Necessity: chains identify themselves + The Ledger Profile MUST fix the exact canonical identifier of its Execution + Ledger and the exact bytes that identifier contributes. The Ledger Profile + MUST derive `ledgerId` as the keccak256 of those bytes, which is 32 bytes + wide whatever form the identifier took. Necessity: ledgers 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 - 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. + rather than the identifier itself. The Ledger Profile author MUST ensure + those canonical bytes differ from every other Execution Ledger on which the + same operation domains may accept libID OAuth Proofs. This specification + supplies no global ledger-identifier registry; reusing the bytes forfeits + cross-ledger replay separation. The application composition MUST select the + Execution Ledger's Ledger Profile and supply its canonical Ledger ID to the + Ceremony Client for each ceremony. The Ceremony Client MUST validate and + commit that exact 32-byte value. Selecting a Ledger Profile is destination + selection, not proof authority: the Verifier Dispatcher MUST independently take + the Ledger ID of its digest recomputation from its own Execution Ledger + environment or immutable deployment configuration. The Verifier Dispatcher MUST + NOT take the Ledger ID from the OAuth Proof, Authorized Transaction Data, or + any other caller-controlled input. Necessity: an Application can select a + destination ledger just as it selects the operation and its transaction data, + while independent Execution Ledger recomputation makes a proof constructed for + any other ledger unusable there. Several Execution Ledgers expose no + intrinsic ledger 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 + The Ledger Profile MUST define how the Execution Ledger authenticates the + Transaction Author and supplies Block Time. The Identity Integration MUST obtain both from that authenticated environment rather than caller-controlled data. Necessity: the ceremony rules must not depend on one execution environment's caller or clock. @@ -329,15 +357,15 @@ the selected Platform Verifier, including this Authorization Digest layout. its own replay nullifier. - REQ-COMMON-01E (upholds SP-REPLAY-01): - The Canonical Runtime MUST draw the 32-byte `authorizationNonce` freshly for + The Ceremony Client MUST draw the 32-byte `authorizationNonce` freshly for each ceremony from a cryptographically secure random source. `transactionData` carries one transaction's arguments as opaque canonical bytes. - REQ-COMMON-01F (upholds SP-BIND-01): - The Chain Profile and Consumer protocol MUST fix one exact Authorized - Transaction Data encoding for each transaction kind. The Consumer MUST + The Ledger Profile and the Identity Integration's protocol MUST fix one exact Authorized + Transaction Data encoding for each transaction kind. The Identity Integration MUST decode `transactionData` into that format and reject trailing bytes, noncanonical encodings, and any other argument shape. @@ -346,9 +374,9 @@ two methods, chosen by what the platform's authorization can carry: | Identity platform | Where the Authorization Digest is bound | Who compares it | |---|---|---| -| Google | Authorization Digest public proof input, carried by the signed OIDC `nonce` | the Platform Verifier, against the digest recomputed under REQ-COMMON-02 | -| X | revealed `code_verifier` of the notarized token request | the Platform Verifier, by recomputing that verifier under REQ-COMMON-15A | -| GitHub | revealed `code_verifier` of the notarized token exchange | the Platform Verifier, by recomputing that verifier under REQ-COMMON-15A | +| Google | Authorization Digest public proof input, carried by the signed OIDC `nonce` | the LOPPV, against the digest recomputed under REQ-COMMON-02 | +| X | revealed `code_verifier` of the notarized token request | the LOPPV, by recomputing that verifier under REQ-COMMON-15A | +| GitHub | revealed `code_verifier` of the notarized token exchange | the LOPPV, by recomputing that verifier under REQ-COMMON-15A | The X and GitHub circuits expose no Authorization Digest public input, so a requirement to compare one is unsatisfiable on those paths; Google carries no @@ -356,17 +384,17 @@ 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 Verifier Dispatcher MUST recompute the Authorization Digest from the + caller-supplied operation domain and Platform Ceremony Version, its observed + Ledger ID, and the `authorizationNonce` and Authorized Transaction Data + carried in the OAuth Proof. - 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 + input, the LOPPV MUST reject a proof whose Authorization Digest public input differs from the digest recomputed under REQ-COMMON-02. - REQ-COMMON-02B (upholds SP-BIND-01): Where a Platform Profile carries the Authorization Digest through the PKCE - construction of §7 instead, the Platform Verifier MUST bind that digest by + construction of §7 instead, the LOPPV MUST bind that digest by the verifier recomputation of REQ-COMMON-15A. The Proving Circuit of such a profile MUST NOT expose an Authorization Digest public input. - REQ-COMMON-02C (upholds SP-BIND-01): @@ -376,37 +404,38 @@ compare there. Neither method is optional, and no profile uses both. neither carries evidence nothing has tied to the transaction it was authorized for. - REQ-COMMON-03 (upholds SP-REPLAY-01): - The Consumer MUST record every Authorization Digest it accepts, before + The Identity Integration MUST record every Authorization Digest it accepts, before applying any authoritative effect. - REQ-COMMON-03A (upholds SP-REPLAY-01): - The Consumer MUST reject an Authorization Digest it has already recorded. + The Identity Integration 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 - 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 + Recording at the Verifier Dispatcher instead would let anyone observing a + OAuth Proof call the Verifier Dispatcher first, consume the digest, and leave the + Identity Integration nothing to apply — a denial of service costing the attacker only a + fee. A digest is spendable once at each Identity Integration that accepts its operation + domain, and REQ-COMMON-01A leaves domain choice with the Identity Integration that lives with that consequence. - REQ-COMMON-04 (upholds SP-BIND-01): - The Consumer MUST authenticate the Transaction Author under its Chain Profile + The Identity Integration MUST authenticate the Transaction Author under its Ledger Profile and enforce the invoked transaction kind's authorization predicate before - applying any authoritative effect. The Consumer MUST NOT treat the Transaction - Submitter as the Transaction Author unless the Chain Profile authenticates + applying any authoritative effect. The Identity Integration MUST NOT treat the Transaction + Submitter as the Transaction Author unless the Ledger 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`, -`chainId = keccak256(UTF8("example:1"))`, `authorizationNonce = 0x5555…5555`, +keccak256(UTF8("libid.claim-identity"))`, `platformCeremonyVersion = 1`, +`ledgerId = keccak256(UTF8("example:1"))`, `authorizationNonce = 0x5555…5555`, and `transactionData = 0x00010203`: ```text operationDomain = 0xcb29bed0428519ef88a3d670e8203db76e06f41aca3e684e2c63b516c9b93e1b -chainId = 0x38064d82f31db40935cc75f2a0d07dcfb448d7c08e7484fc30f5de95484a4066 +ledgerId = 0x38064d82f31db40935cc75f2a0d07dcfb448d7c08e7484fc30f5de95484a4066 authorizationPreimage = 0xcb29bed0428519ef88a3d670e8203db76e06f41aca3e684e2c63b516c9b93e1b000138064d82f31db40935cc75f2a0d07dcfb448d7c08e7484fc30f5de95484a406655555555555555555555555555555555555555555555555555555555555555550000000400010203 authorizationDigest = 0xb318fb559e16a179b853ed2853576cda16032d93b0839bb81a55135d334c0af5 ``` @@ -417,20 +446,20 @@ in §7. ### 5.1 Verification path -A Consumer never verifies a libID proof itself. Verification is four roles -on the Consumer Chain, each answering to the one above it: +An Identity Integration never verifies a libID proof itself. Verification is four roles +on the Execution Ledger, each answering to the one above it: ```text -Consumer names the platform and version, pays the quoted fees, +Identity Integration names the platform and 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, +Verifier Dispatcher selects the LOPPV for that pair, recomputes the + Authorization Digest, hands it and the OAuth Proof down, returns the result | v -Platform Verifier checks that platform's fields, verifies the proof under the +LOPPV checks that platform's fields, verifies the proof under the artifact selected for that pair, then calls the Notary Service once per attestation its profile requires — zero times for a profile carrying none @@ -440,33 +469,38 @@ 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 +Only the Identity Integration 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. +field checking. The Supported Version Set lives in the Verifier Dispatcher. +The Platform Profile defines every immutable platform constant — endpoints, +revealed ranges, attestation format, validity rules, and parameter keys — and +each ledger's LOPPV enforces that profile. Verifier governance owns the mutable +verifier artifact, Notary Service, trust roots, fees, and parameter values. The +verifier's code and address may differ across ledgers without changing the +Platform Ceremony Version. The Identity Integration holds none of those +constants. 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 +is zero, so its path stops at the LOPPV 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 OAuth Proof 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 + The Identity Integration MUST call the Verifier Dispatcher with the identity platform, the + Platform Ceremony Version, the OAuth Proof, and the native value the quotation of REQ-COMMON-06E returns. 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. + of one verification entry point serving every Identity Integration. - REQ-COMMON-05A: - The Proof Verifier MUST select the Platform Verifier its Supported Version - Set registers for that pair. The Proof Verifier MUST NOT accept a + The Verifier Dispatcher MUST select the LOPPV its Supported Version + Set registers for that pair. The Verifier Dispatcher MUST NOT accept a caller-supplied verifier address. Necessity: a caller-selected verifier - verifies nothing. + verifies nothing. Each Execution Ledger selects its own implementation; that + implementation choice is outside the Platform Ceremony Version. - REQ-COMMON-05B: - The Proof Verifier MUST support more than one Platform Verifier Version of + The Verifier Dispatcher MUST support more than one Platform Ceremony 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 @@ -476,93 +510,95 @@ an identity session — so one submission on either path pays two fees. - 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 Execution Ledger 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 + The LOPPV MUST check every field its Platform Profile assigns to it under REQ-COMMON-19E. - The Platform Verifier MUST obtain attestation authenticity from the Notary + The LOPPV 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 + LOPPV MUST treat each of those decisions as final. The LOPPV 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 LOPPV MUST return its verified fields: 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 + observation time are what the ceremony exists to produce, and the Identity Integration has no other authenticated source for them. - REQ-COMMON-45 (upholds SP-BIND-01, SP-EXCHANGE-01): - The Platform Verifier MUST verify the proof carried in the submission under + The LOPPV MUST verify the proof carried in the OAuth Proof 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 - artifact. The Platform Verifier MUST NOT accept a caller-supplied artifact, + the submitted identity platform and Platform Ceremony Version. Different + Execution Ledgers MAY select different artifacts, but each artifact MUST + enforce the same proof statement for that version. The LOPPV MUST reject an + OAuth Proof whose proof does not verify under that + artifact. The LOPPV 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 role performed the acceptance; with no rule placing that work anywhere, no 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 + The Verifier Dispatcher MUST pass the digest it recomputed under REQ-COMMON-02, + together with the complete OAuth Proof, to the LOPPV it + selected. The LOPPV 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 + digest recomputed one hop above them, and a LOPPV 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 operation domain travels in the OAuth Proof and is authenticated by digest +recomputation rather than trusted: an OAuth Proof 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. +uses. The Verifier Dispatcher therefore returns the domain it authenticated, and +the Identity Integration decides whether that domain is its own. - REQ-COMMON-06 (upholds SP-BIND-01): - The Proof Verifier MUST return the authenticated operation domain, the + The Verifier Dispatcher 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 + Identity Integration on acceptance. The Verifier Dispatcher 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 Identity Integration MUST reject a returned operation domain it does not own. The + Identity Integration 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 + The Verifier Dispatcher MUST NOT decode, interpret, or apply the Authorized + Transaction Data. Necessity: transaction semantics belong to the Identity Integration that fixed the operation domain. - 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 + The Verifier Dispatcher MUST take the Ledger ID of the digest recomputation of + REQ-COMMON-02 from the Ledger ID it observes under ASM-LEDGER-02. The Verifier + Dispatcher MUST NOT read a Ledger ID from the OAuth Proof. The Verifier Dispatcher + MUST dispatch on the Platform Ceremony Version the OAuth Proof names. + Necessity: the Execution Ledger 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 + that digest is the whole check on either; the OAuth Proof carries no Ledger 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. + OAuth Proof in the first place. 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 attestation to verify carries no value at all. - REQ-COMMON-06D: - The Proof Verifier and the Platform Verifier MUST each reject a call whose + The Verifier Dispatcher and the LOPPV 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 + the quotation of REQ-COMMON-06E before forwarding. The Verifier Dispatcher MUST + forward exactly the value the LOPPV requires. The LOPPV 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 + The Verifier Dispatcher MUST expose a fee quotation for an identity platform and + Platform Ceremony 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 + zero where it requires none. Necessity: an Identity Integration 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,10 +610,10 @@ 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 - rule; no circuit re-verifies it. The Canonical Runtime MUST compare a + fixed range layout of §9 does not hold. This is a browser-side serialization + rule; no circuit re-verifies it. The Ceremony Client 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 + emits for the expected unencoded value. The Ceremony Client MUST NOT compare that range directly with the unencoded value or apply a second, permissive decoder. - REQ-COMMON-08: @@ -599,10 +635,11 @@ 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 Ceremony Client 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 + The Ceremony Client MUST ignore a forwarding target supplied in the redirect request. - REQ-COMMON-32 (upholds SP-BIND-01, SP-EXCHANGE-01): The Deployment MUST run recurring integration probes establishing @@ -631,77 +668,68 @@ 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 Ceremony Client 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 Ceremony Client 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`: + The LOPPV MUST recompute `code_verifier` from the Authorization + Digest and the submitted `authorizationNonce`. The LOPPV MUST + reject an OAuth Proof 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 LOPPV 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 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 +own right, and it reaches the Identity Integration through §5.1. The Ceremony Client 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 Execution Ledger 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 evidence differs. -| Identity platform | Authenticated source | How the Platform Verifier authenticates the bytes | +| Identity platform | Authenticated source | How the LOPPV 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 OAuth Proof carries the bytes; the LOPPV 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 | @@ -710,58 +738,60 @@ user's browser follows. The protocol therefore spends nothing to conceal it, and returns the readable value rather than a digest of it. - REQ-COMMON-16 (upholds SP-CLIENT-01): - The Platform Verifier MUST return the exact authenticated bytes of the - client identifier. The Platform Verifier MUST NOT return a digest in their - place. Necessity: one representation across platforms lets a Consumer + The LOPPV MUST return the exact authenticated bytes of the + client identifier. The LOPPV MUST NOT return a digest in their + place. Necessity: one representation across platforms lets an Identity Integration 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 LOPPV MUST reject an OAuth Proof 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. - REQ-COMMON-16B (upholds SP-CLIENT-01): - The Platform Verifier MUST require a client identifier authenticated from a + The LOPPV MUST require a client identifier authenticated from a form-serialized request to match `[A-Za-z0-9*._-]+`, the serializer's - nonempty byte-identical ASCII subset. The Platform Verifier MUST reject any + nonempty byte-identical ASCII subset. The LOPPV MUST reject any other revealed client-identifier bytes. Necessity: the - Platform Verifier returns the revealed bytes as the one cross-platform + LOPPV returns the revealed bytes as the one cross-platform 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 Identity Integration 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 Ceremony Client MUST reject an OAuth Proof 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 + The Verifier Dispatcher and the LOPPV MUST NOT require the exposed + client identifier to belong to a registered set. The Identity Integration + 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 + policy; authoritative transaction permission comes from the Identity Integration'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 Execution Ledger effect. ## 9. Notarized transcripts and attestation verification A proof authenticates bytes, not fields, and three roles check fields in 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 -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, +template. The LOPPV checks the fields carried in revealed +attestation bytes, which it reads for itself. The Ceremony Client checks the +ceremony state that exists only in the browser and reaches no proof and +produces no Execution Ledger effect. One role's extraction of each field is +the authoritative one — the +Proving Circuit's where the bytes stay hidden, the LOPPV's where +they are revealed. The Ceremony Client may repeat an authoritative extraction +over the same bytes to derive local identity fields from the exact OAuth Proof, +and nothing on the Execution Ledger 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. @@ -781,7 +811,7 @@ boundary template (REQ-COMMON-19C), and each committed response range is anchored by the delimiters its Platform Profile fixes (REQ-COMMON-18A). Uniqueness of a field inside an authenticated response is ASM-PROV-06 rather than a scan the Proving Circuit performs; the only duplicate scan in the -protocol is the one REQ-COMMON-19A gives the Platform Verifier over bytes it +protocol is the one REQ-COMMON-19A gives the LOPPV over bytes it can read. Disclosure and verification are two separate layers. The Platform Profile @@ -797,16 +827,18 @@ 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 + supports that platform and version pair on an Execution Ledger. 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 + its pinned attestation format carries. The LOPPV MUST reject an attestation whose range commitments use any other algorithm. Launch profiles pin SHA-256. Necessity: the notarization library's default commit algorithm is BLAKE3 while the Proving Circuit computes SHA-256, so a prover @@ -821,7 +853,7 @@ exposes a minimal set of public inputs, which never includes a credential. commitments equal, make that statement vacuous, and publish a stable identifier for the credential. - REQ-COMMON-18A (upholds SP-EXCHANGE-01): - The Platform Verifier MUST check that the revealed ranges and hidden-range + The LOPPV MUST check that the revealed ranges and hidden-range commitments tile the transcript in the exact layout its profile fixes, with each hidden range bounded by revealed anchor bytes, or, where a hidden range reaches an end of the transcript, by the signed transcript length of @@ -837,8 +869,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 LOPPV +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 @@ -850,7 +882,7 @@ and no `authorization` needle to count. - REQ-COMMON-35 (upholds SP-EXCHANGE-01): For an identity-session request that commits a credential inside an HTTP - `Authorization` header, the Platform Verifier MUST require the revealed + `Authorization` header, the LOPPV MUST require the revealed ranges plus the committed range to account for exactly the signed transcript length of the request direction, with no gap and no overlap. Necessity: a check @@ -860,19 +892,18 @@ and no `authorization` needle to count. derivable from the ranges around it. - REQ-COMMON-36 (upholds SP-EXCHANGE-01): The Notary Service MUST carry the total transcript length of each direction - of the session it observed in the data it signs. The Platform Verifier MUST + of the session it observed in the data it signs. The LOPPV MUST take the transcript length used for the coverage check of REQ-COMMON-35 from those signed lengths and from nothing else. Necessity: without a signed length, bytes past the last revealed range are invisible, which is what makes a planted-header request pass every substring-anchored check. - REQ-COMMON-39 (upholds SP-EXCHANGE-01): - For that same identity-session request, the Platform Verifier MUST + For that same identity-session request, the LOPPV MUST 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, + LOPPV MUST leave carriage-return and line-feed bytes in + place. The LOPPV 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 @@ -884,7 +915,7 @@ and no `authorization` needle to count. second genuine `authorization` header is rejected whatever the Identity Platform would have done with it. - REQ-COMMON-40 (upholds SP-EXCHANGE-01): - For that same identity-session request, the Platform Verifier MUST require + For that same identity-session request, the LOPPV MUST require the raw transcript bytes immediately before the committed range to be exactly `\r\nauthorization: Bearer `, and the raw transcript bytes immediately after it to be exactly `\r\n`. @@ -894,7 +925,7 @@ and no `authorization` needle to count. cannot commit a range positioned somewhere else. Two fixed comparisons at known offsets replace a derived one. - REQ-COMMON-43 (upholds SP-EXCHANGE-01): - The Platform Verifier MUST NOT apply REQ-COMMON-35, REQ-COMMON-39, or + The LOPPV MUST NOT apply REQ-COMMON-35, REQ-COMMON-39, or REQ-COMMON-40 to a credential its Platform Profile commits in a request body. The Platform Profile MUST instead order such a credential last under REQ-COMMON-22 and constrain its charset to exclude a form delimiter. @@ -929,30 +960,32 @@ and no `authorization` needle to count. bytes that field is read from and the one algorithm that reads them. The Platform Profile MUST name exactly one role whose extraction of that field is authoritative. The Platform Profile MUST make the Proving Circuit - authoritative where those bytes stay hidden, and the Platform Verifier - authoritative where they are revealed to the Consumer Chain. The Platform - Profile MUST make the Canonical Runtime authoritative only for a field no - proof statement and no Consumer Chain component reads. The Canonical Runtime + authoritative where those bytes stay hidden, and the LOPPV + authoritative where they are revealed to the Execution Ledger. The Platform + Profile MUST make the Ceremony Client authoritative only for a field no + proof statement and no Execution Ledger component reads. The Ceremony Client 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. + algorithm, for display and local checks. The Ceremony Client MUST return + that repeat only with the exact OAuth Proof whose bytes it read. The + Ceremony Client MUST discard and rederive the local identity fields if any + proof, attestation, identity platform, Platform Ceremony Version, or other + OAuth Proof field changes. The Ceremony Client MUST NOT label those fields + authoritative before the Identity Integration accepts that OAuth Proof; only + the Identity Integration's result is authoritative. The Platform Profile MUST NOT let a - proof statement or a Consumer Chain component depend on that repeat. A + proof statement or an Execution Ledger 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 Ceremony Client's repeat is what lets the + browser derive the identity the exact OAuth Proof asks the Execution Ledger + to bind, so allowing the local identity fields and OAuth Proof to diverge + would reopen the gap where the display names one account and the OAuth Proof + 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 Execution Ledger, 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 @@ -961,7 +994,7 @@ and no `authorization` needle to count. body end. The circuit does not scan the rest of the body for duplicates; that property is ASM-PROV-07. - REQ-COMMON-19A (upholds SP-EXCHANGE-01): - The Platform Verifier extracting a field from revealed attestation bytes + The LOPPV extracting a field from revealed attestation bytes MUST reject a transcript in which the field's full delimiter matches at more than one position. Necessity: an authenticated response value the account holder influences, such as a display name, can embed a lookalike @@ -985,7 +1018,7 @@ and containing no query. Launch profiles use TCP port 443. Authority prevents a transcript from an attacker-controlled server from substituting for the platform; path separates operations on the same server; method separates operations with different HTTP semantics. All three are -authenticated by the attestation itself, so the Platform Verifier compares +authenticated by the attestation itself, so the LOPPV compares them against its profile constants and no circuit compiles a platform endpoint constant. @@ -995,15 +1028,15 @@ constant. holds the session, which is at observation; the verifying side holds no transcript. - REQ-COMMON-21A (upholds SP-BIND-01): - The Platform Verifier MUST compare the authenticated authority, and the + The LOPPV MUST compare the authenticated authority, and the method and path revealed in the request, byte for byte with the profile constants it pins. - REQ-COMMON-21B (upholds SP-EXCHANGE-01): The Implementation MUST construct every notarized request with the media type and `redirect_uri` from its immutable deployment profile. Neither value is a - Consumer input. Necessity: media type selects the platform's request parser, + Identity Integration input. Necessity: media type selects the platform's request parser, while redirect URI is application delivery configuration rather than - Consumer-Chain identity authority. + Execution Ledger 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 @@ -1026,7 +1059,7 @@ for every form-encoded token request. ### 9.1 Attestation verification and its fee -An attestation is authenticated on the Consumer Chain, not inside the Proving +An attestation is authenticated on the Execution Ledger, not inside the Proving Circuit. The Notary Service takes attested data and its notary signature and answers one question: is this attestation authentic. Verifying is a metered service and carries a fixed fee. A Platform Profile whose @@ -1056,13 +1089,13 @@ below governs one attestation a profile does require. whose fee was not delivered. Necessity: verification is a metered service, and an unpaid verification is unmetered. - REQ-COMMON-34A: - The Consumer MUST deliver every fee the quotation of REQ-COMMON-06E returns - in the Consumer Chain's native asset - over the native value-transfer path its Chain Profile fixes. Necessity: + The Identity Integration MUST deliver every fee the quotation of REQ-COMMON-06E returns + in the Execution Ledger's native asset + over the native value-transfer path its Ledger Profile fixes. Necessity: cross-component interoperability without naming one execution environment's transfer mechanism. - REQ-COMMON-34B: - The Chain Profile MUST define that native value-transfer path and the unit + The Ledger Profile MUST define that native value-transfer path and the unit the fee is denominated in. Necessity: the fee is unpayable without both. - REQ-COMMON-34C: The Notary Fee MUST NOT vary with the attested content, @@ -1071,7 +1104,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 + OAuth Proof 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 +1113,12 @@ 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 LOPPV MUST deliver the fees of one OAuth Proof'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 + The LOPPV MUST leave no fee delivered once it rejects the + OAuth Proof. The Ledger Profile MUST define the mechanism by which a rejected + call leaves no value transferred and no state changed in its Identity + Integration. 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. @@ -1108,71 +1141,72 @@ Service. value. The Implementation MUST NOT infer it from an HTTP `Date` header or a local clock. - REQ-COMMON-25A (upholds SP-FRESH-01): - The Consumer MUST complete an otherwise valid authority operation even when - its mutable metadata is stale. The Consumer MUST update + The Identity Integration MUST complete an otherwise valid authority operation even when + its mutable metadata is stale. The Identity Integration MUST update mutable metadata and its watermark only when `metadataObservedAt` is strictly - newer than the stored watermark. The Consumer MUST leave both + newer than the stored watermark. The Identity Integration MUST leave both unchanged for older or equal evidence, including equal evidence carrying conflicting metadata. - REQ-COMMON-26 (upholds SP-FRESH-01): - The Platform Verifier MUST derive `proofValidUntil` from the platform profile's + The LOPPV 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 LOPPV MUST reject an OAuth Proof where `Block Time >= proofValidUntil`. - REQ-COMMON-27 (upholds SP-FRESH-01): - The Platform Verifier MUST NOT accept a caller-supplied validity bound. + The LOPPV MUST NOT accept a caller-supplied validity bound. - REQ-COMMON-28 (upholds SP-FRESH-01): The Implementation MUST perform every timestamp addition and comparison with checked arithmetic before narrowing to an unsigned 64-bit integer. ## 11. Conformance -Roles: Canonical Runtime, Redirect Runtime, Token-Exchange Service, Proving -Circuit, Proof Verifier, Platform Verifier, Notary Service, Consumer. The +Roles: Ceremony Client, Ceremony Popup, Proving Circuit, Verifier Dispatcher, +LOPPV, Notary Service, Identity Integration. 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 OAuth Proof 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 Ceremony Client accepts the canonical Ledger ID selected from either of two + Ledger Profiles and produces distinct digests; the Verifier Dispatcher accepts the + proof for its own Ledger Profile and rejects the proof constructed for the + other, and neither the OAuth Proof nor Authorized Transaction Data can + override its Ledger ID. Two Ledger Profiles which accept the same operation domains are ineligible when they reuse the same canonical identifier bytes; - the Consumer rejects a + the Identity Integration rejects a caller-substituted Block Time; and a Transaction Submitter that cannot satisfy the transaction kind's Transaction Author predicate. - TEST-COMMON-03 (exercises REQ-COMMON-03, REQ-COMMON-03A): 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. + OAuth Proof 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 - identifier registered nowhere remains acceptable to the Platform Verifier. + The Ceremony Client rejects an OAuth Proof carrying a client identifier + other than its immutable profile's client, while a proof carrying a client + identifier registered nowhere remains acceptable to the LOPPV. Every platform returns the identifier as exact bytes, never a digest, and a - submission whose supplied bytes its evidence does not authenticate is + OAuth Proof 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): @@ -1185,16 +1219,17 @@ the constructions that role implements. prove; and a transcript whose ranges do not tile the profile layout is rejected. A profile naming two authoritative extractions of one field, or naming an authoritative extraction by a role that cannot see the bytes, is - ineligible; a profile whose Canonical Runtime repeats an extraction the - Platform Verifier owns, and one extracting a field in one role and comparing + ineligible; a profile whose Ceremony Client repeats an extraction the + LOPPV 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. + OAuth Proof after deriving the local identity fields discards those fields + and requires derivation from the replacement OAuth Proof. A profile whose + Attestation Count is nonzero and which defines no attestation protocol, + format, or required security properties is invalid. A destination ledger + 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, + The LOPPV rejects an authenticated foreign authority, method, or path. The request constructor refuses a media type or `redirect_uri` differing from its immutable deployment profile. One verifying key serves two deployments configured with different clients and redirect URIs. @@ -1203,13 +1238,14 @@ 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 OAuth Proof 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 @@ -1217,12 +1253,12 @@ the constructions that role implements. - 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 - than the one the ceremony committed fails digest recomputation; a Consumer + one platform both verify; an OAuth Proof naming an operation domain other + than the one the ceremony committed fails digest recomputation; an Identity Integration 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 Ledger ID from the Verifier Dispatcher's observed environment, so the + same OAuth Proof presented on another ledger fails it and no caller-supplied + Ledger ID reaches it; 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 +1268,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 OAuth Proof 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,51 +1296,50 @@ 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 OAuth Proof 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; an OAuth Proof 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; an OAuth Proof on a two-count profile is quoted and charged + exactly two fees; and an OAuth Proof 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 + An OAuth Proof whose proof does not verify under the artifact selected for + its identity platform and Platform Ceremony 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 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 + The LOPPV receives the digest the Verifier Dispatcher recomputed + together with the complete OAuth Proof, 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. + LOPPV taking the digest from any other source rejects the + OAuth Proof. ## 12. Security Considerations This document enforces SP-BIND-01, SP-CLIENT-01, SP-EXCHANGE-01, 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 -because it binds no Consumer identifier. Every Consumer transaction +Replay within one Identity Integration deployment is prevented by `authorizationNonce` +and REQ-COMMON-03. Replay across Execution Ledgers whose Ledger Profiles use +distinct canonical identifier bytes is prevented by the Ledger ID in the +digest; a profile collision forfeits that separation. Replay across Platform Ceremony Versions is prevented by +`platformCeremonyVersion`. The digest does not prevent cross-deployment replay +because it binds no Identity Integration identifier. Every Identity Integration transaction kind therefore defines an authorization predicate over the authenticated Transaction Author and the proof-bound Authorized Transaction Data. A copied 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 +ceremony the Ceremony Client opened. The check is local because independent +Application Deployments own different OAuth clients. The Identity Integration authenticates the proof-bound transaction and Transaction Author instead; it does not maintain an OAuth-client allowlist or admit applications on the -Consumer Chain. +Execution Ledger. Consent-screen phishing remains outside protocol enforcement. A hostile site borrowing an honest deployment's OAuth client does not receive its response, @@ -1316,10 +1351,10 @@ 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 -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 +The verification path of §5.1 concentrates authority. An Identity Integration accepts the +Verifier Dispatcher's decision, operation domain, and Authorized Transaction Data +without rechecking them, so a compromised Verifier Dispatcher authorizes arbitrary +transactions at every Identity Integration at once; a compromised LOPPV does the same for one platform and version; a compromised Notary Service accepts attestations no notary signed. Their selection is verifier governance, which is therefore a trust root rather than configuration. The Notary Fees are a @@ -1332,10 +1367,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 OAuth Proof publishes it afterwards +as the same nonce already required to recompute the Authorization Digest, +which also lets the LOPPV 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..0697215d 100644 --- a/specs/libid.md +++ b/specs/libid.md @@ -15,85 +15,92 @@ specifications. ## System model and specification ownership -libID turns an identity-platform authorization into a proof that a Consumer -applies to one proof-bound transaction: +libID turns an identity-platform authorization into a proof that an Identity +Integration 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 - | - Notary Service - (X and GitHub, - once per - attestation) +User -> Application Deployment -> Ceremony Client -> Identity Platform + | + v + Proving Circuit -> Identity Integration + | + v + Verifier Dispatcher + | + Ledger OAuth Platform Proof Verifier (LOPPV) + | + 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 +An Identity Integrator deploys or maintains the Identity Integration. +Application Deployments use it and may be operated independently. + +The Identity Integration never verifies evidence itself. It calls the +Verifier Dispatcher, which selects the LOPPV 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 back as an accept-or-reject decision plus the authenticated operation domain, -Authorized Transaction Data, and client identifier, and the Consumer decides -what that transaction means. [Common §5.1](ceremony-common.md#51-verification-path) -owns this path. +Authorized Transaction Data, and client identifier. The Identity Integration +decides what that transaction means. +[Common §5.1](ceremony-common.md#51-verification-path) 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 +An Application Deployment operator controls its frontend, redirect deployment, +OAuth 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 +Execution Ledger authenticates the Transaction Author and supplies its Ledger ID and Block Time. | Principal | Knows and can | Trusted for | Not trusted for | |---|---|---|---| | User | chooses an account and authorizes an operation | human intent | parsing or cryptographic verification | -| Application operator | configures clients and deployment assets; starts or withholds work | deployment availability and declared configuration | identity fields, proof target, or proof validity | +| Application Deployment operator | configures clients and deployment assets; starts or withholds work | deployment availability and declared configuration | identity fields, proof target, or proof validity | +| Identity Integrator | deploys or maintains an Identity Integration and its operation domains | application-specific transaction semantics | proof authenticity outside the Verifier Dispatcher's result | | Identity-platform operator | authenticates accounts and issues signed or TLS-authenticated responses | the `ASM-PROV-*` behavior the selected profile cites | the proof-bound transaction or Transaction Author | | Notary operator | operates the X/GitHub attestation key and observes sessions | `ASM-NOTARY-01` | user intent or transaction authorization | | 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 +X/GitHub notary keys, the selected LOPPV artifacts, the Verifier +Dispatcher, the LOPPVs it selects, Verifier +governance, and Execution Ledger consensus. The Verifier Dispatcher is the most concentrated of these: every +Identity Integration 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 +authorizes arbitrary transactions at every Identity Integration at once. A compromised +LOPPV 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. +already committed. Loss of an Application Deployment is a liveness failure. +Compromise of the Ceremony Client build or its supply chain defeats local +client and operation construction. Compromise of a platform signing root, notary key, or +selected LOPPV 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 | +| Ledger ID, Transaction Author, Block Time, and transaction-data encoding | Identity Integration's Ledger 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 | Identity Integration 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 +Identity Integration 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 ledger only while that ledger's +Verifier Governance Process selects a conforming verifier artifact and, where +required, a compatible Notary Service. ## Enforceable guarantees and accepted boundaries -The Platform Verifier enforces the proof-bound operation — comparing the +The LOPPV enforces the proof-bound operation — comparing the Authorization Digest public input on Google, recomputing the revealed `code_verifier` on X and GitHub (REQ-COMMON-02A, REQ-COMMON-15A) — verifies the proof under the artifact selected for the submitted platform and version @@ -101,23 +108,23 @@ the proof under the artifact selected for the submitted platform and version is the signed ID Token on Google and the revealed attestation bytes on X and GitHub; the Proving Circuit proves only what cannot be read from that evidence, which is Google's signature relation and, on X and GitHub, that one -hidden bearer opens both sessions' commitments. The Consumer enforces replay +hidden bearer opens both sessions' commitments. The Identity Integration 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, -PKCE, delivery, notary, browser, verifier-soundness, and chain behaviors. It +an effect (REQ-COMMON-03, REQ-COMMON-03A). The Ceremony Client +locally enforces the selected OAuth client and redirect profile. The protocol +assumes the named identity-platform parser, +PKCE, delivery, notary, browser, verifier-soundness, and ledger 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 -Consumer Chain failure. +Execution Ledger failure. ## Conventions @@ -132,7 +139,7 @@ as shown here. Protocol parameters are governance-owned unsigned 64-bit values expressed in seconds, read where they are enforced. The Verifier Governance Process may update a supported parameter and emits its -key, previous value, and new value. The Platform Verifier reads the current +key, previous value, and new value. The LOPPV reads the current value when it verifies a proof; browser reads are advisory only. Lowering a parameter may reject an outstanding proof, while raising one may extend an outstanding X/GitHub proof. Current trust-root membership remains required. @@ -141,7 +148,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 @@ -150,9 +157,9 @@ outstanding X/GitHub proof. Current trust-root membership remains required. new value after a successful update. Necessity: independent implementations must read and observe one closed parameter set. - REQ-PARAM-02: - The Platform Verifier MUST use the current governance value and checked + The LOPPV MUST use the current governance value and checked arithmetic whenever a ceremony rule names one of these parameters. The - Platform Verifier MUST NOT accept a caller-supplied substitute. Necessity: + LOPPV MUST NOT accept a caller-supplied substitute. Necessity: callers must not widen proof freshness. - TEST-PARAM-01 (exercises REQ-PARAM-01, REQ-PARAM-02): The launch values reproduce the platform validity vectors; an unknown key, diff --git a/specs/platform-ceremonies.md b/specs/platform-ceremonies.md index 3d63325e..5024164d 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 Identity Integration'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,62 @@ 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 +OAuth Proof. Each platform section defines its own launch version. A version +covers the digest, OAuth construction, and platform-specific proof statement, +not any Execution Ledger's verifier implementation. + +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 Ceremony Client MUST select and retain one exact profile for the live + ceremony. The Ceremony Client 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 LOPPV artifact or Notary + Service deployment. Before accepting that platform and version pair, the + Verifier Governance Process MUST select a conforming LOPPV artifact and, for a + TLSNotary profile, a compatible Notary Service. Necessity: ledger artifacts + implement the versioned ceremony boundary; they do not define it. +- REQ-PLAT-01B: + The Ceremony Client 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 must dispatch on + one canonical pair. - REQ-PLAT-02: - The Canonical Runtime MUST treat a profile as ineligible until the + The Ceremony Client 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 Ceremony Client 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 Ceremony Client MUST derive the local identity fields exclusively from + the Platform Profile's canonical source in the exact OAuth Proof it + returns. + Those fields are not an authority decision; only the Identity Integration's + acceptance of that exact OAuth Proof is. For X and GitHub, the Ceremony + Client MUST parse the exact revealed identity-response bytes that the + LOPPV extracts, using the same canonical extraction and + normalization rules. The Ceremony Client 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 @@ -112,17 +127,17 @@ derivation, layered strictly: - REQ-PLAT-08A (upholds SP-BIND-01): The Proving Circuit and the Notary Service MUST NOT case-fold, trim, or - otherwise transform identity bytes. The Consumer MUST receive the handle as + otherwise transform identity bytes. The Identity Integration MUST receive the handle as the raw authenticated bytes of its platform source. - REQ-PLAT-08B (upholds SP-BIND-01): - The Consumer MUST derive the normalized handle from the - proof-verified raw bytes on its own write path. The Consumer MUST + The Identity Integration MUST derive the normalized handle from the + proof-verified raw bytes on its own write path. The Identity Integration MUST NOT accept a caller-supplied normalized handle or pre-hashed handle key. Necessity: the handle arrives inside a proof; a caller supplying the derived key could name any handle it liked. - REQ-PLAT-08C: A browser-side normalization exists only for display and local checks. No - proof statement or Consumer behavior may rely on it. Necessity: a check + proof statement or Identity Integration behavior may rely on it. Necessity: a check running in software the prover chooses whether to run is not a defense. Normalization applies these per-platform criteria: ASCII-only input with @@ -139,7 +154,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 Identity Integration fail conformance. ### 2.2 Metadata ordering and validity ceilings @@ -163,24 +178,29 @@ refresh the authorization. The named lifetimes are current Google's signed `exp` already supplies the accepted one-hour ordering and validity value. A Google proof also requires its signing modulus to remain in -the Platform Verifier's active set. The Authorization Digest carries no expiration. +the LOPPV's active set. The Authorization Digest carries no expiration. `metadataObservedAt` is the monotone metadata watermark of common REQ-COMMON-25A. Older evidence cannot regress stored metadata and does not block an otherwise valid authority operation. - REQ-PLAT-09 (upholds SP-FRESH-01): - The Platform Verifier MUST reject an X or GitHub attestation timestamp more than + The LOPPV MUST reject an X or GitHub attestation timestamp more than `maxFutureAttestationSkew` ahead of Block Time. - REQ-PLAT-09A (upholds SP-FRESH-01): - The Platform Verifier MUST derive `metadataObservedAt` and + The LOPPV MUST derive `metadataObservedAt` and `proofValidUntil` from the exact sources in the table above and from nothing else. ## 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 @@ -203,22 +223,23 @@ operational guidance for obtaining a token whose signed claims satisfy §3.2–§3.3. The signed ID Token is the only Google evidence. - REQ-PLAT-10 (upholds SP-BIND-01): - The Canonical Runtime MUST set `nonce` to the base64url encoding of the 32 + The Ceremony Client MUST set `nonce` to the base64url encoding of the 32 Authorization Digest bytes, not to hexadecimal text. - REQ-PLAT-11 (upholds SP-DELIVERY-01): - The Canonical Runtime MUST request only `response_type=id_token` with - `response_mode=fragment`. The Canonical Runtime MUST NOT request an + The Ceremony Client MUST request only `response_type=id_token` with + `response_mode=fragment`. The Ceremony Client 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 Ceremony Popup 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 Ceremony Popup MUST copy the bounded query and fragment into memory and + clear both before storage or network access. The Ceremony Client MUST require + an empty query and a fragment carrying exactly one `state` plus exactly one + `id_token` XOR `error`. The Ceremony Client 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 + The Ceremony Client MUST match `state` to exactly one live local ceremony and consume it once before accepting the ID Token. No server-side state or prepare request participates in this lookup. @@ -233,16 +254,16 @@ Google nonce = sxj7VZ4WoXm4U-0oU1ds2hYDLZOwg5u4GlUTXTNMCvU ### 3.2 Local token verification - REQ-PLAT-14 (upholds SP-BIND-01): - The Canonical Runtime MUST reject an ID Token whose `nonce` differs from the + The Ceremony Client 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 Ceremony Client MUST reject a Google response carrying `code` or `access_token`. Necessity: neither artifact belongs to this authentication-only profile. ### 3.3 Proof statement -The Proving Circuit and Consumer enforce all of the following: +The Proving Circuit and Identity Integration enforce all of the following: - REQ-PLAT-16 (upholds SP-CLIENT-01): The Proving Circuit MUST hash the exact ASCII @@ -259,9 +280,10 @@ 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 + set as an input. The LOPPV alone checks the modulus under REQ-PLAT-23. JWK decoding and canonical-encoding validation happen where a modulus is admitted to the trusted set, per REQ-PLAT-24; the JWK encoding appears in no signed artifact, so proving it would add nothing. @@ -274,13 +296,13 @@ require a verifier that dispatches on the header `alg`; none exists here. | Authorization Digest | signed `nonce`, decoded as exactly 32 bytes | | client-identifier digest | `SHA256` of the signed `aud` | | canonical `userId` | signed `sub` | - | raw `email` bytes | signed `email`; the Consumer derives the normalized handle | + | raw `email` bytes | signed `email`; the Identity Integration derives the normalized handle | | evidence timestamp | signed `exp`; used for both `metadataObservedAt` and `proofValidUntil` | | RSA modulus | exact `n` that verified the JWS; `e = 65537` is profile-fixed | The Proving Circuit MUST NOT expose a detached second representation of a claim. Proofs are over raw bytes; normalization, such as lowercasing the - handle, is the Consumer's decision at consumption time. + handle, is the Identity Integration's decision at consumption time. - REQ-PLAT-17 (upholds SP-BIND-01): The Proving Circuit MUST prove the signed `iss` equals `https://accounts.google.com`. @@ -290,11 +312,11 @@ require a verifier that dispatches on the header `alg`; none exists here. The Proving Circuit MUST expose `SHA256` of the signed `aud` as the 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 LOPPV MUST require `SHA256` of the `aud` bytes carried in + the OAuth Proof to equal that public input. The LOPPV 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 + inputs, and the Identity Integration still receives the readable value. Admission stays permissionless per common REQ-COMMON-17C. - REQ-PLAT-20: The Proving Circuit MUST prove `email_verified` is the boolean `true`. @@ -311,14 +333,14 @@ require a verifier that dispatches on the header `alg`; none exists here. Duplicate-free top-level structure is the issuer's behavior under ASM-PROV-06; the circuit performs no search and no duplicate scan. - REQ-PLAT-22 (upholds SP-FRESH-01): - The Platform Verifier MUST reject a proof whose signed `exp` places + The LOPPV MUST reject a proof whose signed `exp` places `proofValidUntil` at or before Block Time. The signing key is fetched from Google's JWKS endpoint as witness input. - REQ-PLAT-23 (upholds SP-CLIENT-01): - The Platform Verifier MUST reject a proof whose RSA modulus is absent from - the Platform Verifier's active trusted Google modulus set. + The LOPPV MUST reject a proof whose RSA modulus is absent from + the LOPPV's active trusted Google modulus set. - REQ-PLAT-24: The Verifier Governance Process MUST add a newly published Google signing modulus to the trusted set before Google signs with it in production. @@ -346,18 +368,19 @@ Launch fixes X's `/2/oauth2/token` and `/2/users/me` sessions and GitHub's launch profile, because the prover holds the session keys and prover-egress collusion could inject authenticated server-direction records. - REQ-PLAT-27 (upholds SP-EXCHANGE-01): - The Canonical Runtime MUST NOT let an application, user, request, browser + The Ceremony Client MUST NOT let an application, user, request, browser 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 Ceremony Client 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 Ceremony Client 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. - REQ-PLAT-28A (upholds SP-DELIVERY-01): - The Canonical Runtime MUST match the redirect's `state` to exactly one live + The Ceremony Client MUST match the redirect's `state` to exactly one live local ceremony and consume it once before starting the token request. No server-side state or prepare request participates in this lookup. Necessity: the redirect is the only point where the ceremony that requested @@ -367,11 +390,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. @@ -404,7 +433,7 @@ sessions. - REQ-PLAT-29 (upholds SP-EXCHANGE-01): The Implementation MUST reveal the token request's `code` range. The - Canonical Runtime MUST require that revealed serialized value to equal the + Ceremony Client MUST require that revealed serialized value to equal the canonical form serialization of the code consumed at redirect ingress, byte for byte, under common REQ-COMMON-07. - REQ-PLAT-30 (upholds SP-BIND-01): @@ -421,20 +450,20 @@ attestation format: | Range | Revealed | Why | |---|---|---| -| request method and path | yes | the Platform Verifier compares them with its profile constants | -| endpoint authority | not a range | the Notary Service authenticated the TLS server identity, and the Platform Verifier compares the attested authority against its pinned constant per common REQ-COMMON-21A | -| `grant_type` | yes | constant `authorization_code`; the Platform Verifier compares it byte for byte per REQ-PLAT-56 | -| `client_id` | yes | the Platform Verifier reads and returns it | +| request method and path | yes | the LOPPV compares them with its profile constants | +| endpoint authority | not a range | the Notary Service authenticated the TLS server identity, and the LOPPV compares the attested authority against its pinned constant per common REQ-COMMON-21A | +| `grant_type` | yes | constant `authorization_code`; the LOPPV compares it byte for byte per REQ-PLAT-56 | +| `client_id` | yes | the LOPPV 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 | +| `redirect_uri` | yes | the Ceremony Client compares its immutable profile; no ledger or circuit value | +| `code_verifier` | yes | the LOPPV 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 | | everything else | no | headers, `scope`, `token_type`, other response fields | Neither the authority nor the attestation timestamp is a transcript range. -The authority reaches the Platform Verifier as +The authority reaches the LOPPV as the TLS server identity the Notary Service authenticated under common REQ-COMMON-21, carried in the attested data: the transcript holds the authority only in a `Host` header this table hides, and a revealed @@ -447,14 +476,14 @@ 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): The Implementation MUST reveal the `client_id` range of the token request in the notarized session. - REQ-PLAT-29B (upholds SP-CLIENT-01): - The Platform Verifier MUST read the client identifier from that revealed + The LOPPV MUST read the client identifier from that revealed range and return its exact bytes. The Proving Circuit MUST NOT expose a client identifier public input. Necessity: the attestation already authenticates those bytes, so a circuit copy would be a second @@ -462,22 +491,22 @@ dependency. - REQ-PLAT-29C (upholds SP-EXCHANGE-01): The Implementation MUST reveal the token request's `grant_type` and `redirect_uri` ranges in the notarized session, including in the - attestation the Platform Verifier checks. The Canonical Runtime MUST + attestation the LOPPV checks. The Ceremony Client MUST reject a transcript whose revealed serialized `grant_type` or `redirect_uri` value differs from the canonical form serialization of its immutable deployment-profile value. Neither value is a circuit - constraint or a public proof input, and neither is a value the Consumer - reads; the Platform Verifier compares the revealed `grant_type` itself + constraint or a public proof input, and neither is a value the Identity Integration + reads; the LOPPV compares the revealed `grant_type` itself under REQ-PLAT-56. Revealing them narrows the body a prover can compose without being observed; it does not by itself exclude a duplicate field, which remains ASM-PROV-07. The - Platform Verifier enforces the disclosure: an attestation hiding either + LOPPV enforces the disclosure: an attestation hiding either range does not match the profile layout of common REQ-COMMON-17A and REQ-COMMON-18A and fails verification. - REQ-PLAT-56 (upholds SP-EXCHANGE-01): - The Platform Verifier MUST reject an X token attestation whose revealed + The LOPPV MUST reject an X token attestation whose revealed `grant_type` differs from the exact ASCII bytes `authorization_code`. - Necessity: the Canonical Runtime's comparison under REQ-PLAT-29C runs in + Necessity: the Ceremony Client's comparison under REQ-PLAT-29C runs in software the prover chooses whether to run, and `grant_type` is the one revealed field that changes what X did with the request. A body sending `grant_type=refresh_token` while still carrying a `code`, a `redirect_uri`, @@ -485,7 +514,7 @@ dependency. the fields that grant does not use, returns a fresh bearer, and every revealed range still checks out, so an application holding a user's refresh token could mint identity proofs at arbitrary addresses indefinitely from a - single consent. The check is one byte comparison on the Consumer Chain and + single consent. The check is one byte comparison on the Execution Ledger and adds nothing to the Proving Circuit. - REQ-PLAT-30A (upholds SP-EXCHANGE-01): The Implementation MUST commit the returned `access_token` range of the @@ -495,7 +524,7 @@ dependency. - REQ-PLAT-57 (upholds SP-EXCHANGE-01): The Implementation 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 an X token + immediately following it. The LOPPV MUST reject an X token attestation whose committed range is not framed by exactly those revealed bytes. Necessity: common REQ-COMMON-18A wants a revealed anchor on every hidden range, and a received direction revealing nothing at all leaves the @@ -514,7 +543,7 @@ request byte is revealed: | Range | Revealed | Why | |---|---|---| -| request line and all request headers, except the bearer value | yes | the Platform Verifier runs the line-anchored uniqueness scan of common REQ-COMMON-39 over these bytes and frames the committed range per common REQ-COMMON-40 | +| request line and all request headers, except the bearer value | yes | the LOPPV runs the line-anchored uniqueness scan of common REQ-COMMON-39 over these bytes and frames the committed range per common REQ-COMMON-40 | | bearer value of the `authorization` header | committed | a blinded commitment, opened only in circuit | The two ranges account for the request's signed transcript length exactly, @@ -526,12 +555,12 @@ byte stays behind a range commitment of the pinned attestation format: | Range | Revealed | Why | |---|---|---| -| `"id":"`, the `data.id` value, and its closing quote | yes | the Platform Verifier extracts the canonical `userId` from these bytes per REQ-PLAT-31 | -| `"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 | +| `"id":"`, the `data.id` value, and its closing quote | yes | the LOPPV extracts the canonical `userId` from these bytes per REQ-PLAT-31 | +| `"username":"`, the `data.username` value, and its closing quote | yes | the LOPPV 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 LOPPV +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 @@ -545,13 +574,13 @@ REQ-COMMON-18A requires. revealed response bytes, and a session revealing no response range at all leaves it nothing to read. - REQ-PLAT-31 (upholds SP-BIND-01): - The Platform Verifier MUST extract `id` and `username` from the revealed + The LOPPV MUST extract `id` and `username` from the revealed 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. Necessity: the response carries account-holder-influenced text, such as the display name, that can embed a lookalike field. - REQ-PLAT-31A (upholds SP-BIND-01): - The Canonical Runtime MUST derive the X `userId` and normalized handle from + The Ceremony Client MUST derive the X `userId` and normalized handle from those same revealed `id` and `username` bytes, by the same algorithm REQ-PLAT-31 fixes. That derivation is the repeat common REQ-COMMON-19E permits, and the extraction of REQ-PLAT-31 is the authoritative one. The @@ -577,14 +606,14 @@ REQ-COMMON-18A requires. | Public input | Meaning | |---|---| - | token bearer commitment | the commitment the Platform Verifier matches against the verified token attestation | - | identity bearer commitment | the commitment the Platform Verifier matches against the verified `/users/me` attestation | + | token bearer commitment | the commitment the LOPPV matches against the verified token attestation | + | identity bearer commitment | the commitment the LOPPV matches against the verified `/users/me` attestation | The Proving Circuit MUST keep the bearer private. The Proving Circuit MUST NOT add an Authorization Digest, client identifier, timestamp, endpoint, `userId`, or handle public input. - REQ-PLAT-32C (upholds SP-EXCHANGE-01): - The Platform Verifier MUST require each bearer commitment public input to + The LOPPV MUST require each bearer commitment public input to equal the corresponding commitment in the attestation it verified. Necessity: without this the circuit could prove a link between two attestations other than the @@ -592,7 +621,7 @@ REQ-COMMON-18A requires. The circuit proves exactly one thing: one hidden bearer opens both attestations' blinded commitments. Everything else is checked where it can be -seen — the Platform Verifier binds the Authorization Digest by recomputing +seen — the LOPPV binds the Authorization Digest by recomputing the verifier under common REQ-COMMON-15A, reads the client identifier, evidence timestamp, request method, path, and identity fields from revealed attestation bytes, and takes each session's authority from the TLS server @@ -601,19 +630,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 Ceremony Client 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 Ceremony Client 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 @@ -630,7 +672,7 @@ that session. It produces an attestation, not a proof. | 6 | `code_challenge_method` | `S256` | - REQ-PLAT-34: - The Canonical Runtime MUST request exactly `read:user`. Necessity: GitHub + The Ceremony Client MUST request exactly `read:user`. Necessity: GitHub inherits previously granted scopes for the same OAuth application, so an omitted scope does not yield a known grant. @@ -665,7 +707,7 @@ revealed `client_id` something other than the credential GitHub authenticated. The Implementation MUST reveal the `client_id` range of the exchange request in the notarized session. - REQ-PLAT-35C (upholds SP-CLIENT-01): - The Platform Verifier MUST read the client identifier from that revealed + The LOPPV MUST read the client identifier from that revealed range and return its exact bytes. The Proving Circuit MUST NOT expose a client identifier public input. Necessity: the attestation already authenticates those bytes, so a circuit copy would be a second @@ -677,48 +719,43 @@ revealed `client_id` something other than the credential GitHub authenticated. line-feed exclusion of common REQ-COMMON-37 applies to this range, because the `/user` session sends it inside a header. Necessity: `token_type` and the granted `scope` are response schema, and nothing on - the Consumer Chain acts on them; the Canonical Runtime MAY check them + the Execution Ledger acts on them; the Ceremony Client 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 Ceremony Client 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 @@ -726,43 +763,38 @@ interface TokenExchangeResponseV1 { attestation and the bearer can neither derive the blinder nor build the GitHub proof without it. - 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, + The Ceremony Client MUST treat `bearerOpening` as private witness + material for the Proving Circuit. The Ceremony Client MUST NOT place + `bearerOpening` in an OAuth Proof. The Ceremony Client 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,20 +802,20 @@ 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. +OAuth Proof and every published artifact. | Range | Revealed | Why | |---|---|---| -| `client_id` | yes | the Platform Verifier reads and returns it; the 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 | +| `client_id` | yes | the LOPPV reads and returns it; the Ceremony Client checks its profile | +| `code` | yes | the Ceremony Client compares it to the code it consumed | +| `redirect_uri` | yes | the Ceremony Client compares its immutable profile | +| `code_verifier` | yes | the LOPPV 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 | -| token endpoint authority | not a range | the Notary Service authenticated the TLS server identity, and the Platform Verifier compares the attested authority against its pinned constant per common REQ-COMMON-21A | -| token request method | yes | the Platform Verifier checks its profile method | -| token request path | yes | the Platform Verifier checks its profile path | +| token endpoint authority | not a range | the Notary Service authenticated the TLS server identity, and the LOPPV compares the attested authority against its pinned constant per common REQ-COMMON-21A | +| token request method | yes | the LOPPV checks its profile method | +| token request path | yes | the LOPPV checks its profile path | | `client_secret` | no | never revealed, per REQ-PLAT-35A | | everything else | no | headers, status line, `scope`, `token_type`, other response fields | @@ -792,7 +824,7 @@ commitment. The delimiter row is what anchors the committed bearer range in the received direction, which would otherwise carry no revealed byte and leave that range indistinguishable from a `refresh_token` value. Neither the authority nor the attestation timestamp is a transcript range at all. The -authority reaches the Platform Verifier +authority reaches the LOPPV as the TLS server identity the Notary Service authenticated under common REQ-COMMON-21, carried in the attested data, because the transcript holds the authority only in a `Host` header this table hides and a @@ -802,13 +834,13 @@ 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 + immediately following it. The LOPPV MUST reject a token-exchange attestation whose committed range is not framed by exactly those revealed bytes. Necessity: the exchange response reveals no other byte, so without this anchor nothing distinguishes the committed range from @@ -821,42 +853,42 @@ response header. Revealing more would widen exposure without adding a check. the bearer and are what ties the circuit to the two verified attestations. - 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 + The Ceremony Client MUST verify the returned token-exchange 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 Execution Ledger 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 + path. The LOPPV MUST compare those two revealed values with the + GitHub profile. The LOPPV 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. - REQ-PLAT-46 (upholds SP-EXCHANGE-01): - The Canonical Runtime MUST require the disclosed serialized `code` value to + The Ceremony Client MUST require the disclosed serialized `code` value to equal the canonical form serialization of the code it consumed at redirect ingress, byte for byte. - REQ-PLAT-47 (upholds SP-CLIENT-01): - The Canonical Runtime MUST require the disclosed `client_id` to equal its + The Ceremony Client MUST require the disclosed `client_id` to equal its configured client. Common REQ-COMMON-16B makes those bytes identical before and after form serialization. - REQ-PLAT-48 (upholds SP-BIND-01): - The Canonical Runtime MUST require the disclosed `code_verifier` to equal the + The Ceremony Client MUST require the disclosed `code_verifier` to equal the verifier it derived. Its base64url alphabet is byte-identical under form serialization. - REQ-PLAT-48A (upholds SP-EXCHANGE-01): - The Canonical Runtime MUST require the disclosed serialized `redirect_uri` + The Ceremony Client MUST require the disclosed serialized `redirect_uri` value to equal the canonical form serialization of its immutable deployment-profile value. - REQ-PLAT-49 (upholds SP-EXCHANGE-01): - The Canonical Runtime MUST require the returned bearer, under the returned + The Ceremony Client MUST require the returned bearer, under the returned `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 Ceremony Client 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 +896,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. @@ -883,7 +915,7 @@ request byte is revealed: | Range | Revealed | Why | |---|---|---| -| request line and all request headers, except the bearer value | yes | the Platform Verifier runs the line-anchored uniqueness scan of common REQ-COMMON-39 over these bytes and frames the committed range per common REQ-COMMON-40 | +| request line and all request headers, except the bearer value | yes | the LOPPV runs the line-anchored uniqueness scan of common REQ-COMMON-39 over these bytes and frames the committed range per common REQ-COMMON-40 | | bearer value of the `authorization` header | committed | a blinded commitment, opened only in circuit | The two ranges account for the request's signed transcript length exactly, @@ -895,12 +927,12 @@ byte stays behind a range commitment of the pinned attestation format: | Range | Revealed | Why | |---|---|---| -| `"id":`, the `id` integer token, and the structural byte after it, which is `,` or `}` | yes | the Platform Verifier extracts the canonical `userId` from these bytes per REQ-PLAT-51 | -| `"login":"`, the `login` value, and its closing quote | yes | the Platform Verifier extracts the raw handle bytes from these bytes per REQ-PLAT-51 | +| `"id":`, the `id` integer token, and the structural byte after it, which is `,` or `}` | yes | the LOPPV extracts the canonical `userId` from these bytes per REQ-PLAT-51 | +| `"login":"`, the `login` value, and its closing quote | yes | the LOPPV 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 LOPPV +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 @@ -915,18 +947,18 @@ REQ-COMMON-18A requires. revealed response bytes, and a session revealing no response range at all leaves it nothing to read. - REQ-PLAT-51 (upholds SP-BIND-01): - The Platform Verifier MUST extract `id` and `login` from the revealed + The LOPPV MUST extract `id` and `login` from the revealed 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 + REQ-COMMON-19A. The LOPPV MUST reject a noncanonical `id` + 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 + `,` or `}` and no other byte. The LOPPV MUST reject any other following byte. Necessity: the terminator is what proves the revealed digits are the whole number rather than a prefix of a longer one, and JSON member order does not guarantee which of the two closes it. - REQ-PLAT-51A (upholds SP-BIND-01): - The Canonical Runtime MUST derive the GitHub `userId` and normalized handle + The Ceremony Client MUST derive the GitHub `userId` and normalized handle from those same revealed `id` and `login` bytes, by the same algorithm REQ-PLAT-51 fixes. That derivation is the repeat common REQ-COMMON-19E permits, and the extraction of REQ-PLAT-51 is the authoritative one. The @@ -935,40 +967,40 @@ 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 LOPPV 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: | Public input | Meaning | |---|---| - | token-exchange bearer commitment | the commitment the Platform Verifier matches against the verified token-exchange attestation | - | identity bearer commitment | the commitment the Platform Verifier matches against the verified `/user` attestation | + | token-exchange bearer commitment | the commitment the LOPPV matches against the verified token-exchange attestation | + | identity bearer commitment | the commitment the LOPPV matches against the verified `/user` attestation | The Proving Circuit MUST NOT add an Authorization Digest, client identifier, timestamp, endpoint, `userId`, or handle public input. Necessity: the digest - is bound by the Platform Verifier under common REQ-COMMON-15A, and the rest + is bound by the LOPPV under common REQ-COMMON-15A, and the rest are revealed bytes it reads directly, so a circuit copy would be a second representation of a fact that already has one. - REQ-PLAT-52B (upholds SP-EXCHANGE-01): - The Platform Verifier MUST require each bearer commitment public input to + The LOPPV MUST require each bearer commitment public input to equal the corresponding commitment in the attestation it verified. Necessity: without 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 +range, and the Ceremony Client's local reading of `scope` and `token_type` +binds nothing on the Execution Ledger. 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 Ceremony + Client 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,14 +1013,14 @@ 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, -Platform Verifier, Notary Service, Consumer. +Roles: Ceremony Client, GitHub Token Service, Proving Circuit, +LOPPV, Notary Service, Identity Integration. - TEST-PLAT-01 (exercises REQ-PLAT-10, REQ-PLAT-18): The §3.1 nonce vector reproduces exactly, and a token carrying another nonce @@ -998,8 +1030,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 +1049,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 LOPPV. An OAuth Proof 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. @@ -1037,16 +1070,16 @@ Platform Verifier, Notary Service, Consumer. - TEST-PLAT-09A (exercises REQ-PLAT-29A, REQ-PLAT-29B): An X attestation that does not reveal the token request's `client_id` is rejected; a proof exposing a client identifier public input is rejected; and - the identifier the Platform Verifier returns equals the revealed bytes. + the identifier the LOPPV returns equals the revealed bytes. - TEST-PLAT-09B (exercises REQ-PLAT-30A, REQ-PLAT-32A): An X transcript that reveals plaintext `access_token` bytes in either session, or omits the bearer hash commitment, is rejected. - TEST-PLAT-09C (exercises REQ-PLAT-29C, REQ-PLAT-56): - The Platform Verifier rejects an X attestation that hides the `grant_type` - or `redirect_uri` range, and the Canonical Runtime rejects a revealed value + The LOPPV rejects an X attestation that hides the `grant_type` + or `redirect_uri` range, and the Ceremony Client rejects a revealed value differing from the canonical form serialization of its deployment profile. A redirect URI containing `:` and `/` passes in that encoded form and fails - as literal unencoded bytes. The Platform Verifier rejects an + as literal unencoded bytes. The LOPPV rejects an attestation whose revealed `grant_type` is `refresh_token`, and one whose `grant_type` differs from `authorization_code` in any byte, even when every other revealed range and the proof itself check out. @@ -1057,7 +1090,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 +1101,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 +1132,31 @@ 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 ledgers using different conforming + verifier artifacts. A destination ledger 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 OAuth Proof. Only the Identity + Integration's acceptance of that exact OAuth Proof 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 Ceremony Client + rejects the extra representation; without it, the Ceremony Client and the + LOPPV 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 OAuth Proof. - 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 + OAuth Proof, log, or published artifact contains the opening. Verification: + inspection of the OAuth Proof 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 Ceremony Client performs no such check, so an OAuth Proof +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