pubhubs/servers/transcryptor/
server.rs1use std::ops::{Deref, DerefMut};
2use std::rc::Rc;
3
4use actix_web::web;
5
6use crate::common::elgamal;
7use crate::misc::crypto;
8use crate::misc::serde_ext::bytes_wrapper::B64UU;
9use crate::phcrypto;
10use crate::{
11 api::{self, EndpointDetails as _},
12 servers::{self, AppBase, AppCreatorBase, Constellation, Handle, constellation},
13};
14
15use api::tr::*;
16
17pub type Server = servers::ServerImpl<Details>;
19
20pub struct Details;
21impl servers::Details for Details {
22 const NAME: servers::Name = servers::Name::Transcryptor;
23 type AppT = App;
24 type AppCreatorT = AppCreator;
25 type ExtraRunningState = ExtraRunningState;
26 type ExtraSharedState = ExtraSharedState;
27 type ObjectStoreT = servers::object_store::UseNone;
28
29 fn create_running_state(
30 server: &Server,
31 constellation: &Constellation,
32 ) -> anyhow::Result<Self::ExtraRunningState> {
33 let phc_ss = server.enc_key.shared_secret(&constellation.phc_enc_key);
34
35 Ok(ExtraRunningState {
36 phc_sealing_secret: phcrypto::sealing_secret(&phc_ss),
37 phc_ss,
38 })
39 }
40
41 fn create_extra_shared_state(_config: &servers::Config) -> anyhow::Result<ExtraSharedState> {
42 Ok(ExtraSharedState {})
43 }
44}
45
46pub struct ExtraSharedState {}
47
48#[derive(Clone, Debug)]
49pub struct ExtraRunningState {
50 #[expect(dead_code)]
52 phc_ss: elgamal::SharedSecret,
53
54 pub(super) phc_sealing_secret: crypto::SealingKey,
56}
57
58pub struct App {
59 base: AppBase<Server>,
60 master_enc_key_part: elgamal::PrivateKey,
61 master_enc_key_part_inv: curve25519_dalek::Scalar,
62 pseud_factor_secret: B64UU,
63}
64
65impl Deref for App {
66 type Target = AppBase<Server>;
67
68 fn deref(&self) -> &Self::Target {
69 &self.base
70 }
71}
72
73impl crate::servers::App<Server> for App {
74 fn configure_actix_app(self: &Rc<Self>, sc: &mut web::ServiceConfig) {
75 EhppEP::add_to(self, sc, App::handle_ehpp);
76 api::server::HubPingEP::add_to(self, sc, App::handle_hub_ping);
77 }
78
79 fn check_constellation(&self, constellation: &Constellation) -> bool {
80 let Constellation {
83 inner:
84 constellation::Inner {
85 transcryptor_jwt_key: jwt_key,
87 transcryptor_enc_key: enc_key,
88 transcryptor_master_enc_key_part: master_enc_key_part,
89
90 transcryptor_url: _,
92 auths_enc_key: _,
93 auths_jwt_key: _,
94 auths_url: _,
95 phc_jwt_key: _,
96 phc_enc_key: _,
97 phc_url: _,
98 master_enc_key: _,
99 global_client_url: _,
100 ph_version: _, },
102 id: _,
103 created_at: _,
104 } = constellation;
105
106 enc_key == self.enc_key.public_key()
107 && **jwt_key == self.jwt_key.verifying_key()
108 && master_enc_key_part == self.master_enc_key_part.public_key()
109 }
110
111 fn master_enc_key_part(&self) -> Option<&elgamal::PrivateKey> {
112 Some(&self.master_enc_key_part)
113 }
114}
115
116impl App {
117 async fn handle_hub_ping(
119 app: Rc<Self>,
120 signed_req: web::Json<api::phc::hub::TicketSigned<api::server::PingReq>>,
121 ) -> api::Result<api::server::PingResp> {
122 crate::servers::AppBase::<Server>::handle_hub_ping(app, signed_req).await
123 }
124
125 async fn handle_ehpp(app: Rc<Self>, req: web::Json<EhppReq>) -> api::Result<EhppResp> {
127 let running_state = app.running_state_or_please_retry()?;
128
129 let EhppReq {
130 hub_nonce,
131 hub,
132 ppp,
133 } = req.into_inner();
134
135 let Ok(api::sso::PolymorphicPseudonymPackage {
136 polymorphic_pseudonym,
137 nonce: phc_nonce,
138 }) = ppp.open(&running_state.phc_sealing_secret)
139 else {
140 return Ok(EhppResp::RetryWithNewPpp);
141 };
142
143 let encrypted_hub_pseudonym: elgamal::Triple = phcrypto::t_encrypted_hub_pseudonym(
144 polymorphic_pseudonym,
145 &***app.pseud_factor_secret,
146 &app.master_enc_key_part_inv,
147 hub,
148 );
149
150 Ok(EhppResp::Success(api::Sealed::new(
151 &api::sso::EncryptedHubPseudonymPackage {
152 encrypted_hub_pseudonym,
153 hub_nonce,
154 phc_nonce,
155 },
156 &running_state.phc_sealing_secret,
157 )?))
158 }
159}
160
161#[derive(Clone)]
162pub struct AppCreator {
163 base: AppCreatorBase<Server>,
164 master_enc_key_part: elgamal::PrivateKey,
165 master_enc_key_part_inv: curve25519_dalek::Scalar,
166 pseud_factor_secret: B64UU,
167}
168
169impl Deref for AppCreator {
170 type Target = AppCreatorBase<Server>;
171
172 fn deref(&self) -> &Self::Target {
173 &self.base
174 }
175}
176
177impl DerefMut for AppCreator {
178 fn deref_mut(&mut self) -> &mut Self::Target {
179 &mut self.base
180 }
181}
182
183impl crate::servers::AppCreator<Server> for AppCreator {
184 type ContextT = ();
185
186 fn new(config: &servers::Config) -> anyhow::Result<Self> {
187 let xconf = &config.transcryptor.as_ref().unwrap();
188
189 let master_enc_key_part: elgamal::PrivateKey = xconf
190 .master_enc_key_part
191 .clone()
192 .expect("master_enc_key_part was not generated");
193
194 let pseud_factor_secret = xconf
195 .pseud_factor_secret
196 .clone()
197 .expect("pseud_factor_secret was not generated");
198
199 Ok(Self {
200 base: AppCreatorBase::<Server>::new(config)?,
201 master_enc_key_part_inv: master_enc_key_part.as_scalar().invert(),
202 master_enc_key_part,
203 pseud_factor_secret,
204 })
205 }
206
207 fn into_app(
208 self,
209 handle: &Handle<Server>,
210 _context: &Self::ContextT,
211 generation: usize,
212 ) -> App {
213 App {
214 base: AppBase::new(self.base, handle, generation),
215 master_enc_key_part: self.master_enc_key_part,
216 master_enc_key_part_inv: self.master_enc_key_part_inv,
217 pseud_factor_secret: self.pseud_factor_secret,
218 }
219 }
220}