Skip to main content

pubhubs/api/
sso.rs

1//! Data structures related to the authentication of users towards hubs.
2//!
3//! # Overview
4//!
5//! ## Pseudonyms
6//!
7//! The pseudonym a user $U$ gets in a hub $H$ is
8//! $$\mathrm{Sha512}(g_H \cdot \mathrm{Id}_U )$$
9//! mapped to a [`CurvePoint`], where:
10//!
11//!  - $\mathrm{Id}_U$ is a permanent unchanging **secret user identifier**
12//!    (a random [`CurvePoint`]).  The secret user identifier is known to no one,
13//!    not even the user itself. PHC and the transcryptor only get to see the ElGamal encrypted
14//!    form known as the _polymorphic pseudonym_ (see below).
15//!
16//!    > **Note:** The secret user identifier should not be confused with the `user_id`
17//!    > used by PHC internally to identify a user.)
18//!
19//!
20//!  - $g_H$ is the **pseudonymisation factor**, a [`scalar`](Scalar) unique
21//!    to the hub $H$, known only by the transcryptor:
22//!    $$g_H := \mathrm{Sha512}(H \Vert \ell_d \Vert d \Vert \ell_g \Vert g)$$
23//!    where $H$ is the [**hub id**](crate::hub::BasicInfo::id) (32 bytes),
24//!    $d := \text{"pubhubs-pseud-factor"}$, $g$ is the transcryptor's
25//!    pseudonymisation-factor secret, and $\ell_d, \ell_g$ are the byte
26//!    lengths of $d, g$ encoded as 8-byte big-endian unsigned integers.
27//!
28//!    <details class="toggle">
29//!    <summary class="hideme"><span>Expand example </span></summary>
30//!    
31//!    **Example**
32//!    ```
33//!    use sha2::Digest; // brings `chain_update` and `new` into scope
34//!    let h = pubhubs::id::Id::from([7u8; 32]);
35//!    let g: &[u8] = b"abc";          // 3 bytes
36//!    let d = "pubhubs-pseud-factor"; // 20 bytes
37//!    assert_eq!(
38//!        pubhubs::phcrypto::pseud_factor_for_hub(g, h),
39//!        curve25519_dalek::Scalar::from_hash(
40//!            sha2::Sha512::new()
41//!                .chain_update(h.as_slice())
42//!                .chain_update([0, 0, 0, 0, 0, 0, 0, 20u8])
43//!                .chain_update(d.as_bytes())
44//!                .chain_update([0, 0, 0, 0, 0, 0, 0, 3u8])
45//!                .chain_update(g),
46//!        ),
47//!    );
48//!    ```
49//!
50//!    </details>
51//!
52//! > **Note:** The white paper uses $g_H\cdot \mathrm{Id}_U$ instead
53//! > of $\mathrm{Sha512}(g_H\cdot \mathrm{Id}_U)$.  The hash has been added to
54//! > protect the pseudonym against harvest-now-decrypt-later-by-a-quantum-computer attacks.
55//!
56//! The SSO flow described below gets the pseudonym
57//! $\mathrm{Sha512}(g_H \cdot \mathrm{Id}_U )$ of a user $U$ to the hub $H$ in such a way that:
58//!  - The hub learns only this pseudonym.
59//!  - PHC learns $U$, but not what hub $H$ they are visiting.
60//!  - The transcryptor learns $H$ (and knows $g_H$), but can not* deduce $U$.
61//!
62//! ## Polymorphic pseudonyms
63//!
64//! A **polymorphic pseudonym** $\mathrm{PP}_U$
65//! for the user $U$ is an [ElGamal encryption](elgamal::Triple) of
66//! $\mathrm{Id}_U$ of the form
67//! $$ \mathrm{PP}_U \ :=\ (rB,\ \mathrm{Id}_U + rxB,\  xB).$$
68//! Here:
69//!  - $B$ denotes the base point used by [`CurvePoint`].
70//!  - $x := x_{\mathrm{T}} x_{\mathrm{PHC}}$ is the  **master encryption key**,
71//!    that splits into two **master encryption key parts**, $x_{\mathrm{T}}$ and $x_{\mathrm{PHC}}$,
72//!    picked by the transcryptor and PHC, respectively.
73//!  - $r$ is a random [`Scalar`].
74//!
75//! While each user has just one $\mathrm{Id}_U$,
76//! it has many different polymorphic pseudonyms on account of the random factor $r$.
77//! The polymorphic pseudonym serves two purposes:
78//!
79//!  1. It 'identifies' the user $U$ (towards the transcryptor) without always
80//!     having the same shape, so it can not be used to track logins of the same user.
81//!  
82//!  2. It allows operations to be performed on $\mathrm{Id}_U$ without revealing $\mathrm{Id}_U$
83//!     itself (via the _homomorphic_ properties of ElGamal encryption, see [`elgamal::Triple::rsk`].)
84//!
85//!
86//!
87//!
88//! ## Flow
89//!
90//!  1. The user first obtains a [`PolymorphicPseudonymPackage`] (**PPP**) from PHC,
91//!     which contains a polymorphic pseudonym nonce (**ppnonce**) and a polymorphic pseudonym.
92//!     The polymorphic pseudonym is the ElGamal encryption of
93
94use crate::api::*;
95
96use serde::{Deserialize, Serialize};
97
98use crate::common::elgamal;
99use crate::misc::jwt;
100
101/// Returned (in sealed form) by [`phc::user::PppEP`], needed for  [`tr::EhppEP`].
102#[derive(Serialize, Deserialize, Debug, Clone)]
103#[serde(deny_unknown_fields)]
104pub struct PolymorphicPseudonymPackage {
105    /// The actual polymorphic pseudonym for the user
106    pub polymorphic_pseudonym: elgamal::Triple,
107
108    pub nonce: phc::user::PpNonce,
109}
110
111having_message_code!(PolymorphicPseudonymPackage, Ppp);
112
113/// Returned (in sealed form) by [`tr::EhppEP`], needed for [`phc::user::HhppEP`].
114#[derive(Serialize, Deserialize, Debug, Clone)]
115#[serde(deny_unknown_fields)]
116pub struct EncryptedHubPseudonymPackage {
117    /// Hub pseudonym `g_H Id_U`, elgamal encrypted for `x_PHC`.
118    pub encrypted_hub_pseudonym: elgamal::Triple,
119
120    /// Nonce, from [`hub::EnterStartEP`]
121    pub hub_nonce: hub::EnterNonce,
122
123    /// Nonce, from [`PolymorphicPseudonymPackage::nonce`]
124    pub phc_nonce: phc::user::PpNonce,
125}
126
127having_message_code!(EncryptedHubPseudonymPackage, Ehpp);
128
129/// Returned (in sealed form) by [`phc::user::HhppEP`], needed for [`hub::EnterCompleteEP`].
130#[derive(Serialize, Deserialize, Debug, Clone)]
131#[serde(deny_unknown_fields)]
132pub struct HashedHubPseudonymPackage {
133    /// The hashed hub pseudonym, hashed to a point on curve25519 so we can decide to add an
134    /// additional layer of ElGamal encryption later on.
135    pub hashed_hub_pseudonym: CurvePoint,
136
137    /// When the original pseudonym was issued
138    pub pp_issued_at: jwt::NumericDate,
139
140    /// Nonce, from [`hub::EnterStartEP`]
141    pub hub_nonce: hub::EnterNonce,
142}
143
144impl Signable for HashedHubPseudonymPackage {
145    const CODE: MessageCode = MessageCode::Hhpp;
146    const CONSTELLATION_BOUND: bool = true;
147}