Skip to main content

paas_server/application/
transcrypt.rs

1use crate::access_rules::AccessRules;
2use crate::auth::core::AuthInfo;
3use crate::errors::PAASServerError;
4use crate::session_storage::SessionStorage;
5use actix_web::web::Data;
6use actix_web::{web, HttpResponse};
7use libpep::data::traits::{HasStructure, Pseudonymizable, Rekeyable, Transcryptable};
8use libpep::factors::{EncryptionContext, RekeyInfoProvider};
9use libpep::transcryptor::{DistributedTranscryptor, Transcryptor};
10use log::{debug, error, info, warn};
11use paas_api::transcrypt::{
12    PseudonymizationBatchRequest, PseudonymizationBatchResponse, PseudonymizationRequest,
13    PseudonymizationResponse, RekeyBatchRequest, RekeyBatchResponse, RekeyRequest, RekeyResponse,
14    TranscryptionBatchRequest, TranscryptionBatchResponse, TranscryptionRequest,
15    TranscryptionResponse,
16};
17use serde::Serialize;
18
19pub async fn pseudonymize<T>(
20    item: web::Json<PseudonymizationRequest<T>>,
21    access_rules: Data<AccessRules>,
22    session_storage: Data<Box<dyn SessionStorage>>,
23    pep_system: Data<DistributedTranscryptor>,
24    user: web::ReqData<AuthInfo>,
25) -> Result<HttpResponse, PAASServerError>
26where
27    T: Pseudonymizable + Serialize,
28{
29    let session_storage = session_storage.get_ref();
30    let request = item.into_inner();
31
32    debug!(
33        "Processing pseudonymization request: domain={:?}→{:?} session={:?}→{:?} {}",
34        request.domain_from, request.domain_to, request.session_from, request.session_to, *user
35    );
36
37    if !access_rules.has_access(&user, &request.domain_from, &request.domain_to) {
38        warn!(
39            "Access denied: domain={:?}→{:?} {:?}",
40            request.domain_from, request.domain_to, *user
41        );
42        return Err(PAASServerError::AccessDenied {
43            from: format!("{:?}", request.domain_from.clone()),
44            to: format!("{:?}", request.domain_to.clone()),
45        });
46    }
47
48    let session_valid =
49        match session_storage.session_exists(user.sub.to_string(), request.session_to.clone()) {
50            Ok(valid) => valid,
51            Err(e) => {
52                error!(
53                    "Failed to check if session exists: session={:?} {}",
54                    request.session_to, *user
55                );
56                return Err(PAASServerError::SessionError(Box::new(e)));
57            }
58        };
59
60    if !session_valid {
61        warn!(
62            "Invalid session: session={:?} {}",
63            request.session_to, *user
64        );
65        return Err(PAASServerError::InvalidSession(
66            "Target session not owned by user".to_string(),
67        ));
68    }
69
70    let pseudonymization_info = pep_system.pseudonymization_info(
71        &request.domain_from,
72        &request.domain_to,
73        &request.session_from,
74        &request.session_to,
75    );
76
77    let result = pep_system.pseudonymize(&request.encrypted, &pseudonymization_info);
78
79    info!(
80        "Pseudonymized: domain={:?}→{:?} session={:?}→{:?} {}",
81        request.domain_from, request.domain_to, request.session_from, request.session_to, *user
82    );
83
84    Ok(HttpResponse::Ok().json(PseudonymizationResponse { result }))
85}
86
87pub async fn pseudonymize_batch<T>(
88    item: web::Json<PseudonymizationBatchRequest<T>>,
89    access_rules: Data<AccessRules>,
90    session_storage: Data<Box<dyn SessionStorage>>,
91    pep_system: Data<DistributedTranscryptor>,
92    user: web::ReqData<AuthInfo>,
93) -> Result<HttpResponse, PAASServerError>
94where
95    T: Pseudonymizable + Serialize + Clone + HasStructure,
96{
97    let session_storage = session_storage.get_ref();
98    let request = item.into_inner();
99    let batch_size = request.encrypted.len();
100
101    debug!(
102        "Processing batch pseudonymization request: domain={:?}→{:?} session={:?}→{:?} count={} {}",
103        request.domain_from,
104        request.domain_to,
105        request.session_from,
106        request.session_to,
107        batch_size,
108        *user
109    );
110
111    if !access_rules.has_access(&user, &request.domain_from, &request.domain_to) {
112        warn!(
113            "Access denied: domain={:?}→{:?} {}",
114            request.domain_from, request.domain_to, *user
115        );
116        return Err(PAASServerError::AccessDenied {
117            from: format!("{:?}", request.domain_from.clone()),
118            to: format!("{:?}", request.domain_to.clone()),
119        });
120    }
121
122    let session_valid =
123        match session_storage.session_exists(user.sub.to_string(), request.session_to.clone()) {
124            Ok(valid) => valid,
125            Err(e) => {
126                error!(
127                    "Failed to check if session exists: session={:?} {}",
128                    request.session_to, *user
129                );
130                return Err(PAASServerError::SessionError(Box::new(e)));
131            }
132        };
133
134    if !session_valid {
135        warn!(
136            "Invalid session: session={:?} {}",
137            request.session_to, *user
138        );
139        return Err(PAASServerError::InvalidSession(
140            "Target session not owned by user".to_string(),
141        ));
142    }
143
144    let mut encrypted_pseudonyms = request.encrypted.clone();
145    let mut rng = rand::rng();
146
147    let pseudonymization_info = pep_system.pseudonymization_info(
148        &request.domain_from,
149        &request.domain_to,
150        &request.session_from,
151        &request.session_to,
152    );
153
154    let msg_out = pep_system.pseudonymize_batch(
155        &mut encrypted_pseudonyms,
156        &pseudonymization_info,
157        &mut rng,
158    )?;
159
160    info!(
161        "Pseudonymized batch: domain={:?}→{:?} session={:?}→{:?} count={} {}",
162        request.domain_from,
163        request.domain_to,
164        request.session_from,
165        request.session_to,
166        batch_size,
167        *user
168    );
169
170    Ok(HttpResponse::Ok().json(PseudonymizationBatchResponse {
171        result: Vec::from(msg_out),
172    }))
173}
174
175#[allow(unreachable_code)]
176fn validate_rekey_request(
177    _session_to: &EncryptionContext,
178    _user: &AuthInfo,
179    _session_storage: &dyn SessionStorage,
180) -> Result<(), PAASServerError> {
181    // TODO: check access rules!
182    return Err(PAASServerError::Unauthorized(
183        "Rekeying is currently disabled pending access rule implementation".to_string(),
184    ));
185
186    let session_valid =
187        match _session_storage.session_exists(_user.sub.to_string(), _session_to.clone()) {
188            Ok(valid) => valid,
189            Err(e) => {
190                error!(
191                    "Failed to check if session exists: session={:?} {}",
192                    _session_to, *_user
193                );
194                return Err(PAASServerError::SessionError(Box::new(e)));
195            }
196        };
197
198    if !session_valid {
199        warn!("Invalid session: session={:?} {}", _session_to, *_user);
200        return Err(PAASServerError::InvalidSession(
201            "Target session not owned by user".to_string(),
202        ));
203    }
204    Ok(())
205}
206
207pub async fn rekey<T>(
208    item: web::Json<RekeyRequest<T>>,
209    _access_rules: Data<AccessRules>,
210    session_storage: Data<Box<dyn SessionStorage>>,
211    pep_system: Data<DistributedTranscryptor>,
212    user: web::ReqData<AuthInfo>,
213) -> Result<HttpResponse, PAASServerError>
214where
215    T: Rekeyable + Serialize,
216    Transcryptor: RekeyInfoProvider<<T as Rekeyable>::RekeyInfo>,
217{
218    let session_storage = session_storage.get_ref();
219    let request = item.into_inner();
220
221    debug!(
222        "Processing rekey request: session={:?}→{:?} {}",
223        request.session_from, request.session_to, *user
224    );
225
226    validate_rekey_request(&request.session_to, &user, session_storage.as_ref())?;
227
228    let rekey_info = pep_system.rekey_info(&request.session_from, &request.session_to);
229
230    let result = pep_system.rekey(&request.encrypted, &rekey_info);
231
232    info!(
233        "Rekeyed: session={:?}→{:?} {}",
234        request.session_from, request.session_to, *user
235    );
236
237    Ok(HttpResponse::Ok().json(RekeyResponse { result }))
238}
239
240pub async fn rekey_batch<T>(
241    item: web::Json<RekeyBatchRequest<T>>,
242    _access_rules: Data<AccessRules>,
243    session_storage: Data<Box<dyn SessionStorage>>,
244    pep_system: Data<DistributedTranscryptor>,
245    user: web::ReqData<AuthInfo>,
246) -> Result<HttpResponse, PAASServerError>
247where
248    T: Rekeyable + Serialize + Clone + HasStructure,
249    Transcryptor: RekeyInfoProvider<<T as Rekeyable>::RekeyInfo>,
250    <T as Rekeyable>::RekeyInfo: Copy,
251{
252    let session_storage = session_storage.get_ref();
253    let request = item.into_inner();
254    let batch_size = request.encrypted.len();
255
256    debug!(
257        "Processing batch rekey request: session={:?}→{:?} count={} {}",
258        request.session_from, request.session_to, batch_size, *user
259    );
260    validate_rekey_request(&request.session_to, &user, session_storage.as_ref())?;
261
262    let rekey_info = pep_system.rekey_info(&request.session_from, &request.session_to);
263
264    let mut encrypted = request.encrypted.clone();
265    let mut rng = rand::rng();
266    let msg_out = pep_system.rekey_batch(&mut encrypted, &rekey_info, &mut rng)?;
267
268    info!(
269        "Rekeyed batch: session={:?}→{:?} count={} {}",
270        request.session_from, request.session_to, batch_size, *user
271    );
272
273    Ok(HttpResponse::Ok().json(RekeyBatchResponse {
274        result: Vec::from(msg_out),
275    }))
276}
277
278pub async fn transcrypt<T>(
279    item: web::Json<TranscryptionRequest<T>>,
280    access_rules: Data<AccessRules>,
281    session_storage: Data<Box<dyn SessionStorage>>,
282    pep_system: Data<DistributedTranscryptor>,
283    user: web::ReqData<AuthInfo>,
284) -> Result<HttpResponse, PAASServerError>
285where
286    T: Transcryptable + Serialize,
287{
288    let session_storage = session_storage.get_ref();
289    let request = item.into_inner();
290
291    debug!(
292        "Processing transcrypt request: domain={:?}→{:?} session={:?}→{:?} {}",
293        request.domain_from, request.domain_to, request.session_from, request.session_to, *user
294    );
295
296    if !access_rules.has_access(&user, &request.domain_from, &request.domain_to) {
297        warn!(
298            "Access denied: domain={:?}→{:?} {}",
299            request.domain_from, request.domain_to, *user
300        );
301        return Err(PAASServerError::AccessDenied {
302            from: format!("{:?}", request.domain_from.clone()),
303            to: format!("{:?}", request.domain_to.clone()),
304        });
305    }
306
307    let session_valid =
308        match session_storage.session_exists(user.sub.to_string(), request.session_to.clone()) {
309            Ok(valid) => valid,
310            Err(e) => {
311                error!(
312                    "Failed to check if session exists: session={:?} {}",
313                    request.session_to, *user
314                );
315                return Err(PAASServerError::SessionError(Box::new(e)));
316            }
317        };
318
319    if !session_valid {
320        warn!(
321            "Invalid session: session={:?} {}",
322            request.session_to, *user
323        );
324        return Err(PAASServerError::InvalidSession(
325            "Target session not owned by user".to_string(),
326        ));
327    }
328
329    let transcryption_info = pep_system.transcryption_info(
330        &request.domain_from,
331        &request.domain_to,
332        &request.session_from,
333        &request.session_to,
334    );
335
336    let result = pep_system.transcrypt(&request.encrypted, &transcryption_info);
337
338    info!(
339        "Transcrypted: domain={:?}→{:?} session={:?}→{:?} {}",
340        request.domain_from, request.domain_to, request.session_from, request.session_to, *user
341    );
342
343    Ok(HttpResponse::Ok().json(TranscryptionResponse { result }))
344}
345
346pub async fn transcrypt_batch<T>(
347    item: web::Json<TranscryptionBatchRequest<T>>,
348    access_rules: Data<AccessRules>,
349    session_storage: Data<Box<dyn SessionStorage>>,
350    pep_system: Data<DistributedTranscryptor>,
351    user: web::ReqData<AuthInfo>,
352) -> Result<HttpResponse, PAASServerError>
353where
354    T: Transcryptable + Serialize + HasStructure + Clone,
355{
356    let session_storage = session_storage.get_ref();
357    let request = item.into_inner();
358    let batch_size = request.encrypted.len();
359
360    debug!(
361        "Processing transcrypt batch request: domain={:?}→{:?} session={:?}→{:?} count={} {}",
362        request.domain_from,
363        request.domain_to,
364        request.session_from,
365        request.session_to,
366        batch_size,
367        *user
368    );
369
370    if !access_rules.has_access(&user, &request.domain_from, &request.domain_to) {
371        warn!(
372            "Access denied: domain={:?}→{:?} {}",
373            request.domain_from, request.domain_to, *user
374        );
375        return Err(PAASServerError::AccessDenied {
376            from: format!("{:?}", request.domain_from.clone()),
377            to: format!("{:?}", request.domain_to.clone()),
378        });
379    }
380
381    let session_valid =
382        match session_storage.session_exists(user.sub.to_string(), request.session_to.clone()) {
383            Ok(valid) => valid,
384            Err(e) => {
385                error!(
386                    "Failed to check if session exists: session={:?} {}",
387                    request.session_to, *user
388                );
389                return Err(PAASServerError::SessionError(Box::new(e)));
390            }
391        };
392
393    if !session_valid {
394        warn!(
395            "Invalid session: session={:?} {}",
396            request.session_to, *user
397        );
398        return Err(PAASServerError::InvalidSession(
399            "Target session not owned by user".to_string(),
400        ));
401    }
402
403    let transcryption_info = pep_system.transcryption_info(
404        &request.domain_from,
405        &request.domain_to,
406        &request.session_from,
407        &request.session_to,
408    );
409
410    let mut rng = rand::rng();
411    let msg_out = pep_system.transcrypt_batch(
412        &mut request.encrypted.into_boxed_slice(),
413        &transcryption_info,
414        &mut rng,
415    )?;
416
417    info!(
418        "Transcrypted: domain={:?}→{:?} session={:?}→{:?} count={} {}",
419        request.domain_from,
420        request.domain_to,
421        request.session_from,
422        request.session_to,
423        batch_size,
424        *user
425    );
426
427    Ok(HttpResponse::Ok().json(TranscryptionBatchResponse {
428        result: Vec::from(msg_out),
429    }))
430}