fix(ceremony): derive the PKCE verifier the way ceremony-common does - #24
Merged
Merged
Conversation
The contracts and the merged specification disagreed about section 7 in
two places at once, so a prover built from the specification produced a
verifier this code would never recompute, and no X or GitHub claim could
be made at all.
here SHA256(keccak256("libid.identity.pkce") || digest || pkceNonce)
§7 SHA256(digest || authorizationNonce)
The domain constant is gone, and the second nonce with it. The verifier
is derived under the same `authorizationNonce` the digest already
commits, which is what REQ-COMMON-12 names, and `TlsNotaryProof` loses
its `pkceNonce` field: a caller has no second value left to move, since
changing the nonce moves the digest too.
The specification is the side that defines the boundary. REQ-PLAT-01A
puts the proof statement of a Platform Ceremony Version outside any
verifier artifact, and TEST-PLAT-17 still fixes the launch pairs at
("google", 1), ("x", 1) and ("github", 1). Nothing is deployed under the
old construction, so this makes the artifact conform to `x/v1` and
`github/v1` rather than opening a v2.
The section 7 vector in CeremonyAuthorization.t.sol is the merged one,
transcribed from the specification rather than produced by this library.
The X test that forged the second nonce now forges the revealed verifier
instead, which is the same property once there is one nonce.
Assisted-by: Claude Opus 5
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
xgreenx
marked this pull request as ready for review
September 7, 2026 16:11
SupremaLex
approved these changes
Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The contracts and the merged
ceremony-common§7 built two different PKCEverifiers, so a prover written from the specification produced a
code_verifierTlsNotaryVerifierBasewould never recompute, and every Xand GitHub claim reverted with
CodeVerifierMismatchbefore it could reach aConsumer.
Two differences, not one:
keccak256("libid.identity.pkce") || digest || pkceNonceauthorizationNonceceremony-common§7digest || authorizationNonceThis moves the contracts to the specification, which is what
libid#20 merged.
What changed
CeremonyAuthorization.PKCE_DOMAINis gone;verifierHashisSHA256(authorizationDigest || authorizationNonce), exactly 64 bytes.TlsNotaryProof.pkceNonceis gone. The verifier is recomputed underp.authorizationNonce, so a caller has no second value left to move —changing the nonce moves the digest too (REQ-COMMON-12).
CeremonyAuthorization.t.solpins the merged §7 vector, transcribed fromthe specification:
verifierHash = 0xe6d7810e…c8e65a,code_verifier = 5teBDl6cz4U77aFweV5PbMhBJ_lEFv6LLNKzqnDI5lo,code_challenge = c8HLMaJOzc8OUoRYc7AocL5ioAkXVtAOmoGxoSY60IQ.test_rejectsAForgedPkceNoncebecomestest_rejectsAVerifierDerivedUnderAnotherNonce: it forges the revealedverifier rather than the removed field, which is the same property once
there is one nonce.
libid.identity.pkceas the one literalceremony-commonfixes — it now fixes none — and the Googlelayout-confusion test, whose old comment named
pkceNonceas the word thattrips the decoder. It is now word five, the identity session's offset, read
as a
publicInputslength of 64.No
platformCeremonyVersionbumpREQ-COMMON-12 bumps the version for a change to a published construction.
Here the boundary is not the artifact: REQ-PLAT-01A puts the proof statement
of a Platform Ceremony Version outside any Platform Verifier artifact
("chain artifacts implement the versioned ceremony boundary; they do not
define it"), and TEST-PLAT-17 still fixes the launch pairs at
("google", 1),("x", 1)and("github", 1). Nothing is deployed underthe old construction, so
x/v1is what §7 says and this artifact was simplynot conforming to it. Bumping would open an
x/v2the same merged text doesnot list.
Verification
forge build,forge fmt --check,forge test— 404 passed, 0 failed.specification's own text before it was written into the test.
and
CeremonyProofVerifier— the one ceremony ABI the TS package generates— takes
bytes, so its ABI is unchanged.