Skip to main content

paas_client/
pseudonym_service.rs

1use crate::auth::SystemAuths;
2use crate::sessions::EncryptionContexts;
3use crate::transcryptor_client::{TranscryptorClient, TranscryptorError};
4use libpep::client::{Client, Distributed};
5use libpep::data::traits::{
6    BatchEncryptable, Encryptable, Encrypted, HasStructure, Pseudonymizable, Rekeyable,
7    Transcryptable,
8};
9use libpep::factors::PseudonymizationDomain;
10use libpep::keys::distribution::SessionKeyShares;
11use libpep::keys::{KeyProvider, SessionKeys};
12use libpep::transcryptor::BatchError;
13use paas_api::config::PAASConfig;
14use paas_api::paths::ApiPath;
15use paas_api::status::SystemId;
16use rand_core::{CryptoRng, Rng};
17use serde::Serialize;
18use serde::de::DeserializeOwned;
19use std::collections::HashMap;
20
21#[derive(Debug, thiserror::Error)]
22pub enum PseudonymServiceError {
23    #[error(transparent)]
24    TranscryptorError(#[from] TranscryptorError),
25    #[error(transparent)]
26    BatchError(#[from] BatchError),
27    #[error("No auth found for system {0}")]
28    MissingAuth(SystemId),
29    #[error("No session found for system {0}")]
30    MissingSession(SystemId),
31    #[error("No session key share found for system {0}")]
32    MissingSessionKeyShares(SystemId),
33    #[error("PEP crypto client not initialized")]
34    UninitializedClient,
35    #[error("Transcryptor does not have session")]
36    UninitializedTranscryptor,
37    #[error("Inconsistent config received from {system}")]
38    InconsistentConfig { system: SystemId },
39}
40
41#[derive(Clone)]
42pub struct PseudonymService {
43    pub(crate) config: PAASConfig,
44    pub(crate) transcryptors: Vec<TranscryptorClient>,
45    pep_crypto_client: Option<Client>,
46}
47
48pub type SessionKeySharess = HashMap<SystemId, SessionKeyShares>;
49
50/// Convert encrypted pseudonyms into your own pseudonyms, using the [PseudonymService].
51/// The service will communicate with the configured transcryptors, and wraps around a [Client] for cryptographic operations.
52impl PseudonymService {
53    /// Create a new PseudonymService with the given configuration.
54    pub async fn new(
55        config: PAASConfig,
56        auths: SystemAuths,
57    ) -> Result<Self, PseudonymServiceError> {
58        let transcryptors =
59            futures::future::try_join_all(config.transcryptors.iter().map(|c| async {
60                let auth = auths
61                    .get(&c.system_id)
62                    .ok_or_else(|| PseudonymServiceError::MissingAuth(c.system_id.clone()))?;
63
64                let mut client = TranscryptorClient::new(c.clone(), auth)
65                    .await
66                    .map_err(PseudonymServiceError::TranscryptorError)?;
67
68                let reported_config = client
69                    .check_config()
70                    .await
71                    .map_err(PseudonymServiceError::TranscryptorError)?;
72
73                if reported_config != config {
74                    return Err(PseudonymServiceError::InconsistentConfig {
75                        system: c.system_id.clone(),
76                    });
77                }
78
79                Ok(client)
80            }))
81            .await?;
82
83        Ok(Self {
84            config,
85            transcryptors,
86            pep_crypto_client: None,
87        })
88    }
89
90    /// Create a new PseudonymService with the given configuration, allowing HTTP URLs.
91    /// This should only be used for testing purposes.
92    #[doc(hidden)]
93    pub async fn new_allow_http(
94        config: PAASConfig,
95        auths: SystemAuths,
96    ) -> Result<Self, PseudonymServiceError> {
97        let transcryptors =
98            futures::future::try_join_all(config.transcryptors.iter().map(|c| async {
99                let auth = auths
100                    .get(&c.system_id)
101                    .ok_or_else(|| PseudonymServiceError::MissingAuth(c.system_id.clone()))?;
102
103                let mut client = TranscryptorClient::new_allow_http(c.clone(), auth)
104                    .await
105                    .map_err(PseudonymServiceError::TranscryptorError)?;
106
107                let reported_config = client
108                    .check_config()
109                    .await
110                    .map_err(PseudonymServiceError::TranscryptorError)?;
111
112                if reported_config != config {
113                    return Err(PseudonymServiceError::InconsistentConfig {
114                        system: c.system_id.clone(),
115                    });
116                }
117
118                Ok(client)
119            }))
120            .await?;
121
122        Ok(Self {
123            config,
124            transcryptors,
125            pep_crypto_client: None,
126        })
127    }
128
129    /// Restore a [PseudonymService] from a dumped state.
130    pub async fn restore(
131        config: PAASConfig,
132        auths: SystemAuths,
133        session_ids: EncryptionContexts,
134        session_key_shares: SessionKeySharess,
135        session_keys: SessionKeys,
136    ) -> Result<Self, PseudonymServiceError> {
137        let transcryptors =
138            futures::future::try_join_all(config.transcryptors.iter().map(|c| async {
139                let auth = auths
140                    .get(&c.system_id)
141                    .ok_or_else(|| PseudonymServiceError::MissingAuth(c.system_id.clone()))?;
142
143                let session_id = session_ids
144                    .get(&c.system_id)
145                    .ok_or_else(|| PseudonymServiceError::MissingSession(c.system_id.clone()))?;
146
147                let sks = session_key_shares.get(&c.system_id).ok_or_else(|| {
148                    PseudonymServiceError::MissingSessionKeyShares(c.system_id.clone())
149                })?;
150
151                let mut client =
152                    TranscryptorClient::restore(c.clone(), auth, session_id.clone(), *sks)
153                        .await
154                        .map_err(PseudonymServiceError::TranscryptorError)?;
155
156                let reported_config = client
157                    .check_config()
158                    .await
159                    .map_err(PseudonymServiceError::TranscryptorError)?;
160
161                if reported_config != config {
162                    return Err(PseudonymServiceError::InconsistentConfig {
163                        system: c.system_id.clone(),
164                    });
165                }
166
167                Ok(client)
168            }))
169            .await?;
170
171        Ok(Self {
172            config,
173            transcryptors,
174            pep_crypto_client: Some(Client::restore(session_keys)),
175        })
176    }
177
178    /// Dump the current state of the [PseudonymService].
179    pub fn dump(
180        &self,
181    ) -> Result<(EncryptionContexts, SessionKeys, SessionKeySharess), PseudonymServiceError> {
182        let session_ids = self.get_current_sessions();
183        let session_keys = *self
184            .pep_crypto_client
185            .as_ref()
186            .ok_or(PseudonymServiceError::UninitializedClient)?
187            .dump();
188
189        let mut session_key_shares = HashMap::new();
190        for transcryptor in &self.transcryptors {
191            if let Some(key_share) = transcryptor.sks.as_ref() {
192                session_key_shares.insert(transcryptor.config.system_id.clone(), *key_share);
193            }
194        }
195
196        Ok((session_ids?, session_keys, session_key_shares))
197    }
198
199    /// Check if the PEP crypto client is initialized.
200    pub fn is_initialized(&self) -> bool {
201        self.pep_crypto_client.is_some()
202    }
203
204    /// Start a new session with all configured transcryptors, and initialize a [Client] using the session keys.
205    pub async fn init(&mut self) -> Result<(), PseudonymServiceError> {
206        let mut sks = vec![];
207        for transcryptor in &mut self.transcryptors {
208            let (_session_id, key_share) = transcryptor
209                .start_session()
210                .await
211                .map_err(PseudonymServiceError::TranscryptorError)?;
212            sks.push(key_share);
213        }
214
215        self.pep_crypto_client = Some(Client::from_shares(self.config.blinded_global_keys, &sks));
216
217        Ok(())
218    }
219
220    /// End all sessions
221    pub async fn end(&mut self) -> Result<(), PseudonymServiceError> {
222        futures::future::try_join_all(self.transcryptors.iter_mut().map(|client| async {
223            client
224                .end_session()
225                .await
226                .map_err(PseudonymServiceError::TranscryptorError)
227        }))
228        .await?;
229
230        Ok(())
231    }
232
233    /// Refresh a transcryptor's session
234    pub async fn refresh_session(
235        &mut self,
236        transcryptor_index: usize,
237    ) -> Result<(), PseudonymServiceError> {
238        let old_sks = self.transcryptors[transcryptor_index].sks;
239
240        let (_, new_sks) = {
241            let transcryptor = &mut self.transcryptors[transcryptor_index];
242            transcryptor
243                .start_session()
244                .await
245                .map_err(PseudonymServiceError::TranscryptorError)?
246        };
247
248        if let (Some(old_sks), Some(crypto_client)) = (old_sks, self.pep_crypto_client.as_mut()) {
249            crypto_client.update_session_secret_keys(old_sks, new_sks);
250        } else {
251            self.init().await?;
252        }
253
254        Ok(())
255    }
256
257    // TODO add a way to change the order of transcryptors, and add a way to add new transcryptors
258
259    /// Transform an encrypted pseudonym into your own pseudonym.
260    pub async fn pseudonymize<T>(
261        &mut self,
262        encrypted_pseudonym: &T,
263        sessions_from: &EncryptionContexts,
264        domain_from: &PseudonymizationDomain,
265        domain_to: &PseudonymizationDomain,
266    ) -> Result<T, PseudonymServiceError>
267    where
268        T: Pseudonymizable + DeserializeOwned + Serialize + Clone + ApiPath,
269    {
270        if self.pep_crypto_client.is_none() {
271            self.init().await?;
272        }
273
274        let mut transcrypted = encrypted_pseudonym.clone();
275
276        for i in 0..self.transcryptors.len() {
277            let system_id = &self.transcryptors[i].config.system_id;
278            let session_from = sessions_from
279                .get(system_id)
280                .ok_or_else(|| PseudonymServiceError::MissingSession(system_id.clone()))?;
281
282            let session_id = match &self.transcryptors[i].session_id {
283                Some(id) => id.clone(),
284                None => return Err(PseudonymServiceError::UninitializedTranscryptor),
285            };
286
287            let result = self.transcryptors[i]
288                .pseudonymize(
289                    &transcrypted,
290                    domain_from,
291                    domain_to,
292                    session_from,
293                    &session_id,
294                )
295                .await;
296
297            transcrypted = match result {
298                Err(TranscryptorError::InvalidSession(_)) => {
299                    self.refresh_session(i).await?;
300
301                    let new_session_id = match &self.transcryptors[i].session_id {
302                        Some(id) => id.clone(),
303                        None => return Err(PseudonymServiceError::UninitializedTranscryptor),
304                    };
305                    self.transcryptors[i]
306                        .pseudonymize(
307                            &transcrypted,
308                            domain_from,
309                            domain_to,
310                            session_from,
311                            &new_session_id,
312                        )
313                        .await?
314                }
315                Err(err) => return Err(PseudonymServiceError::TranscryptorError(err)),
316                Ok(value) => value,
317            };
318        }
319
320        Ok(transcrypted)
321    }
322
323    /// Transform a batch of encrypted pseudonyms into your own pseudonyms.
324    /// Notice that the order of the pseudonyms in the input and output vectors are NOT the same, to prevent linking.
325    /// If you need to preserve the order, you should call the [pseudonymize] method for each pseudonym individually. (TODO: add a feature flag to preserve order)
326    pub async fn pseudonymize_batch<T>(
327        &mut self,
328        encrypted_pseudonyms: Vec<T>,
329        sessions_from: &EncryptionContexts,
330        domain_from: &PseudonymizationDomain,
331        domain_to: &PseudonymizationDomain,
332    ) -> Result<Vec<T>, PseudonymServiceError>
333    where
334        T: Pseudonymizable + DeserializeOwned + Serialize + Clone + ApiPath + HasStructure,
335    {
336        if self.pep_crypto_client.is_none() {
337            self.init().await?;
338        }
339
340        let mut transcrypted = encrypted_pseudonyms;
341
342        for i in 0..self.transcryptors.len() {
343            let system_id = &self.transcryptors[i].config.system_id;
344            let session_from = sessions_from
345                .get(system_id)
346                .ok_or_else(|| PseudonymServiceError::MissingSession(system_id.clone()))?;
347
348            let session_id = match &self.transcryptors[i].session_id {
349                Some(id) => id.clone(),
350                None => return Err(PseudonymServiceError::UninitializedTranscryptor),
351            };
352
353            let result = self.transcryptors[i]
354                .pseudonymize_batch(
355                    transcrypted.clone(),
356                    domain_from,
357                    domain_to,
358                    session_from,
359                    &session_id,
360                )
361                .await;
362
363            transcrypted = match result {
364                Err(TranscryptorError::InvalidSession(_)) => {
365                    self.refresh_session(i).await?;
366
367                    let new_session_id = match &self.transcryptors[i].session_id {
368                        Some(id) => id.clone(),
369                        None => return Err(PseudonymServiceError::UninitializedTranscryptor),
370                    };
371                    self.transcryptors[i]
372                        .pseudonymize_batch(
373                            transcrypted,
374                            domain_from,
375                            domain_to,
376                            session_from,
377                            &new_session_id,
378                        )
379                        .await?
380                }
381                Err(err) => return Err(PseudonymServiceError::TranscryptorError(err)),
382                Ok(value) => value,
383            };
384        }
385
386        Ok(transcrypted)
387    }
388
389    /// Transform an encrypted data point encrypted in one session into a data point you can decrypt.
390    pub async fn rekey<T>(
391        &mut self,
392        encrypted_data_point: &T,
393        sessions_from: &EncryptionContexts,
394    ) -> Result<T, PseudonymServiceError>
395    where
396        T: Rekeyable + DeserializeOwned + Serialize + Clone + ApiPath,
397    {
398        if self.pep_crypto_client.is_none() {
399            self.init().await?;
400        }
401
402        let mut transcrypted = encrypted_data_point.clone();
403
404        for i in 0..self.transcryptors.len() {
405            let system_id = &self.transcryptors[i].config.system_id;
406            let session_from = sessions_from
407                .get(system_id)
408                .ok_or_else(|| PseudonymServiceError::MissingSession(system_id.clone()))?;
409
410            let session_id = match &self.transcryptors[i].session_id {
411                Some(id) => id.clone(),
412                None => return Err(PseudonymServiceError::UninitializedTranscryptor),
413            };
414
415            let result = self.transcryptors[i]
416                .rekey(&transcrypted, session_from, &session_id)
417                .await;
418
419            transcrypted = match result {
420                Err(TranscryptorError::InvalidSession(_)) => {
421                    self.refresh_session(i).await?;
422
423                    let new_session_id = match &self.transcryptors[i].session_id {
424                        Some(id) => id.clone(),
425                        None => return Err(PseudonymServiceError::UninitializedTranscryptor),
426                    };
427                    self.transcryptors[i]
428                        .rekey(&transcrypted, session_from, &new_session_id)
429                        .await?
430                }
431                Err(err) => return Err(PseudonymServiceError::TranscryptorError(err)),
432                Ok(value) => value,
433            };
434        }
435
436        Ok(transcrypted)
437    }
438    /// Transform a batch of encrypted data points encrypted in one session into data points you can decrypt.
439    /// Notice that the order of the data points in the input and output vectors are NOT the same, to prevent linking.
440    /// If you need to preserve the order, you should call the [rekey] method for each data point individually. (TODO: add a feature flag to preserve order)
441    pub async fn rekey_batch<T>(
442        &mut self,
443        encrypted_data_points: Vec<T>,
444        sessions_from: &EncryptionContexts,
445    ) -> Result<Vec<T>, PseudonymServiceError>
446    where
447        T: Rekeyable + DeserializeOwned + Serialize + Clone + ApiPath + HasStructure,
448    {
449        if self.pep_crypto_client.is_none() {
450            self.init().await?;
451        }
452
453        let mut transcrypted = encrypted_data_points;
454
455        for i in 0..self.transcryptors.len() {
456            let system_id = &self.transcryptors[i].config.system_id;
457            let session_from = sessions_from
458                .get(system_id)
459                .ok_or_else(|| PseudonymServiceError::MissingSession(system_id.clone()))?;
460
461            let session_id = match &self.transcryptors[i].session_id {
462                Some(id) => id.clone(),
463                None => return Err(PseudonymServiceError::UninitializedTranscryptor),
464            };
465
466            let result = self.transcryptors[i]
467                .rekey_batch(transcrypted.clone(), session_from, &session_id)
468                .await;
469
470            transcrypted = match result {
471                Err(TranscryptorError::InvalidSession(_)) => {
472                    self.refresh_session(i).await?;
473
474                    let new_session_id = match &self.transcryptors[i].session_id {
475                        Some(id) => id.clone(),
476                        None => return Err(PseudonymServiceError::UninitializedTranscryptor),
477                    };
478                    self.transcryptors[i]
479                        .rekey_batch(transcrypted, session_from, &new_session_id)
480                        .await?
481                }
482                Err(err) => return Err(PseudonymServiceError::TranscryptorError(err)),
483                Ok(value) => value,
484            };
485        }
486
487        Ok(transcrypted)
488    }
489    /// Transform a single encrypted data item into data you can decrypt.
490    pub async fn transcrypt<T>(
491        &mut self,
492        encrypted: &T,
493        sessions_from: &EncryptionContexts,
494        domain_from: &PseudonymizationDomain,
495        domain_to: &PseudonymizationDomain,
496    ) -> Result<T, PseudonymServiceError>
497    where
498        T: Transcryptable + DeserializeOwned + Serialize + Clone + ApiPath,
499    {
500        if self.pep_crypto_client.is_none() {
501            self.init().await?;
502        }
503
504        let mut transcrypted = encrypted.clone();
505
506        for i in 0..self.transcryptors.len() {
507            let system_id = &self.transcryptors[i].config.system_id;
508            let session_from = sessions_from
509                .get(system_id)
510                .ok_or_else(|| PseudonymServiceError::MissingSession(system_id.clone()))?;
511
512            let session_id = match &self.transcryptors[i].session_id {
513                Some(id) => id.clone(),
514                None => return Err(PseudonymServiceError::UninitializedTranscryptor),
515            };
516
517            let result = self.transcryptors[i]
518                .transcrypt(
519                    &transcrypted,
520                    domain_from,
521                    domain_to,
522                    session_from,
523                    &session_id,
524                )
525                .await;
526
527            transcrypted = match result {
528                Err(TranscryptorError::InvalidSession(_)) => {
529                    self.refresh_session(i).await?;
530
531                    let new_session_id = match &self.transcryptors[i].session_id {
532                        Some(id) => id.clone(),
533                        None => return Err(PseudonymServiceError::UninitializedTranscryptor),
534                    };
535                    self.transcryptors[i]
536                        .transcrypt(
537                            &transcrypted,
538                            domain_from,
539                            domain_to,
540                            session_from,
541                            &new_session_id,
542                        )
543                        .await?
544                }
545                Err(err) => return Err(PseudonymServiceError::TranscryptorError(err)),
546                Ok(value) => value,
547            };
548        }
549
550        Ok(transcrypted)
551    }
552
553    /// Transform a batch of encrypted data for different entities into data you can decrypt.
554    /// Notice that the order of the entities in the input and output vectors are NOT the same, to prevent linking.
555    pub async fn transcrypt_batch<T>(
556        &mut self,
557        encrypted: Vec<T>,
558        sessions_from: &EncryptionContexts,
559        domain_from: &PseudonymizationDomain,
560        domain_to: &PseudonymizationDomain,
561    ) -> Result<Vec<T>, PseudonymServiceError>
562    where
563        T: Transcryptable + DeserializeOwned + Serialize + Clone + ApiPath + HasStructure,
564    {
565        if self.pep_crypto_client.is_none() {
566            self.init().await?;
567        }
568
569        let mut transcrypted = encrypted;
570
571        for i in 0..self.transcryptors.len() {
572            let system_id = &self.transcryptors[i].config.system_id;
573            let session_from = sessions_from
574                .get(system_id)
575                .ok_or_else(|| PseudonymServiceError::MissingSession(system_id.clone()))?;
576
577            let session_id = match &self.transcryptors[i].session_id {
578                Some(id) => id.clone(),
579                None => return Err(PseudonymServiceError::UninitializedTranscryptor),
580            };
581
582            let result = self.transcryptors[i]
583                .transcrypt_batch(
584                    transcrypted.clone(),
585                    domain_from,
586                    domain_to,
587                    session_from,
588                    &session_id,
589                )
590                .await;
591
592            transcrypted = match result {
593                Err(TranscryptorError::InvalidSession(_)) => {
594                    self.refresh_session(i).await?;
595
596                    let new_session_id = match &self.transcryptors[i].session_id {
597                        Some(id) => id.clone(),
598                        None => return Err(PseudonymServiceError::UninitializedTranscryptor),
599                    };
600                    self.transcryptors[i]
601                        .transcrypt_batch(
602                            transcrypted,
603                            domain_from,
604                            domain_to,
605                            session_from,
606                            &new_session_id,
607                        )
608                        .await?
609                }
610                Err(err) => return Err(PseudonymServiceError::TranscryptorError(err)),
611                Ok(value) => value,
612            };
613        }
614
615        Ok(transcrypted)
616    }
617    /// Encrypt a message using the [Client]'s current session.
618    pub fn encrypt<R: Rng + CryptoRng, E: Encryptable + 'static>(
619        &mut self,
620        message: &E,
621        rng: &mut R,
622    ) -> Result<(E::EncryptedType, EncryptionContexts), PseudonymServiceError>
623    where
624        SessionKeys: KeyProvider<E::PublicKeyType>,
625    {
626        let pep_client = self
627            .pep_crypto_client
628            .as_ref()
629            .ok_or(PseudonymServiceError::UninitializedClient)?;
630
631        Ok((
632            pep_client.encrypt(message, rng),
633            self.get_current_sessions()?.clone(),
634        ))
635    }
636
637    /// Batch encrypt a vec of message using the [Client]'s current session.
638    pub fn encrypt_batch<R: Rng + CryptoRng, E: Encryptable + BatchEncryptable + 'static>(
639        &mut self,
640        message: &[E],
641        rng: &mut R,
642    ) -> Result<(Vec<E::EncryptedType>, EncryptionContexts), PseudonymServiceError>
643    where
644        SessionKeys: KeyProvider<E::PublicKeyType>,
645    {
646        let pep_client = self
647            .pep_crypto_client
648            .as_ref()
649            .ok_or(PseudonymServiceError::UninitializedClient)?;
650
651        Ok((
652            pep_client.encrypt_batch(message, rng)?,
653            self.get_current_sessions()?.clone(),
654        ))
655    }
656
657    pub fn get_current_sessions(&self) -> Result<EncryptionContexts, PseudonymServiceError> {
658        let sessions = self
659            .transcryptors
660            .iter()
661            .map(|t| {
662                let session_id = t
663                    .session_id
664                    .clone()
665                    .ok_or(PseudonymServiceError::UninitializedTranscryptor)?;
666
667                Ok((t.config.system_id.clone(), session_id))
668            })
669            .collect::<Result<_, PseudonymServiceError>>()?;
670
671        Ok(EncryptionContexts(sessions))
672    }
673
674    /// Decrypt an encrypted message using the [Client]'s current session.
675    pub fn decrypt<E: Encrypted>(
676        &mut self,
677        encrypted: &E,
678    ) -> Result<E::UnencryptedType, PseudonymServiceError>
679    where
680        SessionKeys: KeyProvider<E::SecretKeyType>,
681    {
682        let pep_client = self
683            .pep_crypto_client
684            .as_ref()
685            .ok_or(PseudonymServiceError::UninitializedClient)?;
686
687        Ok(pep_client.decrypt(encrypted))
688    }
689
690    /// Batch decrypt a vec of encrypted messages using the [Client]'s current session.
691    pub fn decrypt_batch<E: Encrypted>(
692        &mut self,
693        encrypted: &[E],
694    ) -> Result<Vec<E::UnencryptedType>, PseudonymServiceError>
695    where
696        SessionKeys: KeyProvider<E::SecretKeyType>,
697    {
698        let pep_client = self
699            .pep_crypto_client
700            .as_ref()
701            .ok_or(PseudonymServiceError::UninitializedClient)?;
702
703        Ok(pep_client.decrypt_batch(encrypted)?)
704    }
705}