Skip to main content

pubhubs/servers/
constellation.rs

1//! Details on the constellation of PubHubs servers
2
3use std::ops::Deref;
4
5use sha2::digest::Digest;
6
7use crate::api;
8use crate::common::elgamal;
9use crate::id;
10use crate::phcrypto;
11use crate::servers;
12
13/// Public details on the constellation of PubHubs servers (stored in the [`inner`] field)
14/// paired with an derived [`id`].  [`Deref`]s to [`Inner`].
15///
16/// # Comparing constellations
17///
18/// [`Constellation`] does not implement [`PartialEq`], because there are two valid ways to compare
19/// constellations `c1` and `c2`, namely `c1.id == c2.id` and `c1.inner == c2.inner`, and it should
20/// be clear in the code which one is being used.
21///
22/// [`id`]: Constellation::id
23/// [`inner`]: Constellation::inner
24#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
25pub struct Constellation {
26    /// Identifier for this constellation derived from [`Inner`] using a hash.
27    pub id: id::Id,
28
29    /// When this constellation was first created by pubhubs central.  When two parties
30    /// have different constellations, the party with the oldest constellation should
31    /// update.
32    pub created_at: api::NumericDate,
33
34    #[serde(flatten)]
35    pub inner: Inner,
36}
37
38impl Deref for Constellation {
39    type Target = Inner;
40
41    fn deref(&self) -> &Inner {
42        &self.inner
43    }
44}
45// NOTE: When adding a new field to the constellation make sure it has a default value in the first
46// version so that when the new version of PHC contacts the old versions of the transcryptor and
47// the authentication server, PHC will not crash on missing fields at the transcryptor and the
48// authentication server.
49//
50// (The converse is not necessary:  when an outdated authentication server and transcryptor
51// are running discovery against a freshly updated PHC, PHC's constellation will not be set
52// and will thus not cause the transcryptor or authentication server to crash.)
53#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
54pub struct Inner {
55    pub transcryptor_url: url::Url,
56    pub transcryptor_jwt_key: api::VerifyingKey,
57    pub transcryptor_enc_key: elgamal::PublicKey,
58    /// `x_T B` - so the transcryptor can check that the correct keypart was used
59    pub transcryptor_master_enc_key_part: elgamal::PublicKey,
60
61    pub phc_url: url::Url,
62    pub phc_jwt_key: api::VerifyingKey,
63    pub phc_enc_key: elgamal::PublicKey,
64
65    pub auths_url: url::Url,
66    pub auths_jwt_key: api::VerifyingKey,
67    pub auths_enc_key: elgamal::PublicKey,
68
69    /// `x_T x_PHC B`
70    pub master_enc_key: elgamal::PublicKey,
71
72    pub global_client_url: url::Url,
73
74    /// pubhubs version
75    pub ph_version: Option<String>,
76}
77
78impl Inner {
79    /// Returns the url of the named server
80    pub fn url(&self, name: servers::Name) -> &url::Url {
81        match name {
82            servers::Name::PubhubsCentral => &self.phc_url,
83            servers::Name::Transcryptor => &self.transcryptor_url,
84            servers::Name::AuthenticationServer => &self.auths_url,
85        }
86    }
87
88    /// Returns a [`sha2::Sha256`] hash of this constellation - used to compute [`Constellation::id`].
89    pub(crate) fn sha256(&self) -> sha2::Sha256 {
90        let Inner {
91            transcryptor_url,
92            transcryptor_jwt_key,
93            transcryptor_enc_key,
94            transcryptor_master_enc_key_part,
95
96            phc_url,
97            phc_jwt_key,
98            phc_enc_key,
99
100            auths_url,
101            auths_jwt_key,
102            auths_enc_key,
103
104            global_client_url,
105
106            master_enc_key,
107            ph_version,
108        } = self;
109
110        // NOTE: it would be easier to serialize self using, say, serde_json, and then hash that,
111        // but it's not evident whether serializing the same constellation twice will give the same
112        // string.
113
114        sha2::Sha256::new()
115            .chain_update(transcryptor_url.as_str())
116            .chain_update(**transcryptor_jwt_key)
117            .chain_update(transcryptor_enc_key)
118            .chain_update(transcryptor_master_enc_key_part)
119            .chain_update(phc_url.as_str())
120            .chain_update(**phc_jwt_key)
121            .chain_update(phc_enc_key)
122            .chain_update(auths_url.as_str())
123            .chain_update(**auths_jwt_key)
124            .chain_update(auths_enc_key)
125            .chain_update(master_enc_key)
126            .chain_update(global_client_url.as_str())
127            .chain_update(ph_version.as_ref().map(String::as_str).unwrap_or("n/a"))
128    }
129
130    pub fn derive_id(&self) -> id::Id {
131        phcrypto::constellation_id(self)
132    }
133}
134
135/// A full [`Constellation`], or just the [`id::Id`].
136#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
137#[serde(untagged)]
138pub enum ConstellationOrId {
139    Constellation(Box<Constellation>),
140    Id { id: id::Id },
141}
142
143impl ConstellationOrId {
144    /// Returns the [`Constellation`], if any.
145    pub fn constellation(&self) -> Option<&Constellation> {
146        if let Self::Constellation(c) = self {
147            return Some(c);
148        }
149        None
150    }
151
152    /// Returns underlying [`Constellation`], if any.
153    pub fn into_constellation(self) -> Option<Constellation> {
154        if let Self::Constellation(c) = self {
155            return Some(*c);
156        }
157        None
158    }
159
160    /// Returns the [`id::Id`]
161    pub fn id(&self) -> &id::Id {
162        match self {
163            Self::Constellation(c) => &c.id,
164            Self::Id { id } => id,
165        }
166    }
167}