interop/clients/corecrypto/
ffi.rs

1// Wire
2// Copyright (C) 2022 Wire Swiss GmbH
3
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see http://www.gnu.org/licenses/.
16
17use 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    // We will create a NamedTempFile which we will immediately use to get the path.
32    // Once we get the path, we don't need to read from it anymore, but the compiler
33    // will rightly point out that the value we store in the CoreCryptoFfiClient
34    // struct is never read. However, we need to store the NamedTempFile instance in
35    // the struct, so that the temporary file is not cleaned up prematurely.
36    // So mark the field as unused to silence the compiler here.
37    _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}