interop/clients/corecrypto/
ffi.rs

1use 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    // We will create a NamedTempFile which we will immediately use to get the path.
16    // Once we get the path, we don't need to read from it anymore, but the compiler
17    // will rightly point out that the value we store in the CoreCryptoFfiClient
18    // struct is never read. However, we need to store the NamedTempFile instance in
19    // the struct, so that the temporary file is not cleaned up prematurely.
20    // So mark the field as unused to silence the compiler here.
21    _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}