From 5666f123a24ae7a33cd25cc57dc7b8e11a98c0db Mon Sep 17 00:00:00 2001 From: SupremaLex Date: Thu, 17 Sep 2026 19:37:59 +0300 Subject: [PATCH] refactor(ceremony): the token request commits nothing, as a rule `_tokenBody` requires one revealed run and no commitment, in the base verifier, for every profile. The virtual `_tokenSentCommitments` and its two overrides go, together with the profile field that fed them: `secretField` leaves profiles.json, and the generator stops emitting `*_TOKEN_SENT_COMMITMENTS`, `secret_field` and `secretField` in Solidity, Rust and TypeScript. The body's length now comes from the revealed run, which is the whole request. Every launch profile answered zero, so no verifier accepts anything it did not accept before, and none refuses anything it accepted. The count moves from a per-profile answer to a fixed rule because both profiles state one shape and the specification has no committed body field left to order. `TokenSession` in the published libid-profiles crate loses `secret_field`. That ships in the same release as the whole-range reveal, so libid-rs takes both in one bump. Assisted-by: Claude Fable 5.1 Signed-off-by: SupremaLex --- rust/profiles/src/profiles.rs | 6 ---- scripts/regen-ceremony-profiles.py | 26 --------------- .../contracts/ceremony/CeremonyProfile.sol | 7 ---- .../ceremony/GitHubPlatformVerifier.sol | 6 ---- .../ceremony/TlsNotaryVerifierBase.sol | 33 ++++++------------- .../contracts/ceremony/XPlatformVerifier.sol | 6 ---- solidity/contracts/ceremony/profiles.json | 2 -- .../contracts/src/ceremony/profiles.ts | 4 --- 8 files changed, 10 insertions(+), 80 deletions(-) diff --git a/rust/profiles/src/profiles.rs b/rust/profiles/src/profiles.rs index 3d240f1c..2d2ccda6 100644 --- a/rust/profiles/src/profiles.rs +++ b/rust/profiles/src/profiles.rs @@ -56,10 +56,6 @@ pub struct Session { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct TokenSession { pub session: Session, - /// The body field committed rather than revealed, ordered last so the - /// committed run is a suffix (REQ-COMMON-22). `None` for a public client, - /// whose request hides nothing and is revealed whole. - pub secret_field: Option<&'static str>, /// The header lines a Platform Verifier requires, each exactly once with /// its value: `host` and `content-type`, lowercased as the wire spells /// them. Every other header is the runtime's own, save the names @@ -120,7 +116,6 @@ pub const X: Profile = Profile { path: "/2/oauth2/token", request_line: "POST /2/oauth2/token ", }, - secret_field: None, required_headers: &[ "host: api.x.com", "content-type: application/x-www-form-urlencoded", @@ -153,7 +148,6 @@ pub const GITHUB: Profile = Profile { path: "/login/oauth/access_token", request_line: "POST /login/oauth/access_token ", }, - secret_field: None, required_headers: &[ "host: github.com", "content-type: application/x-www-form-urlencoded", diff --git a/scripts/regen-ceremony-profiles.py b/scripts/regen-ceremony-profiles.py index 6357a21f..8d6b447c 100755 --- a/scripts/regen-ceremony-profiles.py +++ b/scripts/regen-ceremony-profiles.py @@ -231,8 +231,6 @@ def validate(spec: dict[str, Any]) -> None: safe(session["path"], "path") if name == "token": required_headers(session) - if session["secretField"] is not None: - safe(session["secretField"], "secretField") if name == "identity": safe(session["idField"], "idField") safe(session["handleField"], "handleField") @@ -353,22 +351,6 @@ def gen_sol(spec: dict[str, Any]) -> str: f' bytes internal constant FORBIDDEN_REQUEST_HEADERS = "{escaped(crlf(forbidden_headers(spec)))}";' ) - lines += [ - "", - " /// @dev How many committed ranges the token request carries. A confidential", - " /// client commits its secret and a public client hides nothing, so this", - " /// is one or zero and never a preference.", - "", - ] - for profile in profiles: - token = profile["sessions"].get("token") - if token is None: - continue - count = 0 if token["secretField"] is None else 1 - lines.append( - f" uint256 internal constant {upper(profile['platform'])}_TOKEN_SENT_COMMITMENTS = {count};" - ) - lines += [ "", " /// @dev The two identity members a Platform Verifier reads out of the", @@ -539,10 +521,6 @@ def gen_rust(spec: dict[str, Any]) -> str: "#[derive(Clone, Copy, Debug, PartialEq, Eq)]", "pub struct TokenSession {", " pub session: Session,", - " /// The body field committed rather than revealed, ordered last so the", - " /// committed run is a suffix (REQ-COMMON-22). `None` for a public client,", - " /// whose request hides nothing and is revealed whole.", - " pub secret_field: Option<&'static str>,", " /// The header lines a Platform Verifier requires, each exactly once with", " /// its value: `host` and `content-type`, lowercased as the wire spells", " /// them. Every other header is the runtime's own, save the names", @@ -596,7 +574,6 @@ def gen_rust(spec: dict[str, Any]) -> str: else: lines.append(" token: Some(TokenSession {") lines += rust_session(token, 8) - lines.append(f" secret_field: {rust_str(token['secretField'])},") lines += rust_array("required_headers: ", required_headers(token), " ", ",") lines.append(" }),") @@ -720,8 +697,6 @@ def gen_ts(spec: dict[str, Any]) -> str: "", "export interface TokenSession {", " readonly session: Session", - " /** The body field committed rather than revealed, or null. */", - " readonly secretField: string | null", " /** The header lines a Platform Verifier requires, each exactly once with", " * its value: `host` and `content-type`. Every other header is the", " * runtime's own, save the names `FORBIDDEN_REQUEST_HEADERS` lists.", @@ -759,7 +734,6 @@ def gen_ts(spec: dict[str, Any]) -> str: else: lines.append(" token: {") lines += ts_session(token, 4) - lines.append(f" secretField: {ts_str(token['secretField'])},") lines += ts_array("requiredHeaders: ", required_headers(token), " ", ",") lines.append(" },") diff --git a/solidity/contracts/ceremony/CeremonyProfile.sol b/solidity/contracts/ceremony/CeremonyProfile.sol index da6e8236..abaa97c9 100644 --- a/solidity/contracts/ceremony/CeremonyProfile.sol +++ b/solidity/contracts/ceremony/CeremonyProfile.sol @@ -87,13 +87,6 @@ library CeremonyProfile { bytes internal constant FORBIDDEN_REQUEST_HEADERS = "authorization\r\ncontent-encoding\r\ncookie\r\ntransfer-encoding\r\nx-http-method\r\nx-http-method-override\r\nx-method-override"; - /// @dev How many committed ranges the token request carries. A confidential - /// client commits its secret and a public client hides nothing, so this - /// is one or zero and never a preference. - - uint256 internal constant X_TOKEN_SENT_COMMITMENTS = 0; - uint256 internal constant GITHUB_TOKEN_SENT_COMMITMENTS = 0; - /// @dev The two identity members a Platform Verifier reads out of the /// revealed response bytes (REQ-PLAT-51). diff --git a/solidity/contracts/ceremony/GitHubPlatformVerifier.sol b/solidity/contracts/ceremony/GitHubPlatformVerifier.sol index e5bbbd6d..0eb379c1 100644 --- a/solidity/contracts/ceremony/GitHubPlatformVerifier.sol +++ b/solidity/contracts/ceremony/GitHubPlatformVerifier.sol @@ -95,12 +95,6 @@ contract GitHubPlatformVerifier is TlsNotaryVerifierBase { return CeremonyProfile.AUTHORITY_GITHUB_API; } - /// @dev GitHub's exchange hides no body field, so its request is revealed - /// whole and this direction carries no commitment at all. - function _tokenSentCommitments() internal pure override returns (uint256) { - return CeremonyProfile.GITHUB_TOKEN_SENT_COMMITMENTS; - } - function _tokenRequestLine() internal pure override returns (bytes memory) { return CeremonyProfile.GITHUB_TOKEN_REQUEST_LINE; } diff --git a/solidity/contracts/ceremony/TlsNotaryVerifierBase.sol b/solidity/contracts/ceremony/TlsNotaryVerifierBase.sol index ea844ab0..2c8a9ff3 100644 --- a/solidity/contracts/ceremony/TlsNotaryVerifierBase.sol +++ b/solidity/contracts/ceremony/TlsNotaryVerifierBase.sol @@ -95,7 +95,7 @@ abstract contract TlsNotaryVerifierBase is IPlatformVerifier, PlatformVerifierBa /// @dev The first revealed range does not begin the transcript, so nothing /// says the bytes read as a request line ARE the request line. error RequestLineNotAtOrigin(uint32 start); - /// @dev The token request does not have the exact shape the profile fixes. + /// @dev The token request is not one revealed run with no commitment. error WrongTokenRequestLayout(uint256 revealedRanges, uint256 commitments); /// @dev The head/body separator is missing or ambiguous, so the body cannot /// be located by the framing the server itself parsed. @@ -132,11 +132,6 @@ abstract contract TlsNotaryVerifierBase is IPlatformVerifier, PlatformVerifierBa /// `authorization`, and the framing around the committed value. function _tokenRequiredHeaders() internal pure virtual returns (bytes memory); - /// @dev How many committed ranges the token request carries. Both launch - /// profiles hide no body field, so both answer zero and their - /// requests are revealed whole. - function _tokenSentCommitments() internal pure virtual returns (uint256); - /// @dev Anything the profile checks in the token body beyond the fields /// every profile reads. Default: nothing. function _checkTokenBody(bytes memory body) internal pure virtual {} @@ -332,7 +327,7 @@ abstract contract TlsNotaryVerifierBase is IPlatformVerifier, PlatformVerifierBa // Both stay: REQ-COMMON-21A is about the method and the path, and a // deployment pointed at the wrong endpoint should hear that rather than // that some byte of its request differs. - bytes memory body = _tokenBody(data.sent, data.sentTranscriptLength); + bytes memory body = _tokenBody(data.sent); _checkTokenBody(body); // REQ-COMMON-15A. This is the whole binding between the evidence and @@ -467,10 +462,9 @@ abstract contract TlsNotaryVerifierBase is IPlatformVerifier, PlatformVerifierBa /// real one, and every field below is then read from bytes the /// platform never saw while the platform executed something else. /// - /// So the profile fixes the shape exactly: ONE revealed run beginning - /// at offset 0, and exactly the committed ranges the profile expects. - /// Both launch profiles expect none: the run covers the request - /// through to its signed length, so no body byte is hidden. + /// So the shape is fixed exactly: ONE revealed run beginning at + /// offset 0 and no commitment at all, so the run covers the request + /// through to its signed length and no body byte is hidden. /// /// AND THE HEAD ITSELF, byte for byte. Revealing the headers is not /// checking them: they were public and unconstrained here, while @@ -709,12 +703,8 @@ abstract contract TlsNotaryVerifierBase is IPlatformVerifier, PlatformVerifierBa return a.length == b.length && keccak256(a) == keccak256(b); } - function _tokenBody(CeremonyAttestation.DirectionBlock memory block_, uint32 signedLength) - internal - pure - returns (bytes memory body) - { - if (block_.revealed.length != 1 || block_.commitments.length != _tokenSentCommitments()) { + function _tokenBody(CeremonyAttestation.DirectionBlock memory block_) internal pure returns (bytes memory body) { + if (block_.revealed.length != 1 || block_.commitments.length != 0) { revert WrongTokenRequestLayout(block_.revealed.length, block_.commitments.length); } bytes memory whole = block_.revealed[0].value; @@ -734,12 +724,9 @@ abstract contract TlsNotaryVerifierBase is IPlatformVerifier, PlatformVerifierBa uint256 declared = _checkTokenHead(_slice(whole, 0, at)); at += 4; - // `signedLength` is the whole request and the head is revealed, so the - // remainder is the body. It comes from the signed length rather than - // from the revealed run so that a profile committing part of its body - // is framed by what the notary signed; where a profile commits nothing - // the two are the same number. - if (declared != signedLength - at) revert WrongDeclaredBodyLength(declared, signedLength - at); + // The run is the request whole and the head is revealed, so the + // remainder is the body the platform parsed. + if (declared != whole.length - at) revert WrongDeclaredBodyLength(declared, whole.length - at); body = new bytes(whole.length - at); for (uint256 i = 0; i < body.length; ++i) { diff --git a/solidity/contracts/ceremony/XPlatformVerifier.sol b/solidity/contracts/ceremony/XPlatformVerifier.sol index 6faa1af9..136f3462 100644 --- a/solidity/contracts/ceremony/XPlatformVerifier.sol +++ b/solidity/contracts/ceremony/XPlatformVerifier.sol @@ -75,12 +75,6 @@ contract XPlatformVerifier is TlsNotaryVerifierBase { return CeremonyProfile.AUTHORITY_X_API; } - /// @dev X uses a public client and hides no body field, so its token - /// request is revealed whole. - function _tokenSentCommitments() internal pure override returns (uint256) { - return CeremonyProfile.X_TOKEN_SENT_COMMITMENTS; - } - function _tokenRequestLine() internal pure override returns (bytes memory) { return CeremonyProfile.X_TOKEN_REQUEST_LINE; } diff --git a/solidity/contracts/ceremony/profiles.json b/solidity/contracts/ceremony/profiles.json index 0758f98e..9585df3d 100644 --- a/solidity/contracts/ceremony/profiles.json +++ b/solidity/contracts/ceremony/profiles.json @@ -81,7 +81,6 @@ "authority": "api.x.com", "method": "POST", "path": "/2/oauth2/token", - "secretField": null, "requiredHeaders": [ "host: api.x.com", "content-type: application/x-www-form-urlencoded" @@ -136,7 +135,6 @@ "authority": "github.com", "method": "POST", "path": "/login/oauth/access_token", - "secretField": null, "requiredHeaders": [ "host: github.com", "content-type: application/x-www-form-urlencoded" diff --git a/ts/packages/contracts/src/ceremony/profiles.ts b/ts/packages/contracts/src/ceremony/profiles.ts index 69683d24..f29dc47a 100644 --- a/ts/packages/contracts/src/ceremony/profiles.ts +++ b/ts/packages/contracts/src/ceremony/profiles.ts @@ -39,8 +39,6 @@ export interface Session { export interface TokenSession { readonly session: Session - /** The body field committed rather than revealed, or null. */ - readonly secretField: string | null /** The header lines a Platform Verifier requires, each exactly once with * its value: `host` and `content-type`. Every other header is the * runtime's own, save the names `FORBIDDEN_REQUEST_HEADERS` lists. @@ -89,7 +87,6 @@ export const X: Profile = { path: '/2/oauth2/token', requestLine: 'POST /2/oauth2/token ', }, - secretField: null, requiredHeaders: ['host: api.x.com', 'content-type: application/x-www-form-urlencoded'], }, identity: { @@ -121,7 +118,6 @@ export const GITHUB: Profile = { path: '/login/oauth/access_token', requestLine: 'POST /login/oauth/access_token ', }, - secretField: null, requiredHeaders: ['host: github.com', 'content-type: application/x-www-form-urlencoded'], }, identity: {