Skip to main content

wire_e2e_identity/acquisition/
serialization.rs

1use std::sync::Arc;
2
3use jwt_simple::prelude::Jwk;
4use rusty_jwt_tools::prelude::{ClientId, Pem};
5use uuid::Uuid;
6
7use super::{Result, X509CredentialAcquisition, X509CredentialConfiguration, states};
8use crate::pki_env::PkiEnvironment;
9
10#[derive(serde::Serialize, serde::Deserialize)]
11#[serde(remote = "ClientId")]
12pub struct ClientIdDef {
13    /// base64url encoded UUIDv4 unique user identifier
14    pub user_id: Uuid,
15    /// the device id assigned by the backend in hex
16    pub device_id: u64,
17    /// the backend domain of the client
18    pub domain: String,
19}
20
21#[derive(serde::Deserialize, serde::Serialize)]
22struct X509CredentialAcquisitionSerialisationHelper<T: std::fmt::Debug> {
23    config: X509CredentialConfiguration,
24    sign_kp: Pem,
25    acme_kp: Pem,
26    acme_jwk: Jwk,
27    data: T,
28}
29
30impl X509CredentialAcquisition<states::DpopChallengeCompleted> {
31    pub fn deserialize(pki_env: Arc<PkiEnvironment>, bytes: &[u8]) -> Result<Self> {
32        let helper: X509CredentialAcquisitionSerialisationHelper<states::DpopChallengeCompleted> =
33            serde_json::from_slice(bytes)?;
34
35        Ok(Self {
36            pki_env,
37            config: helper.config,
38            sign_kp: helper.sign_kp,
39            acme_kp: helper.acme_kp,
40            acme_jwk: helper.acme_jwk,
41            data: helper.data,
42        })
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use core_crypto_keystore::Database;
49    use rusty_jwt_tools::prelude::{HashAlgorithm, JwsAlgorithm};
50
51    use super::*;
52    use crate::{
53        acme::{AcmeAccount, AcmeChallenge, AcmeOrder},
54        pki_env::hooks::{HttpHeader, HttpMethod, HttpResponse, PkiEnvironmentHooks, PkiEnvironmentHooksError},
55    };
56
57    #[derive(Debug)]
58    struct UnusedPkiEnvironmentHooks;
59
60    #[async_trait::async_trait]
61    impl PkiEnvironmentHooks for UnusedPkiEnvironmentHooks {
62        async fn http_request(
63            &self,
64            _method: HttpMethod,
65            _url: String,
66            _headers: Vec<HttpHeader>,
67            _body: Vec<u8>,
68        ) -> std::result::Result<HttpResponse, PkiEnvironmentHooksError> {
69            unreachable!("serialization round-trip should not perform HTTP requests")
70        }
71
72        async fn authenticate(
73            &self,
74            _idp: String,
75            _key_auth: String,
76            _acme_aud: String,
77            _acquisition_snapshot: Vec<u8>,
78        ) -> std::result::Result<String, PkiEnvironmentHooksError> {
79            unreachable!("serialization round-trip should not authenticate")
80        }
81
82        async fn get_backend_nonce(&self) -> std::result::Result<String, PkiEnvironmentHooksError> {
83            unreachable!("serialization round-trip should not request backend nonces")
84        }
85
86        async fn fetch_backend_access_token(
87            &self,
88            _dpop: String,
89        ) -> std::result::Result<String, PkiEnvironmentHooksError> {
90            unreachable!("serialization round-trip should not fetch backend access tokens")
91        }
92    }
93
94    #[tokio::test]
95    async fn can_serialize_and_deserialize_dpop_challenge_completed_acquisition() {
96        let pki_env = Arc::new(
97            PkiEnvironment::new(Arc::new(UnusedPkiEnvironmentHooks), Database::open_in_memory().unwrap())
98                .await
99                .unwrap(),
100        );
101        let client_id = ClientId::try_new(Uuid::new_v4().to_string(), 1, "wire.example").unwrap();
102        let config = X509CredentialConfiguration {
103            acme_directory_url: "acme.example".into(),
104            sign_alg: JwsAlgorithm::P256,
105            hash_alg: HashAlgorithm::SHA256,
106            display_name: "Alice".into(),
107            client_id,
108            handle: "alice".into(),
109            domain: "wire.example".into(),
110            team: Some("team".into()),
111            validity_period: std::time::Duration::from_secs(3600),
112        };
113        let initialized = X509CredentialAcquisition::try_new(pki_env.clone(), config).unwrap();
114        let acquisition = X509CredentialAcquisition::<states::DpopChallengeCompleted> {
115            pki_env: initialized.pki_env,
116            config: initialized.config,
117            sign_kp: initialized.sign_kp,
118            acme_kp: initialized.acme_kp,
119            acme_jwk: initialized.acme_jwk,
120            data: states::DpopChallengeCompleted {
121                nonce: "acme-nonce".into(),
122                acme_account: AcmeAccount::default(),
123                order: AcmeOrder::default(),
124                oidc_challenge: AcmeChallenge::new_user(),
125            },
126        };
127
128        let serialized = serde_json::to_vec(&acquisition).unwrap();
129        let deserialized =
130            X509CredentialAcquisition::<states::DpopChallengeCompleted>::deserialize(pki_env, &serialized).unwrap();
131
132        assert_eq!(
133            serde_json::to_value(&acquisition).unwrap(),
134            serde_json::to_value(&deserialized).unwrap()
135        );
136    }
137}