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 Ok(())
77 }
78}
79
80#[async_trait::async_trait(?Send)]
81impl EmulatedMlsClient for CoreCryptoFfiClient {
82 async fn get_keypackage(&self) -> Result<Vec<u8>> {
83 let ciphersuite = CIPHERSUITE_IN_USE.into();
84 let credential_type = CredentialType::Basic;
85 let extractor = TransactionHelper::new(move |context| async move {
86 Ok(context
87 .client_keypackages(ciphersuite, credential_type, 1)
88 .await?
89 .pop()
90 .unwrap())
91 });
92 self.cc.transaction(extractor.clone()).await?;
93 let kp = extractor.into_return_value();
94 Ok(kp)
95 }
96
97 async fn add_client(&self, conversation_id: &[u8], kp: &[u8]) -> Result<()> {
98 let conversation_id = conversation_id.to_vec();
99 if !self.cc.conversation_exists(&conversation_id).await? {
100 let cfg = core_crypto_ffi::ConversationConfiguration {
101 ciphersuite: Some(CIPHERSUITE_IN_USE.into()),
102 external_senders: Default::default(),
103 custom: Default::default(),
104 };
105 let conversation_id = conversation_id.clone();
106 self.cc
107 .transaction(TransactionHelper::new(async move |context| {
108 let conversation_id = conversation_id.clone();
109 context
110 .create_conversation(&conversation_id, CredentialType::Basic, cfg)
111 .await?;
112 Ok(())
113 }))
114 .await?;
115 }
116
117 let key_packages = vec![kp.to_vec()];
118 let extractor = TransactionHelper::new(async move |context| {
119 context
120 .add_clients_to_conversation(&conversation_id, key_packages)
121 .await
122 });
123 self.cc.transaction(extractor.clone()).await?;
124 Ok(())
125 }
126
127 async fn kick_client(&self, conversation_id: &[u8], client_id: &[u8]) -> Result<()> {
128 let client_id = ClientId::from(core_crypto::prelude::ClientId::from(client_id));
129 let conversation_id = conversation_id.to_vec();
130 let extractor = TransactionHelper::new(move |context| async move {
131 context
132 .remove_clients_from_conversation(&conversation_id, vec![client_id])
133 .await
134 });
135 self.cc.transaction(extractor.clone()).await?;
136 Ok(())
137 }
138
139 async fn process_welcome(&self, welcome: &[u8]) -> Result<Vec<u8>> {
140 let cfg = CustomConfiguration {
141 key_rotation_span: None,
142 wire_policy: None,
143 };
144 let welcome = welcome.to_vec();
145 let extractor =
146 TransactionHelper::new(move |context| async move { context.process_welcome_message(welcome, cfg).await });
147 self.cc.transaction(extractor.clone()).await?;
148 Ok(extractor.into_return_value().id)
149 }
150
151 async fn encrypt_message(&self, conversation_id: &[u8], message: &[u8]) -> Result<Vec<u8>> {
152 let conversation_id = conversation_id.to_vec();
153 let message = message.to_vec();
154 let extractor =
155 TransactionHelper::new(
156 move |context| async move { context.encrypt_message(&conversation_id, message).await },
157 );
158 self.cc.transaction(extractor.clone()).await?;
159 Ok(extractor.into_return_value())
160 }
161
162 async fn decrypt_message(&self, conversation_id: &[u8], message: &[u8]) -> Result<Option<Vec<u8>>> {
163 let conversation_id = conversation_id.to_vec();
164 let message = message.to_vec();
165 let extractor =
166 TransactionHelper::new(
167 move |context| async move { context.decrypt_message(&conversation_id, message).await },
168 );
169 self.cc.transaction(extractor.clone()).await?;
170 Ok(extractor.into_return_value().message)
171 }
172}
173
174#[cfg(feature = "proteus")]
175#[async_trait::async_trait(?Send)]
176impl crate::clients::EmulatedProteusClient for CoreCryptoFfiClient {
177 async fn init(&mut self) -> Result<()> {
178 self.cc
179 .transaction(TransactionHelper::new(move |context| async move {
180 context.proteus_init().await
181 }))
182 .await?;
183 Ok(())
184 }
185
186 async fn get_prekey(&self) -> Result<Vec<u8>> {
187 let prekey_last_id = self.prekey_last_id.get() + 1;
188 self.prekey_last_id.replace(prekey_last_id);
189 let extractor =
190 TransactionHelper::new(move |context| async move { context.proteus_new_prekey(prekey_last_id).await });
191 self.cc.transaction(extractor.clone()).await?;
192 Ok(extractor.into_return_value())
193 }
194
195 async fn session_from_prekey(&self, session_id: &str, prekey: &[u8]) -> Result<()> {
196 let session_id = session_id.to_string();
197 let prekey = prekey.to_vec();
198 self.cc
199 .transaction(TransactionHelper::new(move |context| async move {
200 context.proteus_session_from_prekey(session_id, prekey).await
201 }))
202 .await?;
203 Ok(())
204 }
205
206 async fn session_from_message(&self, session_id: &str, message: &[u8]) -> Result<Vec<u8>> {
207 let session_id = session_id.to_string();
208 let message = message.to_vec();
209 let extractor = TransactionHelper::new(move |context| async move {
210 context.proteus_session_from_message(session_id, message).await
211 });
212 self.cc.transaction(extractor.clone()).await?;
213 Ok(extractor.into_return_value())
214 }
215
216 async fn encrypt(&self, session_id: &str, plaintext: &[u8]) -> Result<Vec<u8>> {
217 let session_id = session_id.to_string();
218 let plaintext = plaintext.to_vec();
219 let extractor =
220 TransactionHelper::new(move |context| async move { context.proteus_encrypt(session_id, plaintext).await });
221 self.cc.transaction(extractor.clone()).await?;
222 Ok(extractor.into_return_value())
223 }
224
225 async fn decrypt(&self, session_id: &str, ciphertext: &[u8]) -> Result<Vec<u8>> {
226 let session_id = session_id.to_string();
227 let ciphertext = ciphertext.to_vec();
228 let extractor =
229 TransactionHelper::new(move |context| async move { context.proteus_decrypt(session_id, ciphertext).await });
230 self.cc.transaction(extractor.clone()).await?;
231 Ok(extractor.into_return_value())
232 }
233
234 async fn fingerprint(&self) -> Result<String> {
235 self.cc.proteus_fingerprint().await.map_err(Into::into)
236 }
237}