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;