interop/clients/corecrypto/
native.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 color_eyre::eyre::Result;
18use std::cell::Cell;
19use std::sync::Arc;
20use tls_codec::Serialize;
21
22use core_crypto::prelude::*;
23
24use crate::util::MlsTransportSuccessProvider;
25use crate::{
26    CIPHERSUITE_IN_USE,
27    clients::{EmulatedClient, EmulatedClientProtocol, EmulatedClientType, EmulatedMlsClient},
28};
29
30#[derive(Debug)]
31pub(crate) struct CoreCryptoNativeClient {
32    cc: CoreCrypto,
33    client_id: Vec<u8>,
34    #[cfg(feature = "proteus")]
35    prekey_last_id: Cell<u16>,
36}
37
38#[allow(dead_code)]
39impl CoreCryptoNativeClient {
40    pub(crate) async fn new() -> Result<Self> {
41        Self::internal_new(false).await
42    }
43
44    async fn internal_new(deferred: bool) -> Result<Self> {
45        let client_id = uuid::Uuid::new_v4();
46
47        let ciphersuites = vec![CIPHERSUITE_IN_USE.into()];
48        let cid = if !deferred {
49            Some(client_id.as_hyphenated().to_string().as_bytes().into())
50        } else {
51            None
52        };
53        let configuration =
54            MlsCentralConfiguration::try_new("whatever".into(), "test".into(), cid, ciphersuites, None, Some(100))?;
55
56        let cc = CoreCrypto::from(MlsCentral::try_new_in_memory(configuration).await?);
57
58        cc.provide_transport(Arc::new(MlsTransportSuccessProvider::default()))
59            .await;
60
61        Ok(Self {
62            cc,
63            client_id: client_id.into_bytes().into(),
64            #[cfg(feature = "proteus")]
65            prekey_last_id: Cell::new(0),
66        })
67    }
68}
69
70#[async_trait::async_trait(?Send)]
71impl EmulatedClient for CoreCryptoNativeClient {
72    fn client_name(&self) -> &str {
73        "CoreCrypto::native"
74    }
75
76    fn client_type(&self) -> EmulatedClientType {
77        EmulatedClientType::Native
78    }
79
80    fn client_id(&self) -> &[u8] {
81        self.client_id.as_slice()
82    }
83
84    fn client_protocol(&self) -> EmulatedClientProtocol {
85        EmulatedClientProtocol::MLS | EmulatedClientProtocol::PROTEUS
86    }
87
88    async fn wipe(mut self) -> Result<()> {
89        drop(self.cc);
90        Ok(())
91    }
92}
93
94#[async_trait::async_trait(?Send)]
95impl EmulatedMlsClient for CoreCryptoNativeClient {
96    async fn get_keypackage(&self) -> Result<Vec<u8>> {
97        let transaction = self.cc.new_transaction().await?;
98        let start = std::time::Instant::now();
99        let kp = transaction
100            .get_or_create_client_keypackages(CIPHERSUITE_IN_USE.into(), MlsCredentialType::Basic, 1)
101            .await?
102            .pop()
103            .unwrap();
104
105        log::info!(
106            "KP Init Key [took {}ms]: Client {} [{}] - {}",
107            start.elapsed().as_millis(),
108            self.client_name(),
109            hex::encode(&self.client_id),
110            hex::encode(kp.hpke_init_key()),
111        );
112        transaction.finish().await?;
113
114        Ok(kp.tls_serialize_detached()?)
115    }
116
117    async fn add_client(&self, conversation_id: &[u8], kp: &[u8]) -> Result<()> {
118        let conversation_id = conversation_id.to_vec();
119        let transaction = self.cc.new_transaction().await?;
120        if !transaction.conversation_exists(&conversation_id).await? {
121            let config = MlsConversationConfiguration {
122                ciphersuite: CIPHERSUITE_IN_USE.into(),
123                ..Default::default()
124            };
125            transaction
126                .new_conversation(&conversation_id, MlsCredentialType::Basic, config)
127                .await?;
128        }
129
130        use tls_codec::Deserialize as _;
131
132        let kp = KeyPackageIn::tls_deserialize(&mut &kp[..])?;
133        transaction
134            .conversation(&conversation_id)
135            .await?
136            .add_members(vec![kp])
137            .await?;
138        transaction.finish().await?;
139
140        Ok(())
141    }
142
143    async fn kick_client(&self, conversation_id: &[u8], client_id: &[u8]) -> Result<()> {
144        let transaction = self.cc.new_transaction().await?;
145        transaction
146            .conversation(&conversation_id.to_owned())
147            .await?
148            .remove_members(&[client_id.to_owned().into()])
149            .await?;
150        transaction.finish().await?;
151
152        Ok(())
153    }
154
155    async fn process_welcome(&self, welcome: &[u8]) -> Result<Vec<u8>> {
156        let transaction = self.cc.new_transaction().await?;
157
158        let result = transaction
159            .process_raw_welcome_message(welcome.into(), MlsCustomConfiguration::default())
160            .await?
161            .id;
162        transaction.finish().await?;
163        Ok(result)
164    }
165
166    async fn encrypt_message(&self, conversation_id: &[u8], message: &[u8]) -> Result<Vec<u8>> {
167        let transaction = self.cc.new_transaction().await?;
168        let result = transaction
169            .conversation(&conversation_id.to_vec())
170            .await?
171            .encrypt_message(message)
172            .await?;
173        transaction.finish().await?;
174        Ok(result)
175    }
176
177    async fn decrypt_message(&self, conversation_id: &[u8], message: &[u8]) -> Result<Option<Vec<u8>>> {
178        let transaction = self.cc.new_transaction().await?;
179        let result = transaction
180            .conversation(&conversation_id.to_vec())
181            .await?
182            .decrypt_message(message)
183            .await?
184            .app_msg;
185        transaction.finish().await?;
186        Ok(result)
187    }
188}
189
190#[cfg(feature = "proteus")]
191#[async_trait::async_trait(?Send)]
192impl crate::clients::EmulatedProteusClient for CoreCryptoNativeClient {
193    async fn init(&mut self) -> Result<()> {
194        let transaction = self.cc.new_transaction().await?;
195        transaction.proteus_init().await?;
196        Ok(transaction.finish().await?)
197    }
198
199    async fn get_prekey(&self) -> Result<Vec<u8>> {
200        let transaction = self.cc.new_transaction().await?;
201        let prekey_last_id = self.prekey_last_id.get() + 1;
202        self.prekey_last_id.replace(prekey_last_id);
203        let result = transaction.proteus_new_prekey(prekey_last_id).await?;
204        transaction.finish().await?;
205        Ok(result)
206    }
207
208    async fn session_from_prekey(&self, session_id: &str, prekey: &[u8]) -> Result<()> {
209        let transaction = self.cc.new_transaction().await?;
210        let _ = transaction.proteus_session_from_prekey(session_id, prekey).await?;
211        transaction.finish().await?;
212        Ok(())
213    }
214
215    async fn session_from_message(&self, session_id: &str, message: &[u8]) -> Result<Vec<u8>> {
216        let transaction = self.cc.new_transaction().await?;
217        let (_, ret) = transaction.proteus_session_from_message(session_id, message).await?;
218        transaction.finish().await?;
219        Ok(ret)
220    }
221
222    async fn encrypt(&self, session_id: &str, plaintext: &[u8]) -> Result<Vec<u8>> {
223        let transaction = self.cc.new_transaction().await?;
224        let result = transaction.proteus_encrypt(session_id, plaintext).await?;
225        transaction.finish().await?;
226        Ok(result)
227    }
228
229    async fn decrypt(&self, session_id: &str, ciphertext: &[u8]) -> Result<Vec<u8>> {
230        let transaction = self.cc.new_transaction().await?;
231        let result = transaction.proteus_decrypt(session_id, ciphertext).await?;
232        transaction.finish().await?;
233        Ok(result)
234    }
235
236    async fn fingerprint(&self) -> Result<String> {
237        Ok(self.cc.proteus_fingerprint().await?)
238    }
239}