core_crypto/transaction_context/e2e_identity/
stash.rs

1use super::Result;
2use crate::RecursiveError;
3use crate::e2e_identity::EnrollmentHandle;
4use crate::prelude::E2eiEnrollment;
5use crate::transaction_context::TransactionContext;
6
7impl TransactionContext {
8    /// Allows persisting an active enrollment (for example while redirecting the user during OAuth)
9    /// in order to resume it later with [TransactionContext::e2ei_enrollment_stash_pop]
10    ///
11    /// # Arguments
12    /// * `enrollment` - the enrollment instance to persist
13    ///
14    /// # Returns
15    /// A handle for retrieving the enrollment later on
16    pub async fn e2ei_enrollment_stash(&self, enrollment: E2eiEnrollment) -> Result<EnrollmentHandle> {
17        enrollment
18            .stash(
19                &self
20                    .mls_provider()
21                    .await
22                    .map_err(RecursiveError::transaction("getting mls provider"))?,
23            )
24            .await
25            .map_err(RecursiveError::e2e_identity("stashing enrollment"))
26            .map_err(Into::into)
27    }
28
29    /// Fetches the persisted enrollment and deletes it from the keystore
30    ///
31    /// # Arguments
32    /// * `handle` - returned by [TransactionContext::e2ei_enrollment_stash]
33    pub async fn e2ei_enrollment_stash_pop(&self, handle: EnrollmentHandle) -> Result<E2eiEnrollment> {
34        E2eiEnrollment::stash_pop(
35            &self
36                .mls_provider()
37                .await
38                .map_err(RecursiveError::transaction("getting mls provider"))?,
39            handle,
40        )
41        .await
42        .map_err(RecursiveError::e2e_identity("popping stashed enrollment"))
43        .map_err(Into::into)
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use crate::{
50        e2e_identity::{enrollment::test_utils::*, id::WireQualifiedClientId},
51        prelude::{E2eiEnrollment, INITIAL_KEYING_MATERIAL_COUNT},
52        test_utils::{x509::X509TestChain, *},
53    };
54    use mls_crypto_provider::MlsCryptoProvider;
55    use wasm_bindgen_test::*;
56
57    use core_crypto_keystore::DatabaseKey;
58
59    wasm_bindgen_test_configure!(run_in_browser);
60
61    #[apply(all_cred_cipher)]
62    #[wasm_bindgen_test]
63    async fn stash_and_pop_should_not_abort_enrollment(case: TestContext) {
64        let mut cc = SessionContext::new_uninitialized(&case).await;
65        Box::pin(async move {
66            let x509_test_chain = X509TestChain::init_empty(case.signature_scheme());
67
68            let is_renewal = false;
69            let (mut enrollment, cert) = e2ei_enrollment(
70                &mut cc,
71                &case,
72                &x509_test_chain,
73                Some(E2EI_CLIENT_ID_URI),
74                is_renewal,
75                init_enrollment,
76                |e, cc| {
77                    Box::pin(async move {
78                        let handle = cc.e2ei_enrollment_stash(e).await.unwrap();
79                        cc.e2ei_enrollment_stash_pop(handle).await.unwrap()
80                    })
81                },
82            )
83            .await
84            .unwrap();
85
86            assert!(
87                cc.transaction
88                    .e2ei_mls_init_only(&mut enrollment, cert, Some(INITIAL_KEYING_MATERIAL_COUNT))
89                    .await
90                    .is_ok()
91            );
92        })
93        .await
94    }
95
96    // this ensures the nominal test does its job
97    #[apply(all_cred_cipher)]
98    #[wasm_bindgen_test]
99    async fn should_fail_when_restoring_invalid(case: TestContext) {
100        let mut cc = SessionContext::new_uninitialized(&case).await;
101        Box::pin(async move {
102            let x509_test_chain = X509TestChain::init_empty(case.signature_scheme());
103
104            let is_renewal = false;
105            let result = e2ei_enrollment(
106                &mut cc,
107                &case,
108                &x509_test_chain,
109                Some(E2EI_CLIENT_ID_URI),
110                is_renewal,
111                init_enrollment,
112                move |e, _cc| {
113                    Box::pin(async move {
114                        // this restore recreates a partial enrollment
115                        let key = DatabaseKey::generate();
116                        let backend = MlsCryptoProvider::try_new_in_memory(&key).await.unwrap();
117                        backend.new_transaction().await.unwrap();
118                        let client_id = e.client_id().parse::<WireQualifiedClientId>().unwrap();
119                        E2eiEnrollment::try_new(
120                            client_id.into(),
121                            e.display_name().to_string(),
122                            e.handle().to_string(),
123                            e.team().map(ToString::to_string),
124                            1,
125                            &backend,
126                            *e.ciphersuite(),
127                            None,
128                            #[cfg(not(target_family = "wasm"))]
129                            None,
130                        )
131                        .unwrap()
132                    })
133                },
134            )
135            .await;
136            assert!(result.is_err());
137        })
138        .await
139    }
140}