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        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}