interop/clients/corecrypto/
ffi.rs1use crate::{
18 CIPHERSUITE_IN_USE,
19 clients::{EmulatedClient, EmulatedClientProtocol, EmulatedClientType, EmulatedMlsClient},
20};
21use color_eyre::eyre::Result;
22use core_crypto_ffi::{ClientId, CoreCrypto, CustomConfiguration, MlsCredentialType, context::TransactionHelper};
23use std::cell::Cell;
24use std::sync::Arc;
25use tempfile::NamedTempFile;
26
27#[derive(Debug)]
28pub(crate) struct CoreCryptoFfiClient {
29 cc: CoreCrypto,
30 client_id: Vec<u8>,
31 _temp_file: NamedTempFile,
38 #[cfg(feature = "proteus")]
39 prekey_last_id: Cell<u16>,
40}
41
42impl CoreCryptoFfiClient {
43 pub(crate) async fn new() -> Result<CoreCryptoFfiClient> {
44 let client_id = uuid::Uuid::new_v4();
45 let client_id_bytes: Vec<u8> = client_id.as_hyphenated().to_string().as_bytes().into();
46 let client_id = ClientId::from(core_crypto::prelude::ClientId::from(&client_id_bytes[..]));
47 let ciphersuite = CIPHERSUITE_IN_USE;
48 let temp_file = NamedTempFile::with_prefix("interop-ffi-keystore-")?;
49
50 let cc = CoreCrypto::new(
51 temp_file.path().to_string_lossy().into_owned(),
52 "key".to_string(),
53 Some(client_id),
54 Some(vec![ciphersuite].into()),
55 None,
56 )
57 .await?;
58
59 cc.provide_transport(Arc::new(crate::MlsTransportSuccessProvider::default()))
60 .await?;
61
62 Ok(Self {
63 cc,
64 _temp_file: temp_file,
65 client_id: client_id_bytes,
66 #[cfg(feature = "proteus")]
67 prekey_last_id: Cell::new(0),
68 })
69 }
70}
71
72#[async_trait::async_trait(?Send)]
73impl EmulatedClient for CoreCryptoFfiClient {
74 fn client_name(&self) -> &str {
75 "CoreCrypto::ffi"
76 }
77
78 fn client_type(&self) -> EmulatedClientType {
79 EmulatedClientType::Native
80 }
81
82 fn client_id(&self) -> &[u8] {
83 self.client_id.as_slice()
84 }
85
86 fn client_protocol(&self) -> EmulatedClientProtocol {
87 EmulatedClientProtocol::MLS | EmulatedClientProtocol::PROTEUS
88 }
89
90 async fn wipe(mut self) -> Result<()> {
91 drop(self.cc);
92 Ok(())
93 }
94}
95
96#[async_trait::async_trait(?Send)]
97impl EmulatedMlsClient for CoreCryptoFfiClient {
98 async fn get_keypackage(&self) -> Result<Vec<u8>> {
99 let ciphersuite = CIPHERSUITE_IN_USE.into();
100 let credential_type = MlsCredentialType::Basic;
101 let extractor = TransactionHelper::new(move |context| async move {
102 Ok(context
103 .client_keypackages(ciphersuite, credential_type, 1)
104 .await?
105 .pop()
106 .unwrap())
107 });
108 self.cc.transaction(extractor.clone()).await?;
109 let kp = extractor.into_return_value();
110 Ok(kp)
111 }
112
113 async fn add_client(&self, conversation_id: &[u8], kp: &[u8]) -> Result<()> {
114 if !self.cc.conversation_exists(conversation_id.to_vec()).await? {
115 let cfg = core_crypto_ffi::ConversationConfiguration {
116 ciphersuite: CIPHERSUITE_IN_USE.into(),
117 external_senders: vec![],
118 custom: CustomConfiguration {
119 key_rotation_span: None,
120 wire_policy: None,
121 },
122 };
123 let conversation_id = conversation_id.to_vec();
124 self.cc
125 .transaction(TransactionHelper::new(move |context| async move {
126 context
127 .create_conversation(conversation_id, MlsCredentialType::Basic, cfg)
128 .await?;
129 Ok(())
130 }))
131 .await?;
132 }
133
134 let conversation_id = conversation_id.to_vec();
135 let key_packages = vec![kp.to_vec()];
136 let extractor = TransactionHelper::new(move |context| async move {
137 context.add_clients_to_conversation(conversation_id, key_packages).await
138 });
139 self.cc.transaction(extractor.clone()).await?;
140 Ok(())
141 }
142
143 async fn kick_client(&self, conversation_id: &[u8], client_id: &[u8]) -> Result<()> {
144 let client_id = ClientId::from(core_crypto::prelude::ClientId::from(client_id));
145 let conversation_id = conversation_id.to_vec();
146 let extractor = TransactionHelper::new(move |context| async move {
147 context
148 .remove_clients_from_conversation(conversation_id, vec![client_id])
149 .await
150 });
151 self.cc.transaction(extractor.clone()).await?;
152 Ok(())
153 }
154
155 async fn process_welcome(&self, welcome: &[u8]) -> Result<Vec<u8>> {
156 let cfg = CustomConfiguration {
157 key_rotation_span: None,
158 wire_policy: None,
159 };
160 let welcome = welcome.to_vec();
161 let extractor =
162 TransactionHelper::new(move |context| async move { context.process_welcome_message(welcome, cfg).await });
163 self.cc.transaction(extractor.clone()).await?;
164 Ok(extractor.into_return_value().id)
165 }
166
167 async fn encrypt_message(&self, conversation_id: &[u8], message: &[u8]) -> Result<Vec<u8>> {
168 let conversation_id = conversation_id.to_vec();
169 let message = message.to_vec();
170 let extractor =
171 TransactionHelper::new(
172 move |context| async move { context.encrypt_message(conversation_id, message).await },
173 );
174 self.cc.transaction(extractor.clone()).await?;
175 Ok(extractor.into_return_value())
176 }
177
178 async fn decrypt_message(&self, conversation_id: &[u8], message: &[u8]) -> Result<Option<Vec<u8>>> {
179 let conversation_id = conversation_id.to_vec();
180 let message = message.to_vec();
181 let extractor =
182 TransactionHelper::new(
183 move |context| async move { context.decrypt_message(conversation_id, message).await },
184 );
185 self.cc.transaction(extractor.clone()).await?;
186 Ok(extractor.into_return_value().message)
187 }
188}
189
190#[cfg(feature = "proteus")]
191#[async_trait::async_trait(?Send)]
192impl crate::clients::EmulatedProteusClient for CoreCryptoFfiClient {
193 async fn init(&mut self) -> Result<()> {
194 self.cc
195 .transaction(TransactionHelper::new(move |context| async move {
196 context.proteus_init().await
197 }))
198 .await?;
199 Ok(())
200 }
201
202 async fn get_prekey(&self) -> Result<Vec<u8>> {
203 let prekey_last_id = self.prekey_last_id.get() + 1;
204 self.prekey_last_id.replace(prekey_last_id);
205 let extractor =
206 TransactionHelper::new(move |context| async move { context.proteus_new_prekey(prekey_last_id).await });
207 self.cc.transaction(extractor.clone()).await?;
208 Ok(extractor.into_return_value())
209 }
210
211 async fn session_from_prekey(&self, session_id: &str, prekey: &[u8]) -> Result<()> {
212 let session_id = session_id.to_string();
213 let prekey = prekey.to_vec();
214 self.cc
215 .transaction(TransactionHelper::new(move |context| async move {
216 context.proteus_session_from_prekey(session_id, prekey).await
217 }))
218 .await?;
219 Ok(())
220 }
221
222 async fn session_from_message(&self, session_id: &str, message: &[u8]) -> Result<Vec<u8>> {
223 let session_id = session_id.to_string();
224 let message = message.to_vec();
225 let extractor = TransactionHelper::new(move |context| async move {
226 context.proteus_session_from_message(session_id, message).await
227 });
228 self.cc.transaction(extractor.clone()).await?;
229 Ok(extractor.into_return_value())
230 }
231
232 async fn encrypt(&self, session_id: &str, plaintext: &[u8]) -> Result<Vec<u8>> {
233 let session_id = session_id.to_string();
234 let plaintext = plaintext.to_vec();
235 let extractor =
236 TransactionHelper::new(move |context| async move { context.proteus_encrypt(session_id, plaintext).await });
237 self.cc.transaction(extractor.clone()).await?;
238 Ok(extractor.into_return_value())
239 }
240
241 async fn decrypt(&self, session_id: &str, ciphertext: &[u8]) -> Result<Vec<u8>> {
242 let session_id = session_id.to_string();
243 let ciphertext = ciphertext.to_vec();
244 let extractor =
245 TransactionHelper::new(move |context| async move { context.proteus_decrypt(session_id, ciphertext).await });
246 self.cc.transaction(extractor.clone()).await?;
247 Ok(extractor.into_return_value())
248 }
249
250 async fn fingerprint(&self) -> Result<String> {
251 self.cc.proteus_fingerprint().await.map_err(Into::into)
252 }
253}