pubhubs/servers/phc/
user_sso.rs1use std::rc::Rc;
3
4use curve25519_dalek::RistrettoPoint;
5
6use crate::api;
7use crate::id;
8
9use serde::{Deserialize, Serialize};
10
11use super::server::*;
12use crate::api::phc::user::*;
13use crate::api::sso::*;
14
15impl App {
16 pub(crate) async fn handle_user_ppp(
18 app: Rc<Self>,
19 auth_token: actix_web::web::Header<AuthToken>,
20 ) -> api::Result<PppResp> {
21 let running_state = app.running_state_or_please_retry()?;
22
23 let Ok((user_state, _)) = app
24 .open_auth_token_and_get_user_state(auth_token.into_inner())
25 .await?
26 else {
27 return Ok(PppResp::RetryWithNewAuthToken);
28 };
29
30 let now = api::NumericDate::now();
31
32 let nonce_inner = PpNonceInner {
33 user_id: user_state.id,
34 not_valid_after: now.add_clamp(app.pp_nonce_validity.as_secs()),
35 issued_at: now,
36 };
37
38 Ok(PppResp::Success(api::Sealed::new(
39 &PolymorphicPseudonymPackage {
40 polymorphic_pseudonym: user_state.polymorphic_pseudonym.rerandomize(),
43 nonce: api::Sealed::new(&nonce_inner, &app.pp_nonce_secret)?.into(),
44 },
45 &running_state.t_sealing_secret,
46 )?))
47 }
48
49 pub(crate) async fn handle_user_hhpp(
51 app: Rc<Self>,
52 req: actix_web::web::Json<HhppReq>,
53 auth_token: actix_web::web::Header<AuthToken>,
54 ) -> api::Result<HhppResp> {
55 let running_state = app.running_state_or_please_retry()?;
56
57 let Ok(auth_token_user_id) = app.open_auth_token(auth_token.into_inner()) else {
58 return Ok(HhppResp::RetryWithNewAuthToken);
59 };
60
61 let req = req.into_inner();
62 let hhpp_signature_scheme = req.hhpp_signature_scheme;
63
64 let Ok(EncryptedHubPseudonymPackage {
65 encrypted_hub_pseudonym,
66 hub_nonce,
67 phc_nonce,
68 hub_id_mac,
69 }) = req.ehpp.open(&running_state.t_sealing_secret)
70 else {
71 log::debug!("invalid Ehpp submitted to Hhpp endpoint");
72 return Ok(HhppResp::RetryWithNewPpp);
73 };
74
75 let Ok(PpNonceInner {
76 user_id: phc_nonce_user_id,
77 issued_at: pp_issued_at,
78 not_valid_after,
79 }) = api::Sealed::<PpNonceInner>::from(phc_nonce).open(&app.pp_nonce_secret)
80 else {
81 log::info!("Ehpp containing invalid PHC nonce submitted to Hhpp endpoint");
82 return Ok(HhppResp::RetryWithNewPpp);
83 };
85
86 if phc_nonce_user_id != auth_token_user_id {
87 log::warn!("user {auth_token_user_id} used {phc_nonce_user_id}'s PP nonce");
88 return Err(api::ErrorCode::BadRequest);
89 }
90
91 if not_valid_after < api::NumericDate::now() {
92 log::debug!("Ehpp containing expired PHC nonce submitted to Hhpp endpoint");
93 return Ok(HhppResp::RetryWithNewPpp);
94 }
95
96 let Some(hub_pseudonym) =
97 encrypted_hub_pseudonym.decrypt_and_check_pk(&app.master_enc_key_part)
98 else {
99 log::warn!("hub pseudonym was encrypted for the wrong public key");
100 return Err(api::ErrorCode::InternalError);
101 };
105
106 let hashed_hub_pseudonym: api::CurvePoint =
107 RistrettoPoint::hash_from_bytes::<sha2::Sha512>(hub_pseudonym.compress().as_bytes())
108 .compress()
109 .into();
110
111 let hhpp = HashedHubPseudonymPackage {
112 hashed_hub_pseudonym,
113 pp_issued_at,
114 hub_nonce,
115 hub_id_mac,
116 };
117
118 let signed = match hhpp_signature_scheme {
122 HhppSignatureScheme::Ed25519 => api::Signed::new_opts(
123 app.shared.signing_key.ed25519_signing_key(),
124 &hhpp,
125 app.pp_nonce_validity,
126 Some(&running_state.constellation),
127 )?,
128 HhppSignatureScheme::HybridInterim => api::Signed::new_opts(
129 &app.shared.signing_key,
130 &hhpp,
131 app.pp_nonce_validity,
132 Some(&running_state.constellation),
133 )?,
134 HhppSignatureScheme::HybridStandard => {
135 log::warn!(
136 "hub requested the not-yet-implemented conformant HHPP signature scheme"
137 );
138 return Err(api::ErrorCode::BadRequest);
139 }
140 };
141
142 Ok(HhppResp::Success(signed))
143 }
144}
145
146#[derive(Serialize, Deserialize, Debug)]
148struct PpNonceInner {
149 not_valid_after: api::NumericDate,
151
152 issued_at: api::NumericDate,
154
155 user_id: id::Id,
157}
158
159api::having_message_code!(PpNonceInner, PpNonce);
160
161impl From<api::Sealed<PpNonceInner>> for PpNonce {
162 fn from(sealed: api::Sealed<PpNonceInner>) -> Self {
163 Self {
164 inner: sealed.inner,
165 }
166 }
167}
168
169impl From<PpNonce> for api::Sealed<PpNonceInner> {
170 fn from(nonce: PpNonce) -> Self {
171 nonce.inner.into()
172 }
173}