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