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