Skip to main content

libid_profiles/
profiles.rs

1// Generated by scripts/regen-ceremony-profiles.py. Do not edit.
2// The source of truth is solidity/contracts/ceremony/profiles.json.
3
4//! The single source of truth for the ceremony profiles. Solidity, Rust and
5//! TypeScript constants are generated from this file by
6//! scripts/regen-ceremony-profiles.py. The Platform Verifiers are hand
7//! written and READ these constants; only the values live here.
8//!
9//! THESE STRINGS ARE OURS, NOT THE SPECIFICATION'S. ceremony-common fixes no
10//! literal of its own: platform-ceremonies REQ-PLAT-01 fixes the profile
11//! NAMES, and every byte below is the profile author's choice. So this is a
12//! cross-implementation agreement, and it is agreed here rather than four
13//! times over.
14//!
15//! Four components must produce the same bytes: this repository's verifiers,
16//! the browser that notarizes three of the four sessions, the deployment that
17//! notarizes the fourth, and the notary that signs what they observed. A
18//! disagreement is silent -- a Consumer dispatching on one string and a
19//! verifier registered under another simply never meet, and a request line a
20//! prover composes one byte differently is an attestation rejected with no
21//! error that says why.
22//!
23//! Changing a value here changes what a deployed verifier accepts. That is a
24//! new ceremonyVersion, not an edit: --check compares every shipped profile
25//! against the last release and refuses a changed profile that kept its
26//! version.
27
28/// Which shape a platform's immutable identifier takes in its response.
29///
30/// The bare-integer form takes its structural terminator with it, which is
31/// what proves the revealed digits are the whole number rather than a prefix
32/// of a longer one (REQ-PLAT-51).
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum IdShape {
35    /// `"id":"2244994945"`.
36    JsonString,
37    /// `"id":583231,`
38    JsonInteger,
39}
40
41/// One notarized session: which server, and which request.
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub struct Session {
44    /// The lowercase TLS server name the notary authenticates, with no
45    /// trailing dot. `authorityId` is its keccak256, which the verifier
46    /// compares against the constant its profile pins (REQ-COMMON-21A). It is
47    /// also the `Host` header the request carries.
48    pub authority: &'static str,
49    pub method: &'static str,
50    pub path: &'static str,
51    /// The request-line prefix the verifier compares byte for byte, trailing
52    /// space included. The space is what stops `GET /user ` prefixing
53    /// `GET /users/me`.
54    pub request_line: &'static str,
55}
56
57/// The token session: the OAuth exchange.
58#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub struct TokenSession {
60    pub session: Session,
61    /// The body field committed rather than revealed, ordered last so the
62    /// committed run is a suffix (REQ-COMMON-22). `None` for a public client,
63    /// whose request hides nothing and is revealed whole.
64    pub secret_field: Option<&'static str>,
65}
66
67/// The identity session: the authenticated read that names the account.
68#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub struct IdentitySession {
70    pub session: Session,
71    pub id_field: &'static str,
72    pub id_shape: IdShape,
73    pub handle_field: &'static str,
74}
75
76/// One platform's ceremony profile at one Platform Ceremony Version.
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct Profile {
79    /// The platform name. `platformId` is its keccak256.
80    pub platform: &'static str,
81    /// A profile is the pair, not the name (REQ-PLAT-01).
82    pub ceremony_version: u16,
83    /// `None` where the profile notarizes nothing.
84    pub token: Option<TokenSession>,
85    pub identity: Option<IdentitySession>,
86}
87
88impl Profile {
89    /// How many attestations a submission for this profile carries. Derived
90    /// from the sessions rather than stated beside them (REQ-COMMON-41).
91    pub const fn attestation_count(&self) -> u8 {
92        self.token.is_some() as u8 + self.identity.is_some() as u8
93    }
94}
95
96/// Google's evidence is a signed token checked against Google's published
97/// keys, so the profile notarizes nothing: no session, no attestation,
98/// and no Notary Fee.
99pub const GOOGLE: Profile = Profile {
100    platform: "google",
101    ceremony_version: 1,
102    token: None,
103    identity: None,
104};
105
106/// A public client with S256 PKCE and two browser-owned sessions, both
107/// served by the same host.
108pub const X: Profile = Profile {
109    platform: "x",
110    ceremony_version: 1,
111    token: Some(TokenSession {
112        session: Session {
113            authority: "api.x.com",
114            method: "POST",
115            path: "/2/oauth2/token",
116            request_line: "POST /2/oauth2/token ",
117        },
118        secret_field: None,
119    }),
120    identity: Some(IdentitySession {
121        session: Session {
122            authority: "api.x.com",
123            method: "GET",
124            path: "/2/users/me",
125            request_line: "GET /2/users/me ",
126        },
127        id_field: "id",
128        id_shape: IdShape::JsonString,
129        handle_field: "username",
130    }),
131};
132
133/// A confidential client. The exchange runs in the deployment because the
134/// secret cannot reach the browser, and the two sessions are served by
135/// DIFFERENT hosts -- which is why one pinned authority per profile would
136/// be wrong.
137pub const GITHUB: Profile = Profile {
138    platform: "github",
139    ceremony_version: 1,
140    token: Some(TokenSession {
141        session: Session {
142            authority: "github.com",
143            method: "POST",
144            path: "/login/oauth/access_token",
145            request_line: "POST /login/oauth/access_token ",
146        },
147        secret_field: Some("client_secret"),
148    }),
149    identity: Some(IdentitySession {
150        session: Session {
151            authority: "api.github.com",
152            method: "GET",
153            path: "/user",
154            request_line: "GET /user ",
155        },
156        id_field: "id",
157        id_shape: IdShape::JsonInteger,
158        handle_field: "login",
159    }),
160};
161
162/// The closed launch list. A platform outside it has no profile, and
163/// `CeremonyProfile.attestationCount` reverts on one.
164pub const LAUNCH: &[&Profile] = &[&GOOGLE, &X, &GITHUB];
165
166/// The launch profile for a platform name, or nothing.
167///
168/// Nothing, rather than a default: a caller that cannot name the platform has
169/// nothing to notarize, and guessing produces evidence no verifier accepts.
170pub fn launch(platform: &str) -> Option<&'static Profile> {
171    LAUNCH.iter().copied().find(|p| p.platform == platform)
172}
173
174/// Governance-owned launch parameters, in seconds.
175pub const MAX_FUTURE_ATTESTATION_SKEW_SECONDS: u64 = 300;
176pub const PROOF_LIFETIME_SECONDS_X: u64 = 3600;
177pub const PROOF_LIFETIME_SECONDS_GITHUB: u64 = 3600;