libid-profiles 0.11.0

The libID ceremony profiles: what each platform's notarized sessions request and reveal, mirroring CeremonyProfile.sol byte for byte.
Documentation
// Generated by scripts/regen-ceremony-profiles.py. Do not edit.
// The source of truth is solidity/contracts/ceremony/profiles.json.

//! The single source of truth for the ceremony profiles. Solidity, Rust and
//! TypeScript constants are generated from this file by
//! scripts/regen-ceremony-profiles.py. The Platform Verifiers are hand
//! written and READ these constants; only the values live here.
//!
//! THESE STRINGS ARE OURS, NOT THE SPECIFICATION'S. ceremony-common fixes no
//! literal of its own: platform-ceremonies REQ-PLAT-01 fixes the profile
//! NAMES, and every byte below is the profile author's choice. So this is a
//! cross-implementation agreement, and it is agreed here rather than four
//! times over.
//!
//! Four components must produce the same bytes: this repository's verifiers,
//! the browser that notarizes three of the four sessions, the deployment that
//! notarizes the fourth, and the notary that signs what they observed. A
//! disagreement is silent -- a Consumer dispatching on one string and a
//! verifier registered under another simply never meet, and a request line a
//! prover composes one byte differently is an attestation rejected with no
//! error that says why.
//!
//! Changing a value here changes what a deployed verifier accepts. That is a
//! new ceremonyVersion, not an edit.

/// Which shape a platform's immutable identifier takes in its response.
///
/// The bare-integer form takes its structural terminator with it, which is
/// what proves the revealed digits are the whole number rather than a prefix
/// of a longer one (REQ-PLAT-51).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IdShape {
    /// `"id":"2244994945"`.
    JsonString,
    /// `"id":583231,`
    JsonInteger,
}

/// One notarized session: which server, and which request.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Session {
    /// The lowercase TLS server name the notary authenticates, with no
    /// trailing dot. `authorityId` is its keccak256, which the verifier
    /// compares against the constant its profile pins (REQ-COMMON-21A). It is
    /// also the `Host` header the request carries.
    pub authority: &'static str,
    pub method: &'static str,
    pub path: &'static str,
    /// The request-line prefix the verifier compares byte for byte, trailing
    /// space included. The space is what stops `GET /user ` prefixing
    /// `GET /users/me`.
    pub request_line: &'static str,
}

/// The token session: the OAuth exchange.
#[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
    /// `FORBIDDEN_REQUEST_HEADERS` lists. `content-length` is absent
    /// because its value is the body's own count: the HTTP client appends
    /// it and the verifier reads it rather than compares it.
    pub required_headers: &'static [&'static str],
}

/// The identity session: the authenticated read that names the account.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct IdentitySession {
    pub session: Session,
    pub id_field: &'static str,
    pub id_shape: IdShape,
    pub handle_field: &'static str,
}

/// One platform's ceremony profile at one Platform Ceremony Version.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Profile {
    /// The platform name. `platformId` is its keccak256.
    pub platform: &'static str,
    /// A profile is the pair, not the name (REQ-PLAT-01).
    pub ceremony_version: u16,
    /// `None` where the profile notarizes nothing.
    pub token: Option<TokenSession>,
    pub identity: Option<IdentitySession>,
}

impl Profile {
    /// How many attestations a submission for this profile carries. Derived
    /// from the sessions rather than stated beside them (REQ-COMMON-41).
    pub const fn attestation_count(&self) -> u8 {
        self.token.is_some() as u8 + self.identity.is_some() as u8
    }
}

/// Google's evidence is a signed token checked against Google's published
/// keys, so the profile notarizes nothing: no session, no attestation,
/// and no Notary Fee.
pub const GOOGLE: Profile = Profile {
    platform: "google",
    ceremony_version: 1,
    token: None,
    identity: None,
};

/// A public client with S256 PKCE and two browser-owned sessions, both
/// served by the same host.
pub const X: Profile = Profile {
    platform: "x",
    ceremony_version: 1,
    token: Some(TokenSession {
        session: Session {
            authority: "api.x.com",
            method: "POST",
            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",
        ],
    }),
    identity: Some(IdentitySession {
        session: Session {
            authority: "api.x.com",
            method: "GET",
            path: "/2/users/me",
            request_line: "GET /2/users/me ",
        },
        id_field: "id",
        id_shape: IdShape::JsonString,
        handle_field: "username",
    }),
};

/// A confidential client. The exchange runs in the deployment because the
/// secret cannot reach the browser, and the two sessions are served by
/// DIFFERENT hosts -- which is why one pinned authority per profile would
/// be wrong.
pub const GITHUB: Profile = Profile {
    platform: "github",
    ceremony_version: 1,
    token: Some(TokenSession {
        session: Session {
            authority: "github.com",
            method: "POST",
            path: "/login/oauth/access_token",
            request_line: "POST /login/oauth/access_token ",
        },
        secret_field: Some("client_secret"),
        required_headers: &[
            "host: github.com",
            "content-type: application/x-www-form-urlencoded",
        ],
    }),
    identity: Some(IdentitySession {
        session: Session {
            authority: "api.github.com",
            method: "GET",
            path: "/user",
            request_line: "GET /user ",
        },
        id_field: "id",
        id_shape: IdShape::JsonInteger,
        handle_field: "login",
    }),
};

/// The closed launch list. A platform outside it has no profile, and
/// `CeremonyProfile.attestationCount` reverts on one.
pub const LAUNCH: &[&Profile] = &[&GOOGLE, &X, &GITHUB];

/// The launch profile for a platform name, or nothing.
///
/// Nothing, rather than a default: a caller that cannot name the platform has
/// nothing to notarize, and guessing produces evidence no verifier accepts.
pub fn launch(platform: &str) -> Option<&'static Profile> {
    LAUNCH.iter().copied().find(|p| p.platform == platform)
}

/// Header names no notarized request may carry, compared by every
/// Platform Verifier with the name lowercased, its whitespace removed and
/// `_` read as `-`. Each changes what the platform does with the request
/// in a way no revealed byte shows: `authorization` which client it
/// authenticates, `content-encoding` and `transfer-encoding` which bytes
/// it parses, `cookie` which session it answers for, the three override
/// names which method it runs. The identity request is excepted from
/// `authorization` alone: its one such header, under any scheme, is what
/// REQ-COMMON-39 counts. On the token request the verifier further
/// requires each session's requiredHeaders, `host` and `content-type`,
/// reads `content-length`, and ignores every other header: one outside
/// both lists changes only what the platform answers, and a wrong answer
/// is a response the verifier cannot read.
pub const FORBIDDEN_REQUEST_HEADERS: &[&str] = &[
    "authorization",
    "content-encoding",
    "cookie",
    "transfer-encoding",
    "x-http-method",
    "x-http-method-override",
    "x-method-override",
];

/// Governance-owned launch parameters, in seconds.
pub const MAX_FUTURE_ATTESTATION_SKEW_SECONDS: u64 = 300;
pub const PROOF_LIFETIME_SECONDS_X: u64 = 3600;
pub const PROOF_LIFETIME_SECONDS_GITHUB: u64 = 3600;