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