core_crypto_ffi/generic/
mod.rs

1#![allow(deprecated)]
2// Wire
3// Copyright (C) 2022 Wire Swiss GmbH
4
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see http://www.gnu.org/licenses/.
17
18use log::kv::{Key, Value, Visitor};
19use log::{kv, Level, LevelFilter, Metadata, Record};
20use log_reload::ReloadLog;
21use std::collections::{BTreeMap, HashMap};
22use std::ops::{Deref, DerefMut};
23use std::sync::{Arc, LazyLock, Once};
24use tls_codec::{Deserialize, Serialize};
25
26use crate::UniffiCustomTypeConverter;
27pub use core_crypto::prelude::ConversationId;
28use core_crypto::{
29    prelude::{
30        ClientIdentifier, CryptoError, E2eIdentityError, EntropySeed, KeyPackageIn, KeyPackageRef,
31        MlsBufferedConversationDecryptMessage, MlsCentral, MlsCentralConfiguration, MlsCiphersuite, MlsCommitBundle,
32        MlsConversationConfiguration, MlsConversationCreationMessage, MlsConversationDecryptMessage,
33        MlsConversationInitBundle, MlsCustomConfiguration, MlsGroupInfoBundle, MlsProposalBundle, MlsRotateBundle,
34        VerifiableGroupInfo,
35    },
36    CryptoResult,
37};
38
39use self::context::CoreCryptoContext;
40
41use crate::proteus_impl;
42
43pub mod context;
44
45#[allow(dead_code)]
46pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION");
47
48#[uniffi::export]
49pub fn version() -> String {
50    VERSION.to_string()
51}
52
53#[derive(uniffi::Record)]
54/// Metadata describing the conditions of the build of this software.
55pub struct BuildMetadata {
56    /// Build Timestamp
57    pub timestamp: String,
58    /// Whether this build was in Debug mode (true) or Release mode (false)
59    pub cargo_debug: String,
60    /// Features enabled for this build
61    pub cargo_features: String,
62    /// Optimization level
63    pub opt_level: String,
64    /// Build target triple
65    pub target_triple: String,
66    /// Git branch
67    pub git_branch: String,
68    /// Output of `git describe`
69    pub git_describe: String,
70    /// Hash of current git commit
71    pub git_sha: String,
72    /// `true` when the source code differed from the commit at the most recent git hash
73    pub git_dirty: String,
74}
75
76#[uniffi::export]
77pub fn build_metadata() -> BuildMetadata {
78    BuildMetadata {
79        timestamp: core_crypto::BUILD_METADATA.timestamp.to_string(),
80        cargo_debug: core_crypto::BUILD_METADATA.cargo_debug.to_string(),
81        cargo_features: core_crypto::BUILD_METADATA.cargo_features.to_string(),
82        opt_level: core_crypto::BUILD_METADATA.opt_level.to_string(),
83        target_triple: core_crypto::BUILD_METADATA.target_triple.to_string(),
84        git_branch: core_crypto::BUILD_METADATA.git_branch.to_string(),
85        git_describe: core_crypto::BUILD_METADATA.git_describe.to_string(),
86        git_sha: core_crypto::BUILD_METADATA.git_sha.to_string(),
87        git_dirty: core_crypto::BUILD_METADATA.git_dirty.to_string(),
88    }
89}
90
91#[derive(Debug, thiserror::Error, uniffi::Error)]
92pub enum MlsError {
93    #[error("Conversation already exists")]
94    ConversationAlreadyExists(core_crypto::prelude::ConversationId),
95    #[error("We already decrypted this message once")]
96    DuplicateMessage,
97    #[error("Incoming message is for a future epoch. We will buffer it until the commit for that epoch arrives")]
98    BufferedFutureMessage,
99    #[error("Incoming message is from an epoch too far in the future to buffer.")]
100    WrongEpoch,
101    #[error("The epoch in which message was encrypted is older than allowed")]
102    MessageEpochTooOld,
103    #[error("Tried to decrypt a commit created by self which is likely to have been replayed by the DS")]
104    SelfCommitIgnored,
105    #[error(
106        "You tried to join with an external commit but did not merge it yet. We will reapply this message for you when you merge your external commit"
107    )]
108    UnmergedPendingGroup,
109    #[error("The received proposal is deemed stale and is from an older epoch.")]
110    StaleProposal,
111    #[error("The received commit is deemed stale and is from an older epoch.")]
112    StaleCommit,
113    /// This happens when the DS cannot flag KeyPackages as claimed or not. It this scenario, a client
114    /// requests their old KeyPackages to be deleted but one has already been claimed by another client to create a Welcome.
115    /// In that case the only solution is that the client receiving such a Welcome tries to join the group
116    /// with an External Commit instead
117    #[error("Although this Welcome seems valid, the local KeyPackage it references has already been deleted locally. Join this group with an external commit")]
118    OrphanWelcome,
119    #[error("{0}")]
120    Other(String),
121}
122
123impl From<core_crypto::MlsError> for MlsError {
124    #[inline]
125    fn from(e: core_crypto::MlsError) -> Self {
126        Self::Other(e.to_string())
127    }
128}
129
130#[cfg(feature = "proteus")]
131#[derive(Debug, thiserror::Error, uniffi::Error)]
132pub enum ProteusError {
133    #[error("The requested session was not found")]
134    SessionNotFound,
135    #[error("We already decrypted this message once")]
136    DuplicateMessage,
137    #[error("The remote identity has changed")]
138    RemoteIdentityChanged,
139    #[error("Another Proteus error occurred but the details are probably irrelevant to clients")]
140    Other(u16),
141}
142
143#[cfg(feature = "proteus")]
144impl ProteusError {
145    pub fn from_error_code(code: u16) -> Self {
146        match code {
147            102 => Self::SessionNotFound,
148            204 => Self::RemoteIdentityChanged,
149            209 => Self::DuplicateMessage,
150            _ => Self::Other(code),
151        }
152    }
153
154    pub fn error_code(&self) -> u16 {
155        match self {
156            Self::SessionNotFound => 102,
157            Self::RemoteIdentityChanged => 204,
158            Self::DuplicateMessage => 209,
159            Self::Other(code) => *code,
160        }
161    }
162}
163
164#[cfg(feature = "proteus")]
165impl From<core_crypto::ProteusError> for ProteusError {
166    fn from(value: core_crypto::ProteusError) -> Self {
167        type SessionError = proteus_wasm::session::Error<core_crypto_keystore::CryptoKeystoreError>;
168        match value {
169            core_crypto::ProteusError::ProteusSessionError(SessionError::InternalError(
170                proteus_wasm::internal::types::InternalError::NoSessionForTag,
171            )) => Self::SessionNotFound,
172            core_crypto::ProteusError::ProteusSessionError(SessionError::DuplicateMessage) => Self::DuplicateMessage,
173            core_crypto::ProteusError::ProteusSessionError(SessionError::RemoteIdentityChanged) => {
174                Self::RemoteIdentityChanged
175            }
176            _ => Self::Other(value.error_code().unwrap_or_default()),
177        }
178    }
179}
180
181#[derive(Debug, thiserror::Error, uniffi::Error)]
182pub enum CoreCryptoError {
183    #[error(transparent)]
184    Mls(#[from] MlsError),
185    #[cfg(feature = "proteus")]
186    #[error(transparent)]
187    Proteus(#[from] ProteusError),
188    #[error("End to end identity error: {0}")]
189    E2eiError(String),
190    #[error("error from client: {0}")]
191    ClientError(String),
192}
193
194// This implementation is intended to be temporary; we're going to be completely restructuring the way we handle
195// errors in `core-crypto` soon. We can replace this with better error patterns when we do.
196//
197// Certain error mappings could apply to both MLS and Proteus. In all such cases, we map them to the MLS variant.
198// When we redesign the errors in `core-crypto`, these ambiguities should disappear anyway.
199impl From<CryptoError> for CoreCryptoError {
200    fn from(value: CryptoError) -> Self {
201        #[cfg(feature = "proteus")]
202        if let Some(error_code) = value.proteus_error_code() {
203            if error_code != 0 {
204                // that check _should_ be redundant, but just in case
205                return ProteusError::from_error_code(error_code).into();
206            }
207        }
208
209        match value {
210            CryptoError::ConversationAlreadyExists(id) => MlsError::ConversationAlreadyExists(id).into(),
211            CryptoError::DuplicateMessage => MlsError::DuplicateMessage.into(),
212            CryptoError::BufferedFutureMessage { .. } => MlsError::BufferedFutureMessage.into(),
213            CryptoError::WrongEpoch => MlsError::WrongEpoch.into(),
214            CryptoError::MessageEpochTooOld => MlsError::MessageEpochTooOld.into(),
215            CryptoError::SelfCommitIgnored => MlsError::SelfCommitIgnored.into(),
216            CryptoError::UnmergedPendingGroup => MlsError::UnmergedPendingGroup.into(),
217            CryptoError::StaleProposal => MlsError::StaleProposal.into(),
218            CryptoError::StaleCommit => MlsError::StaleCommit.into(),
219            CryptoError::OrphanWelcome => MlsError::OrphanWelcome.into(),
220            CryptoError::E2eiError(e) => Self::E2eiError(e.to_string()),
221            _ => MlsError::Other(value.to_string()).into(),
222        }
223    }
224}
225
226impl From<E2eIdentityError> for CoreCryptoError {
227    fn from(e: E2eIdentityError) -> Self {
228        Self::E2eiError(e.to_string())
229    }
230}
231
232impl From<uniffi::UnexpectedUniFFICallbackError> for CoreCryptoError {
233    fn from(value: uniffi::UnexpectedUniFFICallbackError) -> Self {
234        Self::ClientError(value.reason)
235    }
236}
237
238type CoreCryptoResult<T> = Result<T, CoreCryptoError>;
239
240#[derive(Debug, Clone, Eq, Hash, PartialEq)]
241pub struct ClientId(core_crypto::prelude::ClientId);
242
243uniffi::custom_type!(ClientId, Vec<u8>);
244
245impl UniffiCustomTypeConverter for ClientId {
246    type Builtin = Vec<u8>;
247
248    fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
249        Ok(Self(core_crypto::prelude::ClientId::from(val)))
250    }
251
252    fn from_custom(obj: Self) -> Self::Builtin {
253        obj.0.to_vec()
254    }
255}
256
257#[allow(non_camel_case_types)]
258#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Enum)]
259#[repr(u16)]
260pub enum CiphersuiteName {
261    /// DH KEM x25519 | AES-GCM 128 | SHA2-256 | Ed25519
262    MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 = 0x0001,
263    /// DH KEM P256 | AES-GCM 128 | SHA2-256 | EcDSA P256
264    MLS_128_DHKEMP256_AES128GCM_SHA256_P256 = 0x0002,
265    /// DH KEM x25519 | Chacha20Poly1305 | SHA2-256 | Ed25519
266    MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 = 0x0003,
267    /// DH KEM x448 | AES-GCM 256 | SHA2-512 | Ed448
268    MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448 = 0x0004,
269    /// DH KEM P521 | AES-GCM 256 | SHA2-512 | EcDSA P521
270    MLS_256_DHKEMP521_AES256GCM_SHA512_P521 = 0x0005,
271    /// DH KEM x448 | Chacha20Poly1305 | SHA2-512 | Ed448
272    MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 = 0x0006,
273    /// DH KEM P384 | AES-GCM 256 | SHA2-384 | EcDSA P384
274    MLS_256_DHKEMP384_AES256GCM_SHA384_P384 = 0x0007,
275}
276
277#[derive(Debug, Clone)]
278pub struct Ciphersuite(core_crypto::prelude::CiphersuiteName);
279
280uniffi::custom_type!(Ciphersuite, u16);
281
282impl From<core_crypto::prelude::CiphersuiteName> for Ciphersuite {
283    fn from(cs: core_crypto::prelude::CiphersuiteName) -> Self {
284        Self(cs)
285    }
286}
287
288impl From<Ciphersuite> for core_crypto::prelude::CiphersuiteName {
289    fn from(cs: Ciphersuite) -> Self {
290        cs.0
291    }
292}
293
294impl From<Ciphersuite> for MlsCiphersuite {
295    fn from(cs: Ciphersuite) -> Self {
296        cs.0.into()
297    }
298}
299
300impl UniffiCustomTypeConverter for Ciphersuite {
301    type Builtin = u16;
302
303    fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
304        core_crypto::prelude::CiphersuiteName::try_from(val)
305            .map(Into::into)
306            .map_err(|_| CryptoError::ImplementationError.into())
307    }
308
309    fn from_custom(obj: Self) -> Self::Builtin {
310        (&obj.0).into()
311    }
312}
313
314#[derive(Debug, Default, Clone)]
315pub struct Ciphersuites(Vec<core_crypto::prelude::CiphersuiteName>);
316
317impl From<Vec<core_crypto::prelude::CiphersuiteName>> for Ciphersuites {
318    fn from(cs: Vec<core_crypto::prelude::CiphersuiteName>) -> Self {
319        Self(cs)
320    }
321}
322
323impl From<Ciphersuites> for Vec<core_crypto::prelude::CiphersuiteName> {
324    fn from(cs: Ciphersuites) -> Self {
325        cs.0
326    }
327}
328
329impl<'a> From<&'a Ciphersuites> for Vec<MlsCiphersuite> {
330    fn from(cs: &'a Ciphersuites) -> Self {
331        cs.0.iter().fold(Vec::with_capacity(cs.0.len()), |mut acc, c| {
332            acc.push((*c).into());
333            acc
334        })
335    }
336}
337
338uniffi::custom_type!(Ciphersuites, Vec<u16>);
339
340impl UniffiCustomTypeConverter for Ciphersuites {
341    type Builtin = Vec<u16>;
342
343    fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
344        val.iter().try_fold(Self(vec![]), |mut acc, c| -> uniffi::Result<Self> {
345            let cs =
346                core_crypto::prelude::CiphersuiteName::try_from(*c).map_err(|_| CryptoError::ImplementationError)?;
347            acc.0.push(cs);
348            Ok(acc)
349        })
350    }
351
352    fn from_custom(obj: Self) -> Self::Builtin {
353        obj.0.into_iter().map(|c| (&c).into()).collect()
354    }
355}
356
357#[derive(Debug, Clone, Copy, PartialEq, Eq, uniffi::Record)]
358/// Supporting struct for CRL registration result
359pub struct CrlRegistration {
360    /// Whether this CRL modifies the old CRL (i.e. has a different revocated cert list)
361    pub dirty: bool,
362    /// Optional expiration timestamp
363    pub expiration: Option<u64>,
364}
365
366impl From<core_crypto::e2e_identity::CrlRegistration> for CrlRegistration {
367    fn from(value: core_crypto::e2e_identity::CrlRegistration) -> Self {
368        Self {
369            dirty: value.dirty,
370            expiration: value.expiration,
371        }
372    }
373}
374
375#[derive(Debug, Clone, uniffi::Record)]
376pub struct ProteusAutoPrekeyBundle {
377    pub id: u16,
378    pub pkb: Vec<u8>,
379}
380
381#[derive(Debug, uniffi::Record)]
382/// see [core_crypto::prelude::MlsConversationCreationMessage]
383pub struct MemberAddedMessages {
384    pub welcome: Vec<u8>,
385    pub commit: Vec<u8>,
386    pub group_info: GroupInfoBundle,
387    pub crl_new_distribution_points: Option<Vec<String>>,
388}
389
390impl TryFrom<MlsConversationCreationMessage> for MemberAddedMessages {
391    type Error = CoreCryptoError;
392
393    fn try_from(msg: MlsConversationCreationMessage) -> Result<Self, Self::Error> {
394        let (welcome, commit, group_info, crl_new_distribution_points) = msg.to_bytes()?;
395        Ok(Self {
396            welcome,
397            commit,
398            group_info: group_info.into(),
399            crl_new_distribution_points: crl_new_distribution_points.into(),
400        })
401    }
402}
403
404#[derive(Debug, uniffi::Record)]
405/// see [core_crypto::prelude::MlsConversationCreationMessage]
406pub struct WelcomeBundle {
407    pub id: ConversationId,
408    pub crl_new_distribution_points: Option<Vec<String>>,
409}
410
411impl From<core_crypto::prelude::WelcomeBundle> for WelcomeBundle {
412    fn from(w: core_crypto::prelude::WelcomeBundle) -> Self {
413        Self {
414            id: w.id,
415            crl_new_distribution_points: w.crl_new_distribution_points.into(),
416        }
417    }
418}
419
420#[derive(Debug, uniffi::Record)]
421pub struct CommitBundle {
422    pub welcome: Option<Vec<u8>>,
423    pub commit: Vec<u8>,
424    pub group_info: GroupInfoBundle,
425}
426
427impl TryFrom<MlsCommitBundle> for CommitBundle {
428    type Error = CoreCryptoError;
429
430    fn try_from(msg: MlsCommitBundle) -> Result<Self, Self::Error> {
431        let (welcome, commit, group_info) = msg.to_bytes_triple()?;
432        Ok(Self {
433            welcome,
434            commit,
435            group_info: group_info.into(),
436        })
437    }
438}
439
440#[derive(Debug, Clone, Copy, uniffi::Enum)]
441#[repr(u8)]
442pub enum MlsGroupInfoEncryptionType {
443    /// Unencrypted `GroupInfo`
444    Plaintext = 1,
445    /// `GroupInfo` encrypted in a JWE
446    JweEncrypted = 2,
447}
448
449impl From<core_crypto::prelude::MlsGroupInfoEncryptionType> for MlsGroupInfoEncryptionType {
450    fn from(value: core_crypto::prelude::MlsGroupInfoEncryptionType) -> Self {
451        match value {
452            core_crypto::prelude::MlsGroupInfoEncryptionType::Plaintext => Self::Plaintext,
453            core_crypto::prelude::MlsGroupInfoEncryptionType::JweEncrypted => Self::JweEncrypted,
454        }
455    }
456}
457
458impl From<MlsGroupInfoEncryptionType> for core_crypto::prelude::MlsGroupInfoEncryptionType {
459    fn from(value: MlsGroupInfoEncryptionType) -> Self {
460        match value {
461            MlsGroupInfoEncryptionType::Plaintext => Self::Plaintext,
462            MlsGroupInfoEncryptionType::JweEncrypted => Self::JweEncrypted,
463        }
464    }
465}
466
467#[derive(Debug, Clone, Copy, uniffi::Enum)]
468#[repr(u8)]
469pub enum MlsRatchetTreeType {
470    /// Plain old and complete `GroupInfo`
471    Full = 1,
472    /// Contains `GroupInfo` changes since previous epoch (not yet implemented)
473    /// (see [draft](https://github.com/rohan-wire/ietf-drafts/blob/main/mahy-mls-ratchet-tree-delta/draft-mahy-mls-ratchet-tree-delta.md))
474    Delta = 2,
475    ByRef = 3,
476}
477
478impl From<core_crypto::prelude::MlsRatchetTreeType> for MlsRatchetTreeType {
479    fn from(value: core_crypto::prelude::MlsRatchetTreeType) -> Self {
480        match value {
481            core_crypto::prelude::MlsRatchetTreeType::Full => Self::Full,
482            core_crypto::prelude::MlsRatchetTreeType::Delta => Self::Delta,
483            core_crypto::prelude::MlsRatchetTreeType::ByRef => Self::ByRef,
484        }
485    }
486}
487
488impl From<MlsRatchetTreeType> for core_crypto::prelude::MlsRatchetTreeType {
489    fn from(value: MlsRatchetTreeType) -> Self {
490        match value {
491            MlsRatchetTreeType::Full => Self::Full,
492            MlsRatchetTreeType::Delta => Self::Delta,
493            MlsRatchetTreeType::ByRef => Self::ByRef,
494        }
495    }
496}
497
498#[derive(Debug, Clone, uniffi::Record)]
499pub struct GroupInfoBundle {
500    pub encryption_type: MlsGroupInfoEncryptionType,
501    pub ratchet_tree_type: MlsRatchetTreeType,
502    pub payload: Vec<u8>,
503}
504
505impl From<MlsGroupInfoBundle> for GroupInfoBundle {
506    fn from(gi: MlsGroupInfoBundle) -> Self {
507        Self {
508            encryption_type: gi.encryption_type.into(),
509            ratchet_tree_type: gi.ratchet_tree_type.into(),
510            payload: gi.payload.bytes(),
511        }
512    }
513}
514
515#[derive(Debug, uniffi::Record)]
516pub struct RotateBundle {
517    pub commits: HashMap<String, CommitBundle>,
518    pub new_key_packages: Vec<Vec<u8>>,
519    pub key_package_refs_to_remove: Vec<Vec<u8>>,
520    pub crl_new_distribution_points: Option<Vec<String>>,
521}
522
523impl TryFrom<MlsRotateBundle> for RotateBundle {
524    type Error = CoreCryptoError;
525
526    fn try_from(bundle: MlsRotateBundle) -> Result<Self, Self::Error> {
527        let (commits, new_key_packages, key_package_refs_to_remove, crl_new_distribution_points) = bundle.to_bytes()?;
528        let commits_size = commits.len();
529        let commits = commits
530            .into_iter()
531            .try_fold(HashMap::with_capacity(commits_size), |mut acc, (id, c)| {
532                let _ = acc.insert(id, c.try_into()?);
533                CoreCryptoResult::Ok(acc)
534            })?;
535        Ok(Self {
536            commits,
537            new_key_packages,
538            key_package_refs_to_remove,
539            crl_new_distribution_points: crl_new_distribution_points.into(),
540        })
541    }
542}
543
544#[derive(Debug, uniffi::Record)]
545pub struct ProposalBundle {
546    pub proposal: Vec<u8>,
547    pub proposal_ref: Vec<u8>,
548    pub crl_new_distribution_points: Option<Vec<String>>,
549}
550
551impl TryFrom<MlsProposalBundle> for ProposalBundle {
552    type Error = CoreCryptoError;
553
554    fn try_from(msg: MlsProposalBundle) -> Result<Self, Self::Error> {
555        let (proposal, proposal_ref, crl_new_distribution_points) = msg.to_bytes()?;
556        Ok(Self {
557            proposal,
558            proposal_ref,
559            crl_new_distribution_points: crl_new_distribution_points.into(),
560        })
561    }
562}
563
564#[derive(Debug, uniffi::Record)]
565pub struct ConversationInitBundle {
566    pub conversation_id: Vec<u8>,
567    pub commit: Vec<u8>,
568    pub group_info: GroupInfoBundle,
569    pub crl_new_distribution_points: Option<Vec<String>>,
570}
571
572impl TryFrom<MlsConversationInitBundle> for ConversationInitBundle {
573    type Error = CoreCryptoError;
574
575    fn try_from(mut from: MlsConversationInitBundle) -> Result<Self, Self::Error> {
576        let conversation_id = std::mem::take(&mut from.conversation_id);
577        let (commit, gi, crl_new_distribution_points) = from.to_bytes()?;
578        Ok(Self {
579            conversation_id,
580            commit,
581            group_info: gi.into(),
582            crl_new_distribution_points: crl_new_distribution_points.into(),
583        })
584    }
585}
586
587#[derive(Debug, uniffi::Record)]
588/// See [core_crypto::prelude::decrypt::MlsConversationDecryptMessage]
589pub struct DecryptedMessage {
590    pub message: Option<Vec<u8>>,
591    pub proposals: Vec<ProposalBundle>,
592    pub is_active: bool,
593    pub commit_delay: Option<u64>,
594    pub sender_client_id: Option<ClientId>,
595    pub has_epoch_changed: bool,
596    pub identity: WireIdentity,
597    pub buffered_messages: Option<Vec<BufferedDecryptedMessage>>,
598    pub crl_new_distribution_points: Option<Vec<String>>,
599}
600
601#[derive(Debug, uniffi::Record)]
602/// because Uniffi does not support recursive structs
603pub struct BufferedDecryptedMessage {
604    pub message: Option<Vec<u8>>,
605    pub proposals: Vec<ProposalBundle>,
606    pub is_active: bool,
607    pub commit_delay: Option<u64>,
608    pub sender_client_id: Option<ClientId>,
609    pub has_epoch_changed: bool,
610    pub identity: WireIdentity,
611    pub crl_new_distribution_points: Option<Vec<String>>,
612}
613
614impl TryFrom<MlsConversationDecryptMessage> for DecryptedMessage {
615    type Error = CoreCryptoError;
616
617    fn try_from(from: MlsConversationDecryptMessage) -> Result<Self, Self::Error> {
618        let proposals = from
619            .proposals
620            .into_iter()
621            .map(ProposalBundle::try_from)
622            .collect::<CoreCryptoResult<Vec<_>>>()?;
623
624        let buffered_messages = if let Some(bm) = from.buffered_messages {
625            let bm = bm
626                .into_iter()
627                .map(TryInto::try_into)
628                .collect::<CoreCryptoResult<Vec<_>>>()?;
629            Some(bm)
630        } else {
631            None
632        };
633
634        Ok(Self {
635            message: from.app_msg,
636            proposals,
637            is_active: from.is_active,
638            commit_delay: from.delay,
639            sender_client_id: from.sender_client_id.map(ClientId),
640            has_epoch_changed: from.has_epoch_changed,
641            identity: from.identity.into(),
642            buffered_messages,
643            crl_new_distribution_points: from.crl_new_distribution_points.into(),
644        })
645    }
646}
647
648impl TryFrom<MlsBufferedConversationDecryptMessage> for BufferedDecryptedMessage {
649    type Error = CoreCryptoError;
650
651    fn try_from(from: MlsBufferedConversationDecryptMessage) -> Result<Self, Self::Error> {
652        let proposals = from
653            .proposals
654            .into_iter()
655            .map(ProposalBundle::try_from)
656            .collect::<CoreCryptoResult<Vec<_>>>()?;
657
658        Ok(Self {
659            message: from.app_msg,
660            proposals,
661            is_active: from.is_active,
662            commit_delay: from.delay,
663            sender_client_id: from.sender_client_id.map(ClientId),
664            has_epoch_changed: from.has_epoch_changed,
665            identity: from.identity.into(),
666            crl_new_distribution_points: from.crl_new_distribution_points.into(),
667        })
668    }
669}
670
671#[derive(Debug, uniffi::Record)]
672/// See [core_crypto::prelude::WireIdentity]
673pub struct WireIdentity {
674    pub client_id: String,
675    pub status: DeviceStatus,
676    pub thumbprint: String,
677    pub credential_type: MlsCredentialType,
678    pub x509_identity: Option<X509Identity>,
679}
680
681impl From<core_crypto::prelude::WireIdentity> for WireIdentity {
682    fn from(i: core_crypto::prelude::WireIdentity) -> Self {
683        Self {
684            client_id: i.client_id,
685            status: i.status.into(),
686            thumbprint: i.thumbprint,
687            credential_type: i.credential_type.into(),
688            x509_identity: i.x509_identity.map(Into::into),
689        }
690    }
691}
692
693#[derive(Debug, Copy, Clone, Eq, PartialEq, uniffi::Enum)]
694#[repr(u8)]
695pub enum DeviceStatus {
696    /// All is fine
697    Valid = 1,
698    /// The Credential's certificate is expired
699    Expired = 2,
700    /// The Credential's certificate is revoked (not implemented yet)
701    Revoked = 3,
702}
703
704impl From<core_crypto::prelude::DeviceStatus> for DeviceStatus {
705    fn from(value: core_crypto::prelude::DeviceStatus) -> Self {
706        match value {
707            core_crypto::prelude::DeviceStatus::Valid => Self::Valid,
708            core_crypto::prelude::DeviceStatus::Expired => Self::Expired,
709            core_crypto::prelude::DeviceStatus::Revoked => Self::Revoked,
710        }
711    }
712}
713
714#[derive(Debug, uniffi::Record)]
715/// See [core_crypto::prelude::X509Identity]
716pub struct X509Identity {
717    pub handle: String,
718    pub display_name: String,
719    pub domain: String,
720    pub certificate: String,
721    pub serial_number: String,
722    pub not_before: u64,
723    pub not_after: u64,
724}
725
726impl From<core_crypto::prelude::X509Identity> for X509Identity {
727    fn from(i: core_crypto::prelude::X509Identity) -> Self {
728        Self {
729            handle: i.handle,
730            display_name: i.display_name,
731            domain: i.domain,
732            certificate: i.certificate,
733            serial_number: i.serial_number,
734            not_before: i.not_before,
735            not_after: i.not_after,
736        }
737    }
738}
739
740#[derive(Debug, Clone, uniffi::Record)]
741/// See [core_crypto::prelude::MlsConversationConfiguration]
742pub struct ConversationConfiguration {
743    pub ciphersuite: Ciphersuite,
744    pub external_senders: Vec<Vec<u8>>,
745    pub custom: CustomConfiguration,
746}
747
748#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, uniffi::Enum)]
749#[repr(u8)]
750pub enum MlsWirePolicy {
751    /// Handshake messages are never encrypted
752    #[default]
753    Plaintext = 1,
754    /// Handshake messages are always encrypted
755    Ciphertext = 2,
756}
757
758impl From<core_crypto::prelude::MlsWirePolicy> for MlsWirePolicy {
759    fn from(value: core_crypto::prelude::MlsWirePolicy) -> Self {
760        match value {
761            core_crypto::prelude::MlsWirePolicy::Plaintext => Self::Plaintext,
762            core_crypto::prelude::MlsWirePolicy::Ciphertext => Self::Ciphertext,
763        }
764    }
765}
766
767impl From<MlsWirePolicy> for core_crypto::prelude::MlsWirePolicy {
768    fn from(value: MlsWirePolicy) -> core_crypto::prelude::MlsWirePolicy {
769        match value {
770            MlsWirePolicy::Plaintext => core_crypto::prelude::MlsWirePolicy::Plaintext,
771            MlsWirePolicy::Ciphertext => core_crypto::prelude::MlsWirePolicy::Ciphertext,
772        }
773    }
774}
775
776#[derive(Debug, Clone, uniffi::Record)]
777/// See [core_crypto::prelude::MlsCustomConfiguration]
778pub struct CustomConfiguration {
779    pub key_rotation_span: Option<std::time::Duration>,
780    pub wire_policy: Option<MlsWirePolicy>,
781}
782
783impl From<CustomConfiguration> for MlsCustomConfiguration {
784    fn from(cfg: CustomConfiguration) -> Self {
785        Self {
786            key_rotation_span: cfg.key_rotation_span,
787            wire_policy: cfg.wire_policy.unwrap_or_default().into(),
788            ..Default::default()
789        }
790    }
791}
792
793#[derive(Debug, Clone, uniffi::Record)]
794/// Dummy comment
795pub struct E2eiDumpedPkiEnv {
796    pub root_ca: String,
797    pub intermediates: Vec<String>,
798    pub crls: Vec<String>,
799}
800
801impl From<core_crypto::e2e_identity::E2eiDumpedPkiEnv> for E2eiDumpedPkiEnv {
802    fn from(value: core_crypto::e2e_identity::E2eiDumpedPkiEnv) -> Self {
803        Self {
804            root_ca: value.root_ca,
805            intermediates: value.intermediates,
806            crls: value.crls,
807        }
808    }
809}
810
811#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, uniffi::Enum)]
812#[repr(u8)]
813pub enum MlsCredentialType {
814    /// Basic credential i.e. a KeyPair
815    #[default]
816    Basic = 0x01,
817    /// A x509 certificate generally obtained through e2e identity enrollment process
818    X509 = 0x02,
819}
820
821impl From<core_crypto::prelude::MlsCredentialType> for MlsCredentialType {
822    fn from(value: core_crypto::prelude::MlsCredentialType) -> Self {
823        match value {
824            core_crypto::prelude::MlsCredentialType::Basic => Self::Basic,
825            core_crypto::prelude::MlsCredentialType::X509 => Self::X509,
826        }
827    }
828}
829
830impl From<MlsCredentialType> for core_crypto::prelude::MlsCredentialType {
831    fn from(value: MlsCredentialType) -> core_crypto::prelude::MlsCredentialType {
832        match value {
833            MlsCredentialType::Basic => core_crypto::prelude::MlsCredentialType::Basic,
834            MlsCredentialType::X509 => core_crypto::prelude::MlsCredentialType::X509,
835        }
836    }
837}
838
839#[derive(Debug)]
840struct CoreCryptoCallbacksWrapper(std::sync::Arc<dyn CoreCryptoCallbacks>);
841
842#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
843#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
844impl core_crypto::prelude::CoreCryptoCallbacks for CoreCryptoCallbacksWrapper {
845    async fn authorize(&self, conversation_id: Vec<u8>, client_id: core_crypto::prelude::ClientId) -> bool {
846        self.0.authorize(conversation_id, ClientId(client_id)).await
847    }
848    async fn user_authorize(
849        &self,
850        conversation_id: Vec<u8>,
851        external_client_id: core_crypto::prelude::ClientId,
852        existing_clients: Vec<core_crypto::prelude::ClientId>,
853    ) -> bool {
854        self.0
855            .user_authorize(
856                conversation_id,
857                ClientId(external_client_id),
858                existing_clients.into_iter().map(ClientId).collect(),
859            )
860            .await
861    }
862    async fn client_is_existing_group_user(
863        &self,
864        conversation_id: Vec<u8>,
865        client_id: core_crypto::prelude::ClientId,
866        existing_clients: Vec<core_crypto::prelude::ClientId>,
867        parent_conversation_clients: Option<Vec<core_crypto::prelude::ClientId>>,
868    ) -> bool {
869        self.0
870            .client_is_existing_group_user(
871                conversation_id,
872                ClientId(client_id),
873                existing_clients.into_iter().map(ClientId).collect(),
874                parent_conversation_clients.map(|pccs| pccs.into_iter().map(ClientId).collect()),
875            )
876            .await
877    }
878}
879
880/// This is needed instead of the original trait ([core_crypto::CoreCryptoCallbacks]) to use the
881/// custom type [ClientId], that UniFFi can handle.
882#[uniffi::export(with_foreign)]
883#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
884#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
885pub trait CoreCryptoCallbacks: std::fmt::Debug + Send + Sync {
886    async fn authorize(&self, conversation_id: Vec<u8>, client_id: ClientId) -> bool;
887    async fn user_authorize(
888        &self,
889        conversation_id: Vec<u8>,
890        external_client_id: ClientId,
891        existing_clients: Vec<ClientId>,
892    ) -> bool;
893    async fn client_is_existing_group_user(
894        &self,
895        conversation_id: Vec<u8>,
896        client_id: ClientId,
897        existing_clients: Vec<ClientId>,
898        parent_conversation_clients: Option<Vec<ClientId>>,
899    ) -> bool;
900}
901
902static INIT_LOGGER: Once = Once::new();
903static LOGGER: LazyLock<ReloadLog<CoreCryptoLoggerWrapper>> = LazyLock::new(|| {
904    ReloadLog::new(CoreCryptoLoggerWrapper {
905        logger: Arc::new(DummyLogger {}),
906    })
907});
908
909/// Initializes the logger
910///
911/// NOTE: in a future  release we will remove `level` argument.
912#[uniffi::export]
913pub fn set_logger(logger: Arc<dyn CoreCryptoLogger>, level: CoreCryptoLogLevel) {
914    set_logger_only(logger);
915    set_max_log_level(level);
916}
917
918/// Initializes the logger
919#[uniffi::export]
920pub fn set_logger_only(logger: Arc<dyn CoreCryptoLogger>) {
921    // unwrapping poisoned lock error which shouldn't happen since we don't panic while replacing the logger
922    LOGGER.handle().replace(CoreCryptoLoggerWrapper { logger }).unwrap();
923
924    INIT_LOGGER.call_once(|| {
925        log::set_logger(LOGGER.deref()).unwrap();
926        log::set_max_level(LevelFilter::Warn);
927    });
928}
929
930/// Set maximum log level forwarded to the logger
931#[uniffi::export]
932pub fn set_max_log_level(level: CoreCryptoLogLevel) {
933    log::set_max_level(level.into());
934}
935
936/// This trait is used to provide a callback mechanism to hook up the rerspective platform logging system
937#[uniffi::export(with_foreign)]
938pub trait CoreCryptoLogger: std::fmt::Debug + Send + Sync {
939    /// Function to setup a hook for the logging messages. Core Crypto will call this method
940    /// whenever it needs to log a message.
941    fn log(&self, level: CoreCryptoLogLevel, message: String, context: Option<String>);
942}
943
944struct KeyValueVisitor<'kvs>(BTreeMap<Key<'kvs>, Value<'kvs>>);
945
946impl<'kvs> Visitor<'kvs> for KeyValueVisitor<'kvs> {
947    #[inline]
948    fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> {
949        self.0.insert(key, value);
950        Ok(())
951    }
952}
953#[derive(Debug)]
954struct DummyLogger {}
955
956impl CoreCryptoLogger for DummyLogger {
957    #[allow(unused_variables)]
958    fn log(&self, level: CoreCryptoLogLevel, json_msg: String, context: Option<String>) {}
959}
960
961#[derive(Clone)]
962struct CoreCryptoLoggerWrapper {
963    logger: std::sync::Arc<dyn CoreCryptoLogger>,
964}
965
966impl CoreCryptoLoggerWrapper {
967    fn adjusted_log_level(&self, metadata: &Metadata) -> Level {
968        match (metadata.level(), metadata.target()) {
969            // increase log level for refinery_core::traits since they are too verbose in transactions
970            (level, "refinery_core::traits") if level >= Level::Info => Level::Debug,
971            (level, "refinery_core::traits::sync") if level >= Level::Info => Level::Debug,
972            (level, _) => level,
973        }
974    }
975}
976
977impl log::Log for CoreCryptoLoggerWrapper {
978    fn enabled(&self, metadata: &Metadata) -> bool {
979        log::max_level() >= self.adjusted_log_level(metadata)
980    }
981
982    fn log(&self, record: &Record) {
983        let kvs = record.key_values();
984        let mut visitor = KeyValueVisitor(BTreeMap::new());
985        let _ = kvs.visit(&mut visitor);
986
987        if !self.enabled(record.metadata()) {
988            return;
989        }
990
991        let message = format!("{}", record.args());
992        let context = serde_json::to_string(&visitor.0).ok();
993        self.logger.log(
994            CoreCryptoLogLevel::from(&self.adjusted_log_level(record.metadata())),
995            message,
996            context,
997        );
998    }
999
1000    fn flush(&self) {}
1001}
1002
1003/// Defines the log level for a CoreCrypto
1004#[derive(Debug, Clone, Copy, uniffi::Enum)]
1005pub enum CoreCryptoLogLevel {
1006    Off,
1007    Trace,
1008    Debug,
1009    Info,
1010    Warn,
1011    Error,
1012}
1013
1014impl From<CoreCryptoLogLevel> for LevelFilter {
1015    fn from(value: CoreCryptoLogLevel) -> LevelFilter {
1016        match value {
1017            CoreCryptoLogLevel::Off => LevelFilter::Off,
1018            CoreCryptoLogLevel::Trace => LevelFilter::Trace,
1019            CoreCryptoLogLevel::Debug => LevelFilter::Debug,
1020            CoreCryptoLogLevel::Info => LevelFilter::Info,
1021            CoreCryptoLogLevel::Warn => LevelFilter::Warn,
1022            CoreCryptoLogLevel::Error => LevelFilter::Error,
1023        }
1024    }
1025}
1026
1027impl From<&Level> for CoreCryptoLogLevel {
1028    fn from(value: &Level) -> Self {
1029        match *value {
1030            Level::Warn => CoreCryptoLogLevel::Warn,
1031            Level::Error => CoreCryptoLogLevel::Error,
1032            Level::Info => CoreCryptoLogLevel::Info,
1033            Level::Debug => CoreCryptoLogLevel::Debug,
1034            Level::Trace => CoreCryptoLogLevel::Trace,
1035        }
1036    }
1037}
1038
1039#[derive(Debug, uniffi::Object)]
1040pub struct CoreCrypto {
1041    central: core_crypto::CoreCrypto,
1042    proteus_last_error_code: std::sync::atomic::AtomicU16,
1043}
1044
1045#[uniffi::export]
1046/// See [core_crypto::mls::MlsCentral::try_new]
1047pub async fn core_crypto_new(
1048    path: String,
1049    key: String,
1050    client_id: ClientId,
1051    ciphersuites: Ciphersuites,
1052    nb_key_package: Option<u32>,
1053) -> CoreCryptoResult<CoreCrypto> {
1054    CoreCrypto::new(path, key, Some(client_id), Some(ciphersuites), nb_key_package).await
1055}
1056
1057#[uniffi::export]
1058/// Similar to [core_crypto_new] but defers MLS initialization. It can be initialized later
1059/// with [CoreCrypto::mls_init].
1060pub async fn core_crypto_deferred_init(path: String, key: String) -> CoreCryptoResult<CoreCrypto> {
1061    CoreCrypto::new(path, key, None, None, None).await
1062}
1063
1064#[allow(dead_code, unused_variables)]
1065#[uniffi::export]
1066impl CoreCrypto {
1067    #[uniffi::constructor]
1068    pub async fn new(
1069        path: String,
1070        key: String,
1071        client_id: Option<ClientId>,
1072        ciphersuites: Option<Ciphersuites>,
1073        nb_key_package: Option<u32>,
1074    ) -> CoreCryptoResult<Self> {
1075        let nb_key_package = nb_key_package
1076            .map(usize::try_from)
1077            .transpose()
1078            .map_err(CryptoError::from)?;
1079        let configuration = MlsCentralConfiguration::try_new(
1080            path,
1081            key,
1082            client_id.map(|cid| cid.0.clone()),
1083            (&ciphersuites.unwrap_or_default()).into(),
1084            None,
1085            nb_key_package,
1086        )?;
1087
1088        let central = MlsCentral::try_new(configuration).await?;
1089        let central = core_crypto::CoreCrypto::from(central);
1090
1091        Ok(CoreCrypto {
1092            central,
1093            proteus_last_error_code: std::sync::atomic::AtomicU16::new(0),
1094        })
1095    }
1096
1097    /// See [core_crypto::context::CentralContext::mls_init]
1098    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1099    pub async fn mls_init(
1100        &self,
1101        client_id: ClientId,
1102        ciphersuites: Ciphersuites,
1103        nb_key_package: Option<u32>,
1104    ) -> CoreCryptoResult<()> {
1105        self.deprecated_transaction(|context| async move {
1106            let nb_key_package = nb_key_package
1107                .map(usize::try_from)
1108                .transpose()
1109                .map_err(CryptoError::from)?;
1110            context
1111                .mls_init(
1112                    ClientIdentifier::Basic(client_id.0),
1113                    (&ciphersuites).into(),
1114                    nb_key_package,
1115                )
1116                .await
1117        })
1118        .await
1119    }
1120
1121    /// See [core_crypto::context::CentralContext::mls_generate_keypairs]
1122    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1123    pub async fn mls_generate_keypairs(&self, ciphersuites: Ciphersuites) -> CoreCryptoResult<Vec<ClientId>> {
1124        self.deprecated_transaction(|context| async move {
1125            context
1126                .mls_generate_keypairs((&ciphersuites).into())
1127                .await
1128                .map(|cids| cids.into_iter().map(ClientId).collect())
1129        })
1130        .await
1131    }
1132
1133    /// See [core_crypto::context::CentralContext::mls_init_with_client_id]
1134    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1135    pub async fn mls_init_with_client_id(
1136        &self,
1137        client_id: ClientId,
1138        tmp_client_ids: Vec<ClientId>,
1139        ciphersuites: Ciphersuites,
1140    ) -> CoreCryptoResult<()> {
1141        self.deprecated_transaction(|context| async move {
1142            context
1143                .mls_init_with_client_id(
1144                    client_id.0,
1145                    tmp_client_ids.into_iter().map(|cid| cid.0).collect(),
1146                    (&ciphersuites).into(),
1147                )
1148                .await
1149        })
1150        .await
1151    }
1152
1153    /// See [core_crypto::context::CentralContext::proteus_reload_sessions]
1154    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1155    pub async fn restore_from_disk(&self) -> CoreCryptoResult<()> {
1156        cfg_if::cfg_if! {
1157            if #[cfg(feature = "proteus")] {
1158                self.deprecated_transaction(|context| async move {
1159                context.proteus_reload_sessions().await.inspect_err(|e|{
1160                    let errcode = e.proteus_error_code();
1161                    if errcode.is_some() {
1162                            self.proteus_last_error_code.store(errcode.unwrap_or_default(), std::sync::atomic::Ordering::SeqCst);
1163                        }
1164                    })?;
1165                    Ok(())
1166                }).await?
1167            }
1168        }
1169        Ok(())
1170    }
1171
1172    /// See [core_crypto::mls::MlsCentral::close]
1173    pub async fn unload(self: std::sync::Arc<Self>) -> CoreCryptoResult<()> {
1174        if let Some(cc) = std::sync::Arc::into_inner(self) {
1175            cc.central.take().close().await?;
1176            Ok(())
1177        } else {
1178            Err(CryptoError::LockPoisonError.into())
1179        }
1180    }
1181
1182    /// See [core_crypto::mls::MlsCentral::wipe]
1183    pub async fn wipe(self: std::sync::Arc<Self>) -> CoreCryptoResult<()> {
1184        if let Some(cc) = std::sync::Arc::into_inner(self) {
1185            cc.central.take().wipe().await?;
1186            Ok(())
1187        } else {
1188            Err(CryptoError::LockPoisonError.into())
1189        }
1190    }
1191
1192    /// See [core_crypto::mls::MlsCentral::callbacks]
1193    pub async fn set_callbacks(&self, callbacks: std::sync::Arc<dyn CoreCryptoCallbacks>) -> CoreCryptoResult<()> {
1194        self.central
1195            .callbacks(std::sync::Arc::new(CoreCryptoCallbacksWrapper(callbacks)))
1196            .await;
1197        Ok(())
1198    }
1199
1200    /// See [core_crypto::mls::MlsCentral::client_public_key]
1201    pub async fn client_public_key(
1202        &self,
1203        ciphersuite: Ciphersuite,
1204        credential_type: MlsCredentialType,
1205    ) -> CoreCryptoResult<Vec<u8>> {
1206        Ok(self
1207            .central
1208            .client_public_key(ciphersuite.into(), credential_type.into())
1209            .await?)
1210    }
1211
1212    /// See [core_crypto::context::CentralContext::get_or_create_client_keypackages]
1213    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1214    pub async fn client_keypackages(
1215        &self,
1216        ciphersuite: Ciphersuite,
1217        credential_type: MlsCredentialType,
1218        amount_requested: u32,
1219    ) -> CoreCryptoResult<Vec<Vec<u8>>> {
1220        self.deprecated_transaction(|context| async move {
1221            let kps = context
1222                .get_or_create_client_keypackages(ciphersuite.into(), credential_type.into(), amount_requested as usize)
1223                .await?;
1224            kps.into_iter()
1225                .map(|kp| {
1226                    kp.tls_serialize_detached()
1227                        .map_err(core_crypto::MlsError::from)
1228                        .map_err(CryptoError::from)
1229                })
1230                .collect::<CryptoResult<Vec<Vec<u8>>>>()
1231        })
1232        .await
1233    }
1234
1235    /// See [core_crypto::context::CentralContext::client_valid_key_packages_count]
1236    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1237    pub async fn client_valid_keypackages_count(
1238        &self,
1239        ciphersuite: Ciphersuite,
1240        credential_type: MlsCredentialType,
1241    ) -> CoreCryptoResult<u64> {
1242        self.deprecated_transaction(|context| async move {
1243            let count = context
1244                .client_valid_key_packages_count(ciphersuite.into(), credential_type.into())
1245                .await?;
1246            Ok(count.try_into().unwrap_or(0))
1247        })
1248        .await
1249    }
1250
1251    /// See [core_crypto::context::CentralContext::delete_keypackages]
1252    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1253    pub async fn delete_keypackages(&self, refs: Vec<Vec<u8>>) -> CoreCryptoResult<()> {
1254        let refs = refs
1255            .into_iter()
1256            .map(|r| KeyPackageRef::from_slice(&r))
1257            .collect::<Vec<_>>();
1258
1259        self.deprecated_transaction(|context| async move { context.delete_keypackages(&refs[..]).await })
1260            .await
1261    }
1262
1263    /// See [core_crypto::context::CentralContext::new_conversation]
1264    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1265    pub async fn create_conversation(
1266        &self,
1267        conversation_id: Vec<u8>,
1268        creator_credential_type: MlsCredentialType,
1269        config: ConversationConfiguration,
1270    ) -> CoreCryptoResult<()> {
1271        let mut lower_cfg = MlsConversationConfiguration {
1272            custom: config.custom.into(),
1273            ciphersuite: config.ciphersuite.into(),
1274            ..Default::default()
1275        };
1276
1277        self.deprecated_transaction(|context| async move {
1278            context
1279                .set_raw_external_senders(&mut lower_cfg, config.external_senders)
1280                .await?;
1281            context
1282                .new_conversation(&conversation_id, creator_credential_type.into(), lower_cfg)
1283                .await
1284        })
1285        .await
1286    }
1287
1288    /// See [core_crypto::mls::MlsCentral::conversation_epoch]
1289    pub async fn conversation_epoch(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<u64> {
1290        Ok(self.central.conversation_epoch(&conversation_id).await?)
1291    }
1292
1293    /// See [core_crypto::mls::MlsCentral::conversation_ciphersuite]
1294    pub async fn conversation_ciphersuite(&self, conversation_id: &ConversationId) -> CoreCryptoResult<Ciphersuite> {
1295        let cs = self.central.conversation_ciphersuite(conversation_id).await?;
1296        Ok(Ciphersuite::from(core_crypto::prelude::CiphersuiteName::from(cs)))
1297    }
1298
1299    /// See [core_crypto::context::CentralContext::process_raw_welcome_message]
1300    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1301    pub async fn process_welcome_message(
1302        &self,
1303        welcome_message: Vec<u8>,
1304        custom_configuration: CustomConfiguration,
1305    ) -> CoreCryptoResult<WelcomeBundle> {
1306        self.deprecated_transaction(|context| async move {
1307            Ok(context
1308                .process_raw_welcome_message(welcome_message, custom_configuration.into())
1309                .await?
1310                .into())
1311        })
1312        .await
1313    }
1314
1315    /// See [core_crypto::context::CentralContext::add_members_to_conversation]
1316    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1317    pub async fn add_clients_to_conversation(
1318        &self,
1319        conversation_id: Vec<u8>,
1320        key_packages: Vec<Vec<u8>>,
1321    ) -> CoreCryptoResult<MemberAddedMessages> {
1322        let key_packages = key_packages
1323            .into_iter()
1324            .map(|kp| {
1325                KeyPackageIn::tls_deserialize(&mut kp.as_slice())
1326                    .map_err(|e| CoreCryptoError::from(CryptoError::MlsError(e.into())))
1327            })
1328            .collect::<CoreCryptoResult<Vec<_>>>()?;
1329
1330        self.deprecated_transaction(|context| async move {
1331            context
1332                .add_members_to_conversation(&conversation_id, key_packages)
1333                .await
1334        })
1335        .await?
1336        .try_into()
1337    }
1338
1339    /// See [core_crypto::context::CentralContext::remove_members_from_conversation]
1340    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1341    pub async fn remove_clients_from_conversation(
1342        &self,
1343        conversation_id: Vec<u8>,
1344        clients: Vec<ClientId>,
1345    ) -> CoreCryptoResult<CommitBundle> {
1346        let clients: Vec<core_crypto::prelude::ClientId> = clients.into_iter().map(|c| c.0).collect();
1347        self.deprecated_transaction(|context| async move {
1348            context
1349                .remove_members_from_conversation(&conversation_id, &clients)
1350                .await
1351        })
1352        .await?
1353        .try_into()
1354    }
1355
1356    /// See [core_crypto::context::CentralContext::mark_conversation_as_child_of]
1357    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1358    pub async fn mark_conversation_as_child_of(&self, child_id: Vec<u8>, parent_id: Vec<u8>) -> CoreCryptoResult<()> {
1359        self.deprecated_transaction(|context| async move {
1360            context.mark_conversation_as_child_of(&child_id, &parent_id).await
1361        })
1362        .await
1363    }
1364
1365    /// See [core_crypto::context::CentralContext::update_keying_material]
1366    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1367    pub async fn update_keying_material(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<CommitBundle> {
1368        self.deprecated_transaction(|context| async move { context.update_keying_material(&conversation_id).await })
1369            .await?
1370            .try_into()
1371    }
1372
1373    /// See [core_crypto::context::CentralContext::commit_pending_proposals]
1374    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1375    pub async fn commit_pending_proposals(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<Option<CommitBundle>> {
1376        self.deprecated_transaction(|context| async move { context.commit_pending_proposals(&conversation_id).await })
1377            .await
1378            .transpose()
1379            .map(|r| r?.try_into())
1380            .transpose()
1381    }
1382
1383    /// see [core_crypto::context::CentralContext::wipe_conversation]
1384    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1385    pub async fn wipe_conversation(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
1386        self.deprecated_transaction(|context| async move { context.wipe_conversation(&conversation_id).await })
1387            .await
1388    }
1389
1390    /// See [core_crypto::context::CentralContext::decrypt_message]
1391    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1392    pub async fn decrypt_message(
1393        &self,
1394        conversation_id: Vec<u8>,
1395        payload: Vec<u8>,
1396    ) -> CoreCryptoResult<DecryptedMessage> {
1397        self.deprecated_transaction(|context| async move { context.decrypt_message(&conversation_id, payload).await })
1398            .await?
1399            .try_into()
1400    }
1401
1402    /// See [core_crypto::context::CentralContext::encrypt_message]
1403    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1404    pub async fn encrypt_message(&self, conversation_id: Vec<u8>, message: Vec<u8>) -> CoreCryptoResult<Vec<u8>> {
1405        self.deprecated_transaction(|context| async move { context.encrypt_message(&conversation_id, message).await })
1406            .await
1407    }
1408
1409    /// See [core_crypto::mls::MlsCentral::conversation_exists]
1410    pub async fn conversation_exists(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<bool> {
1411        Ok(self.central.conversation_exists(&conversation_id).await?)
1412    }
1413
1414    /// See [core_crypto::context::CentralContext::new_add_proposal]
1415    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1416    pub async fn new_add_proposal(
1417        &self,
1418        conversation_id: Vec<u8>,
1419        keypackage: Vec<u8>,
1420    ) -> CoreCryptoResult<ProposalBundle> {
1421        let kp = KeyPackageIn::tls_deserialize(&mut keypackage.as_slice())
1422            .map_err(core_crypto::MlsError::from)
1423            .map_err(CryptoError::from)?;
1424        self.deprecated_transaction(
1425            |context| async move { context.new_add_proposal(&conversation_id, kp.into()).await },
1426        )
1427        .await?
1428        .try_into()
1429    }
1430
1431    /// See [core_crypto::context::CentralContext::new_update_proposal]
1432    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1433    pub async fn new_update_proposal(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<ProposalBundle> {
1434        self.deprecated_transaction(|context| async move { context.new_update_proposal(&conversation_id).await })
1435            .await?
1436            .try_into()
1437    }
1438
1439    /// See [core_crypto::context::CentralContext::new_remove_proposal]
1440    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1441    pub async fn new_remove_proposal(
1442        &self,
1443        conversation_id: Vec<u8>,
1444        client_id: ClientId,
1445    ) -> CoreCryptoResult<ProposalBundle> {
1446        self.deprecated_transaction(|context| async move {
1447            context.new_remove_proposal(&conversation_id, client_id.0).await
1448        })
1449        .await?
1450        .try_into()
1451    }
1452
1453    /// See [core_crypto::context::CentralContext::new_external_add_proposal]
1454    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1455    pub async fn new_external_add_proposal(
1456        &self,
1457        conversation_id: Vec<u8>,
1458        epoch: u64,
1459        ciphersuite: Ciphersuite,
1460        credential_type: MlsCredentialType,
1461    ) -> CoreCryptoResult<Vec<u8>> {
1462        self.deprecated_transaction(|context| async move {
1463            context
1464                .new_external_add_proposal(
1465                    conversation_id,
1466                    epoch.into(),
1467                    ciphersuite.into(),
1468                    credential_type.into(),
1469                )
1470                .await?
1471                .to_bytes()
1472                .map_err(core_crypto::MlsError::from)
1473                .map_err(CryptoError::from)
1474        })
1475        .await
1476    }
1477
1478    /// See [core_crypto::context::CentralContext::join_by_external_commit]
1479    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1480    pub async fn join_by_external_commit(
1481        &self,
1482        group_info: Vec<u8>,
1483        custom_configuration: CustomConfiguration,
1484        credential_type: MlsCredentialType,
1485    ) -> CoreCryptoResult<ConversationInitBundle> {
1486        let group_info = VerifiableGroupInfo::tls_deserialize(&mut group_info.as_slice())
1487            .map_err(core_crypto::MlsError::from)
1488            .map_err(CryptoError::from)?;
1489        self.deprecated_transaction(|context| async move {
1490            context
1491                .join_by_external_commit(group_info, custom_configuration.into(), credential_type.into())
1492                .await
1493        })
1494        .await?
1495        .try_into()
1496    }
1497
1498    /// See [core_crypto::context::CentralContext::merge_pending_group_from_external_commit]
1499    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1500    pub async fn merge_pending_group_from_external_commit(
1501        &self,
1502        conversation_id: Vec<u8>,
1503    ) -> CoreCryptoResult<Option<Vec<BufferedDecryptedMessage>>> {
1504        let maybe_buffered_messages = self
1505            .deprecated_transaction(|context| async move {
1506                context.merge_pending_group_from_external_commit(&conversation_id).await
1507            })
1508            .await?;
1509        let Some(buffered_messages) = maybe_buffered_messages else {
1510            return Ok(None);
1511        };
1512        Ok(Some(
1513            buffered_messages
1514                .into_iter()
1515                .map(TryInto::try_into)
1516                .collect::<CoreCryptoResult<Vec<_>>>()?,
1517        ))
1518    }
1519
1520    /// See [core_crypto::context::CentralContext::clear_pending_group_from_external_commit]
1521    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1522    pub async fn clear_pending_group_from_external_commit(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
1523        self.deprecated_transaction(|context| async move {
1524            context.clear_pending_group_from_external_commit(&conversation_id).await
1525        })
1526        .await
1527    }
1528
1529    /// See [core_crypto::mls::MlsCentral::random_bytes]
1530    pub async fn random_bytes(&self, len: u32) -> CoreCryptoResult<Vec<u8>> {
1531        Ok(self.central.random_bytes(len.try_into().map_err(CryptoError::from)?)?)
1532    }
1533
1534    /// see [core_crypto::prelude::MlsCryptoProvider::reseed]
1535    pub async fn reseed_rng(&self, seed: Vec<u8>) -> CoreCryptoResult<()> {
1536        let seed = EntropySeed::try_from_slice(&seed).map_err(CryptoError::from)?;
1537        self.central.reseed(Some(seed)).await?;
1538
1539        Ok(())
1540    }
1541
1542    /// See [core_crypto::context::CentralContext::commit_accepted]
1543    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1544    pub async fn commit_accepted(
1545        &self,
1546        conversation_id: Vec<u8>,
1547    ) -> CoreCryptoResult<Option<Vec<BufferedDecryptedMessage>>> {
1548        let maybe_buffered_messages = self
1549            .deprecated_transaction(|context| async move { context.commit_accepted(&conversation_id).await })
1550            .await?;
1551        let Some(buffered_messages) = maybe_buffered_messages else {
1552            return Ok(None);
1553        };
1554        Ok(Some(
1555            buffered_messages
1556                .into_iter()
1557                .map(TryInto::try_into)
1558                .collect::<CoreCryptoResult<Vec<_>>>()?,
1559        ))
1560    }
1561
1562    /// See [core_crypto::context::CentralContext::clear_pending_proposal]
1563    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1564    pub async fn clear_pending_proposal(
1565        &self,
1566        conversation_id: Vec<u8>,
1567        proposal_ref: Vec<u8>,
1568    ) -> CoreCryptoResult<()> {
1569        self.deprecated_transaction(|context| async move {
1570            context
1571                .clear_pending_proposal(&conversation_id, proposal_ref.into())
1572                .await
1573        })
1574        .await
1575    }
1576
1577    /// See [core_crypto::context::CentralContext::clear_pending_commit]
1578    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1579    pub async fn clear_pending_commit(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
1580        self.deprecated_transaction(|context| async move { context.clear_pending_commit(&conversation_id).await })
1581            .await
1582    }
1583
1584    /// See [core_crypto::mls::MlsCentral::get_client_ids]
1585    pub async fn get_client_ids(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<Vec<ClientId>> {
1586        Ok(self
1587            .central
1588            .get_client_ids(&conversation_id)
1589            .await
1590            .map(|cids| cids.into_iter().map(ClientId).collect())?)
1591    }
1592
1593    /// See [core_crypto::mls::MlsCentral::export_secret_key]
1594    pub async fn export_secret_key(&self, conversation_id: Vec<u8>, key_length: u32) -> CoreCryptoResult<Vec<u8>> {
1595        Ok(self
1596            .central
1597            .export_secret_key(&conversation_id, key_length as usize)
1598            .await?)
1599    }
1600
1601    /// See [core_crypto::mls::MlsCentral::get_external_sender]
1602    pub async fn get_external_sender(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<Vec<u8>> {
1603        Ok(self.central.get_external_sender(&conversation_id).await?)
1604    }
1605}
1606
1607#[derive(Debug, Copy, Clone, uniffi::Enum)]
1608#[repr(u8)]
1609pub enum E2eiConversationState {
1610    /// All clients have a valid E2EI certificate
1611    Verified = 1,
1612    /// Some clients are either still Basic or their certificate is expired
1613    NotVerified,
1614    /// All clients are still Basic. If all client have expired certificates, [E2eiConversationState::NotVerified] is returned.
1615    NotEnabled,
1616}
1617
1618impl From<core_crypto::prelude::E2eiConversationState> for E2eiConversationState {
1619    fn from(value: core_crypto::prelude::E2eiConversationState) -> Self {
1620        match value {
1621            core_crypto::prelude::E2eiConversationState::Verified => Self::Verified,
1622            core_crypto::prelude::E2eiConversationState::NotVerified => Self::NotVerified,
1623            core_crypto::prelude::E2eiConversationState::NotEnabled => Self::NotEnabled,
1624        }
1625    }
1626}
1627
1628#[cfg_attr(not(feature = "proteus"), allow(unused_variables))]
1629#[uniffi::export]
1630impl CoreCrypto {
1631    /// See [core_crypto::proteus::ProteusCentral::try_new]
1632    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1633    pub async fn proteus_init(&self) -> CoreCryptoResult<()> {
1634        proteus_impl! { self.proteus_last_error_code => {
1635            self.deprecated_transaction(|context| async move {
1636                context.proteus_init().await?;
1637            Ok(())
1638            }).await
1639        }}
1640    }
1641
1642    /// See [core_crypto::proteus::ProteusCentral::session_from_prekey]
1643    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1644    pub async fn proteus_session_from_prekey(&self, session_id: String, prekey: Vec<u8>) -> CoreCryptoResult<()> {
1645        proteus_impl! { self.proteus_last_error_code => {
1646            self.deprecated_transaction(|context| async move {
1647                context.proteus_session_from_prekey(&session_id, &prekey).await?;
1648            Ok(())
1649            }).await
1650        }}
1651    }
1652
1653    /// See [core_crypto::proteus::ProteusCentral::session_from_message]
1654    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1655    pub async fn proteus_session_from_message(
1656        &self,
1657        session_id: String,
1658        envelope: Vec<u8>,
1659    ) -> CoreCryptoResult<Vec<u8>> {
1660        proteus_impl! { self.proteus_last_error_code => {
1661            self.deprecated_transaction(|context| async move {
1662            let (_, payload) = context
1663                .proteus_session_from_message(&session_id, &envelope).await?;
1664            Ok(payload)
1665            }).await
1666        }}
1667    }
1668
1669    /// See [core_crypto::proteus::ProteusCentral::session_save]
1670    /// **Note**: This isn't usually needed as persisting sessions happens automatically when decrypting/encrypting messages and initializing Sessions
1671    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1672    pub async fn proteus_session_save(&self, session_id: String) -> CoreCryptoResult<()> {
1673        proteus_impl! { self.proteus_last_error_code => {
1674            self.deprecated_transaction(|context| async move {
1675                context.proteus_session_save(&session_id).await
1676            }).await
1677        }}
1678    }
1679
1680    /// See [core_crypto::proteus::ProteusCentral::session_delete]
1681    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1682    pub async fn proteus_session_delete(&self, session_id: String) -> CoreCryptoResult<()> {
1683        proteus_impl! { self.proteus_last_error_code => {
1684            self.deprecated_transaction(|context| async move {
1685                context.proteus_session_delete(&session_id).await
1686            }).await
1687        }}
1688    }
1689
1690    /// See [core_crypto::proteus::ProteusCentral::session_exists]
1691    pub async fn proteus_session_exists(&self, session_id: String) -> CoreCryptoResult<bool> {
1692        proteus_impl! { self.proteus_last_error_code => {
1693            Ok(self.central
1694                .proteus_session_exists(&session_id)
1695                .await?)
1696        }}
1697    }
1698
1699    /// See [core_crypto::proteus::ProteusCentral::decrypt]
1700    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1701    pub async fn proteus_decrypt(&self, session_id: String, ciphertext: Vec<u8>) -> CoreCryptoResult<Vec<u8>> {
1702        proteus_impl! { self.proteus_last_error_code => {
1703            self.deprecated_transaction(|context| async move {
1704                context.proteus_decrypt(&session_id, &ciphertext).await
1705            }).await
1706        }}
1707    }
1708
1709    /// See [core_crypto::proteus::ProteusCentral::encrypt]
1710    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1711    pub async fn proteus_encrypt(&self, session_id: String, plaintext: Vec<u8>) -> CoreCryptoResult<Vec<u8>> {
1712        proteus_impl! { self.proteus_last_error_code => {
1713            self.deprecated_transaction(|context| async move {
1714                context.proteus_encrypt(&session_id, &plaintext).await
1715            }).await
1716        }}
1717    }
1718
1719    /// See [core_crypto::proteus::ProteusCentral::encrypt_batched]
1720    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1721    pub async fn proteus_encrypt_batched(
1722        &self,
1723        sessions: Vec<String>,
1724        plaintext: Vec<u8>,
1725    ) -> CoreCryptoResult<std::collections::HashMap<String, Vec<u8>>> {
1726        proteus_impl! { self.proteus_last_error_code => {
1727            self.deprecated_transaction(|context| async move {
1728                context.proteus_encrypt_batched(&sessions, &plaintext).await
1729            }).await
1730        }}
1731    }
1732
1733    /// See [core_crypto::proteus::ProteusCentral::new_prekey]
1734    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1735    pub async fn proteus_new_prekey(&self, prekey_id: u16) -> CoreCryptoResult<Vec<u8>> {
1736        proteus_impl! { self.proteus_last_error_code => {
1737            self.deprecated_transaction(|context| async move {
1738                context.proteus_new_prekey(prekey_id).await
1739            }).await
1740        }}
1741    }
1742
1743    /// See [core_crypto::proteus::ProteusCentral::new_prekey_auto]
1744    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1745    pub async fn proteus_new_prekey_auto(&self) -> CoreCryptoResult<ProteusAutoPrekeyBundle> {
1746        proteus_impl! { self.proteus_last_error_code => {
1747            self.deprecated_transaction(|context| async move {
1748            let (id, pkb) = context
1749                .proteus_new_prekey_auto()
1750                .await?;
1751            Ok(ProteusAutoPrekeyBundle { id, pkb })
1752            }).await
1753        }}
1754    }
1755
1756    /// See [core_crypto::proteus::ProteusCentral::last_resort_prekey]
1757    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1758    pub async fn proteus_last_resort_prekey(&self) -> CoreCryptoResult<Vec<u8>> {
1759        proteus_impl! { self.proteus_last_error_code => {
1760            self.deprecated_transaction(|context| async move {
1761                context.proteus_last_resort_prekey().await}).await
1762        }}
1763    }
1764
1765    /// See [core_crypto::proteus::ProteusCentral::last_resort_prekey_id]
1766    pub fn proteus_last_resort_prekey_id(&self) -> CoreCryptoResult<u16> {
1767        proteus_impl!({ Ok(core_crypto::CoreCrypto::proteus_last_resort_prekey_id()) })
1768    }
1769
1770    /// See [core_crypto::proteus::ProteusCentral::fingerprint]
1771    pub async fn proteus_fingerprint(&self) -> CoreCryptoResult<String> {
1772        proteus_impl! { self.proteus_last_error_code => {
1773            Ok(self.central
1774                .proteus_fingerprint().await?)
1775        }}
1776    }
1777
1778    /// See [core_crypto::proteus::ProteusCentral::fingerprint_local]
1779    pub async fn proteus_fingerprint_local(&self, session_id: String) -> CoreCryptoResult<String> {
1780        proteus_impl! { self.proteus_last_error_code => {
1781            Ok(self.central
1782                .proteus_fingerprint_local(&session_id)
1783                .await?)
1784        }}
1785    }
1786
1787    /// See [core_crypto::proteus::ProteusCentral::fingerprint_remote]
1788    pub async fn proteus_fingerprint_remote(&self, session_id: String) -> CoreCryptoResult<String> {
1789        proteus_impl! { self.proteus_last_error_code => {
1790            Ok(self.central
1791                .proteus_fingerprint_remote(&session_id)
1792                .await?)
1793        }}
1794    }
1795
1796    /// See [core_crypto::proteus::ProteusCentral::fingerprint_prekeybundle]
1797    /// NOTE: uniffi doesn't support associated functions, so we have to have the self here
1798    pub fn proteus_fingerprint_prekeybundle(&self, prekey: Vec<u8>) -> CoreCryptoResult<String> {
1799        proteus_impl!({ Ok(core_crypto::proteus::ProteusCentral::fingerprint_prekeybundle(&prekey)?) })
1800    }
1801
1802    /// See [core_crypto::proteus::ProteusCentral::cryptobox_migrate]
1803    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1804    pub async fn proteus_cryptobox_migrate(&self, path: String) -> CoreCryptoResult<()> {
1805        proteus_impl! { self.proteus_last_error_code => {
1806            self.deprecated_transaction(|context| async move {
1807            context.proteus_cryptobox_migrate(&path).await
1808            }).await
1809        }}
1810    }
1811
1812    /// Returns the latest proteus error code. If 0, no error has occured
1813    ///
1814    /// NOTE: This will clear the last error code.
1815    pub fn proteus_last_error_code(&self) -> Option<u16> {
1816        let raw_error_code = self
1817            .proteus_last_error_code
1818            .swap(0, std::sync::atomic::Ordering::SeqCst);
1819        (raw_error_code != 0).then_some(raw_error_code)
1820    }
1821}
1822
1823// End-to-end identity methods
1824#[allow(dead_code, unused_variables)]
1825#[uniffi::export]
1826impl CoreCrypto {
1827    /// See [core_crypto::context::CentralContext::e2ei_new_enrollment]
1828    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1829    pub async fn e2ei_new_enrollment(
1830        &self,
1831        client_id: String,
1832        display_name: String,
1833        handle: String,
1834        team: Option<String>,
1835        expiry_sec: u32,
1836        ciphersuite: Ciphersuite,
1837    ) -> CoreCryptoResult<E2eiEnrollment> {
1838        self.deprecated_transaction(|context| async move {
1839            context
1840                .e2ei_new_enrollment(
1841                    client_id.into_bytes().into(),
1842                    display_name,
1843                    handle,
1844                    team,
1845                    expiry_sec,
1846                    ciphersuite.into(),
1847                )
1848                .await
1849                .map(async_lock::RwLock::new)
1850                .map(std::sync::Arc::new)
1851                .map(E2eiEnrollment)
1852        })
1853        .await
1854    }
1855
1856    /// See [core_crypto::context::CentralContext::e2ei_new_activation_enrollment]
1857    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1858    pub async fn e2ei_new_activation_enrollment(
1859        &self,
1860        display_name: String,
1861        handle: String,
1862        team: Option<String>,
1863        expiry_sec: u32,
1864        ciphersuite: Ciphersuite,
1865    ) -> CoreCryptoResult<E2eiEnrollment> {
1866        self.deprecated_transaction(|context| async move {
1867            context
1868                .e2ei_new_activation_enrollment(display_name, handle, team, expiry_sec, ciphersuite.into())
1869                .await
1870                .map(async_lock::RwLock::new)
1871                .map(std::sync::Arc::new)
1872                .map(E2eiEnrollment)
1873        })
1874        .await
1875    }
1876
1877    /// See [core_crypto::context::CentralContext::e2ei_new_rotate_enrollment]
1878    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1879    pub async fn e2ei_new_rotate_enrollment(
1880        &self,
1881        display_name: Option<String>,
1882        handle: Option<String>,
1883        team: Option<String>,
1884        expiry_sec: u32,
1885        ciphersuite: Ciphersuite,
1886    ) -> CoreCryptoResult<E2eiEnrollment> {
1887        self.deprecated_transaction(|context| async move {
1888            context
1889                .e2ei_new_rotate_enrollment(display_name, handle, team, expiry_sec, ciphersuite.into())
1890                .await
1891                .map(async_lock::RwLock::new)
1892                .map(std::sync::Arc::new)
1893                .map(E2eiEnrollment)
1894        })
1895        .await
1896    }
1897
1898    pub async fn e2ei_dump_pki_env(&self) -> CoreCryptoResult<Option<E2eiDumpedPkiEnv>> {
1899        Ok(self.central.e2ei_dump_pki_env().await?.map(Into::into))
1900    }
1901
1902    /// See [core_crypto::mls::MlsCentral::e2ei_is_pki_env_setup]
1903    pub async fn e2ei_is_pki_env_setup(&self) -> bool {
1904        self.central.e2ei_is_pki_env_setup().await
1905    }
1906
1907    /// See [core_crypto::context::CentralContext::e2ei_register_acme_ca]
1908    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1909    pub async fn e2ei_register_acme_ca(&self, trust_anchor_pem: String) -> CoreCryptoResult<()> {
1910        self.deprecated_transaction(|context| async move { context.e2ei_register_acme_ca(trust_anchor_pem).await })
1911            .await
1912    }
1913
1914    /// See [core_crypto::context::CentralContext::e2ei_register_intermediate_ca_pem]
1915    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1916    pub async fn e2ei_register_intermediate_ca(&self, cert_pem: String) -> CoreCryptoResult<Option<Vec<String>>> {
1917        self.deprecated_transaction(|context| async move {
1918            Ok(context.e2ei_register_intermediate_ca_pem(cert_pem).await?.into())
1919        })
1920        .await
1921    }
1922
1923    /// See [core_crypto::context::CentralContext::e2ei_register_crl]
1924    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1925    pub async fn e2ei_register_crl(&self, crl_dp: String, crl_der: Vec<u8>) -> CoreCryptoResult<CrlRegistration> {
1926        self.deprecated_transaction(
1927            |context| async move { Ok(context.e2ei_register_crl(crl_dp, crl_der).await?.into()) },
1928        )
1929        .await
1930    }
1931
1932    /// See [core_crypto::context::CentralContext::e2ei_mls_init_only]
1933    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1934    pub async fn e2ei_mls_init_only(
1935        &self,
1936        enrollment: std::sync::Arc<E2eiEnrollment>,
1937        certificate_chain: String,
1938        nb_key_package: Option<u32>,
1939    ) -> CoreCryptoResult<Option<Vec<String>>> {
1940        let nb_key_package = nb_key_package
1941            .map(usize::try_from)
1942            .transpose()
1943            .map_err(CryptoError::from)?;
1944
1945        self.deprecated_transaction(|context| async move {
1946            Ok(context
1947                .e2ei_mls_init_only(
1948                    enrollment.0.write().await.deref_mut(),
1949                    certificate_chain,
1950                    nb_key_package,
1951                )
1952                .await?
1953                .into())
1954        })
1955        .await
1956    }
1957
1958    /// See [core_crypto::context::CentralContext::e2ei_rotate]
1959    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1960    pub async fn e2ei_rotate(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<CommitBundle> {
1961        self.deprecated_transaction(|context| async move { context.e2ei_rotate(&conversation_id, None).await })
1962            .await?
1963            .try_into()
1964    }
1965
1966    /// See [core_crypto::context::CentralContext::e2ei_rotate_all]
1967    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1968    pub async fn e2ei_rotate_all(
1969        &self,
1970        enrollment: std::sync::Arc<E2eiEnrollment>,
1971        certificate_chain: String,
1972        new_key_packages_count: u32,
1973    ) -> CoreCryptoResult<RotateBundle> {
1974        self.deprecated_transaction(|context| async move {
1975            context
1976                .e2ei_rotate_all(
1977                    enrollment.0.write().await.deref_mut(),
1978                    certificate_chain,
1979                    new_key_packages_count as usize,
1980                )
1981                .await
1982        })
1983        .await?
1984        .try_into()
1985    }
1986
1987    /// See [core_crypto::context::CentralContext::e2ei_enrollment_stash]
1988    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
1989    pub async fn e2ei_enrollment_stash(&self, enrollment: std::sync::Arc<E2eiEnrollment>) -> CoreCryptoResult<Vec<u8>> {
1990        let enrollment = std::sync::Arc::into_inner(enrollment).ok_or_else(|| CryptoError::LockPoisonError)?;
1991        let enrollment = std::sync::Arc::into_inner(enrollment.0)
1992            .ok_or_else(|| CryptoError::LockPoisonError)?
1993            .into_inner();
1994
1995        self.deprecated_transaction(|context| async move { context.e2ei_enrollment_stash(enrollment).await })
1996            .await
1997    }
1998
1999    /// See [core_crypto::context::CentralContext::e2ei_enrollment_stash_pop]
2000    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
2001    pub async fn e2ei_enrollment_stash_pop(&self, handle: Vec<u8>) -> CoreCryptoResult<E2eiEnrollment> {
2002        self.deprecated_transaction(|context| async move {
2003            context
2004                .e2ei_enrollment_stash_pop(handle)
2005                .await
2006                .map(async_lock::RwLock::new)
2007                .map(std::sync::Arc::new)
2008                .map(E2eiEnrollment)
2009        })
2010        .await
2011    }
2012
2013    /// See [core_crypto::context::CentralContext::e2ei_conversation_state]
2014    #[deprecated = "Please create a transaction in Core Crypto and call this method from it."]
2015    pub async fn e2ei_conversation_state(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<E2eiConversationState> {
2016        self.deprecated_transaction(|context| async move {
2017            context.e2ei_conversation_state(&conversation_id).await.map(Into::into)
2018        })
2019        .await
2020    }
2021
2022    /// See [core_crypto::mls::MlsCentral::e2ei_is_enabled]
2023    pub async fn e2ei_is_enabled(&self, ciphersuite: Ciphersuite) -> CoreCryptoResult<bool> {
2024        let sc = core_crypto::prelude::MlsCiphersuite::from(core_crypto::prelude::CiphersuiteName::from(ciphersuite))
2025            .signature_algorithm();
2026        Ok(self.central.e2ei_is_enabled(sc).await?)
2027    }
2028
2029    /// See [core_crypto::mls::MlsCentral::get_device_identities]
2030    pub async fn get_device_identities(
2031        &self,
2032        conversation_id: Vec<u8>,
2033        device_ids: Vec<ClientId>,
2034    ) -> CoreCryptoResult<Vec<WireIdentity>> {
2035        let device_ids = device_ids.into_iter().map(|cid| cid.0).collect::<Vec<_>>();
2036        Ok(self
2037            .central
2038            .get_device_identities(&conversation_id, &device_ids[..])
2039            .await?
2040            .into_iter()
2041            .map(Into::into)
2042            .collect::<Vec<_>>())
2043    }
2044
2045    /// See [core_crypto::mls::MlsCentral::get_user_identities]
2046    pub async fn get_user_identities(
2047        &self,
2048        conversation_id: Vec<u8>,
2049        user_ids: Vec<String>,
2050    ) -> CoreCryptoResult<HashMap<String, Vec<WireIdentity>>> {
2051        Ok(self
2052            .central
2053            .get_user_identities(&conversation_id, &user_ids[..])
2054            .await?
2055            .into_iter()
2056            .map(|(k, v)| (k, v.into_iter().map(Into::into).collect()))
2057            .collect::<HashMap<String, Vec<WireIdentity>>>())
2058    }
2059
2060    /// See [core_crypto::mls::MlsCentral::get_credential_in_use]
2061    pub async fn get_credential_in_use(
2062        &self,
2063        group_info: Vec<u8>,
2064        credential_type: MlsCredentialType,
2065    ) -> CoreCryptoResult<E2eiConversationState> {
2066        let group_info = VerifiableGroupInfo::tls_deserialize(&mut group_info.as_slice())
2067            .map_err(core_crypto::MlsError::from)
2068            .map_err(CryptoError::from)?;
2069        Ok(self
2070            .central
2071            .get_credential_in_use(group_info, credential_type.into())
2072            .await?
2073            .into())
2074    }
2075}
2076
2077#[derive(Debug, uniffi::Object)]
2078/// See [core_crypto::e2e_identity::E2eiEnrollment]
2079pub struct E2eiEnrollment(std::sync::Arc<async_lock::RwLock<core_crypto::prelude::E2eiEnrollment>>);
2080
2081#[uniffi::export]
2082impl E2eiEnrollment {
2083    /// See [core_crypto::e2e_identity::E2eiEnrollment::directory_response]
2084    pub async fn directory_response(&self, directory: Vec<u8>) -> CoreCryptoResult<AcmeDirectory> {
2085        Ok(self
2086            .0
2087            .write()
2088            .await
2089            .directory_response(directory)
2090            .map(AcmeDirectory::from)?)
2091    }
2092
2093    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_account_request]
2094    pub async fn new_account_request(&self, previous_nonce: String) -> CoreCryptoResult<Vec<u8>> {
2095        Ok(self.0.read().await.new_account_request(previous_nonce)?)
2096    }
2097
2098    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_account_response]
2099    pub async fn new_account_response(&self, account: Vec<u8>) -> CoreCryptoResult<()> {
2100        Ok(self.0.write().await.new_account_response(account)?)
2101    }
2102
2103    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_order_request]
2104    #[allow(clippy::too_many_arguments)]
2105    pub async fn new_order_request(&self, previous_nonce: String) -> CoreCryptoResult<Vec<u8>> {
2106        Ok(self.0.read().await.new_order_request(previous_nonce)?)
2107    }
2108
2109    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_order_response]
2110    pub async fn new_order_response(&self, order: Vec<u8>) -> CoreCryptoResult<NewAcmeOrder> {
2111        Ok(self.0.read().await.new_order_response(order)?.into())
2112    }
2113
2114    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_authz_request]
2115    pub async fn new_authz_request(&self, url: String, previous_nonce: String) -> CoreCryptoResult<Vec<u8>> {
2116        Ok(self.0.read().await.new_authz_request(url, previous_nonce)?)
2117    }
2118
2119    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_authz_response]
2120    pub async fn new_authz_response(&self, authz: Vec<u8>) -> CoreCryptoResult<NewAcmeAuthz> {
2121        Ok(self.0.write().await.new_authz_response(authz)?.into())
2122    }
2123
2124    #[allow(clippy::too_many_arguments)]
2125    /// See [core_crypto::e2e_identity::E2eiEnrollment::create_dpop_token]
2126    pub async fn create_dpop_token(&self, expiry_secs: u32, backend_nonce: String) -> CoreCryptoResult<String> {
2127        Ok(self.0.read().await.create_dpop_token(expiry_secs, backend_nonce)?)
2128    }
2129
2130    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_dpop_challenge_request]
2131    pub async fn new_dpop_challenge_request(
2132        &self,
2133        access_token: String,
2134        previous_nonce: String,
2135    ) -> CoreCryptoResult<Vec<u8>> {
2136        Ok(self
2137            .0
2138            .read()
2139            .await
2140            .new_dpop_challenge_request(access_token, previous_nonce)?)
2141    }
2142
2143    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_dpop_challenge_response]
2144    pub async fn new_dpop_challenge_response(&self, challenge: Vec<u8>) -> CoreCryptoResult<()> {
2145        Ok(self.0.read().await.new_dpop_challenge_response(challenge)?)
2146    }
2147
2148    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_oidc_challenge_request]
2149    pub async fn new_oidc_challenge_request(
2150        &self,
2151        id_token: String,
2152        refresh_token: String,
2153        previous_nonce: String,
2154    ) -> CoreCryptoResult<Vec<u8>> {
2155        Ok(self
2156            .0
2157            .write()
2158            .await
2159            .new_oidc_challenge_request(id_token, refresh_token, previous_nonce)?)
2160    }
2161
2162    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_oidc_challenge_response]
2163    #[deprecated = "Please create a transaction in Core Crypto and call Self::context_newoidc_challenge_response."]
2164    pub async fn new_oidc_challenge_response(
2165        &self,
2166        cc: std::sync::Arc<CoreCrypto>,
2167        challenge: Vec<u8>,
2168    ) -> CoreCryptoResult<()> {
2169        cc.deprecated_transaction(|context| async move {
2170            self.0
2171                .write()
2172                .await
2173                .new_oidc_challenge_response(&context.mls_provider().await?, challenge)
2174                .await
2175                .map_err(Into::into)
2176        })
2177        .await
2178    }
2179
2180    /// See [core_crypto::e2e_identity::E2eiEnrollment::new_oidc_challenge_response]
2181    pub async fn context_new_oidc_challenge_response(
2182        &self,
2183        cc: std::sync::Arc<CoreCryptoContext>,
2184        challenge: Vec<u8>,
2185    ) -> CoreCryptoResult<()> {
2186        self.0
2187            .write()
2188            .await
2189            .new_oidc_challenge_response(&cc.context.mls_provider().await?, challenge)
2190            .await?;
2191        Ok(())
2192    }
2193
2194    /// See [core_crypto::e2e_identity::E2eiEnrollment::check_order_request]
2195    pub async fn check_order_request(&self, order_url: String, previous_nonce: String) -> CoreCryptoResult<Vec<u8>> {
2196        Ok(self.0.read().await.check_order_request(order_url, previous_nonce)?)
2197    }
2198
2199    /// See [core_crypto::e2e_identity::E2eiEnrollment::check_order_response]
2200    pub async fn check_order_response(&self, order: Vec<u8>) -> CoreCryptoResult<String> {
2201        Ok(self.0.write().await.check_order_response(order)?)
2202    }
2203
2204    /// See [core_crypto::prelude::E2eiEnrollment::finalize_request]
2205    pub async fn finalize_request(&self, previous_nonce: String) -> CoreCryptoResult<Vec<u8>> {
2206        Ok(self.0.write().await.finalize_request(previous_nonce)?)
2207    }
2208
2209    /// See [core_crypto::prelude::E2eiEnrollment::finalize_response]
2210    pub async fn finalize_response(&self, finalize: Vec<u8>) -> CoreCryptoResult<String> {
2211        Ok(self.0.write().await.finalize_response(finalize)?)
2212    }
2213
2214    /// See [core_crypto::prelude::E2eiEnrollment::certificate_request]
2215    pub async fn certificate_request(&self, previous_nonce: String) -> CoreCryptoResult<Vec<u8>> {
2216        Ok(self.0.write().await.certificate_request(previous_nonce)?)
2217    }
2218
2219    /// See [core_crypto::prelude::E2eiEnrollment::get_refresh_token]
2220    pub async fn get_refresh_token(&self) -> CoreCryptoResult<String> {
2221        Ok(self.0.read().await.get_refresh_token().map(Into::into)?)
2222    }
2223}
2224
2225#[derive(Debug, uniffi::Record)]
2226/// See [core_crypto::e2e_identity::types::E2eiAcmeDirectory]
2227pub struct AcmeDirectory {
2228    pub new_nonce: String,
2229    pub new_account: String,
2230    pub new_order: String,
2231    pub revoke_cert: String,
2232}
2233
2234impl From<core_crypto::prelude::E2eiAcmeDirectory> for AcmeDirectory {
2235    fn from(directory: core_crypto::prelude::E2eiAcmeDirectory) -> Self {
2236        Self {
2237            new_nonce: directory.new_nonce,
2238            new_account: directory.new_account,
2239            new_order: directory.new_order,
2240            revoke_cert: directory.revoke_cert,
2241        }
2242    }
2243}
2244
2245impl From<AcmeDirectory> for core_crypto::prelude::E2eiAcmeDirectory {
2246    fn from(directory: AcmeDirectory) -> Self {
2247        Self {
2248            new_nonce: directory.new_nonce,
2249            new_account: directory.new_account,
2250            new_order: directory.new_order,
2251            revoke_cert: directory.revoke_cert,
2252        }
2253    }
2254}
2255
2256#[derive(Debug, uniffi::Record)]
2257/// See [core_crypto::e2e_identity::types::E2eiNewAcmeOrder]
2258pub struct NewAcmeOrder {
2259    pub delegate: Vec<u8>,
2260    pub authorizations: Vec<String>,
2261}
2262
2263impl From<core_crypto::prelude::E2eiNewAcmeOrder> for NewAcmeOrder {
2264    fn from(new_order: core_crypto::prelude::E2eiNewAcmeOrder) -> Self {
2265        Self {
2266            delegate: new_order.delegate,
2267            authorizations: new_order.authorizations,
2268        }
2269    }
2270}
2271
2272impl From<NewAcmeOrder> for core_crypto::prelude::E2eiNewAcmeOrder {
2273    fn from(new_order: NewAcmeOrder) -> Self {
2274        Self {
2275            delegate: new_order.delegate,
2276            authorizations: new_order.authorizations,
2277        }
2278    }
2279}
2280
2281#[derive(Debug, uniffi::Record)]
2282/// See [core_crypto::e2e_identity::types::E2eiNewAcmeAuthz]
2283pub struct NewAcmeAuthz {
2284    pub identifier: String,
2285    pub keyauth: Option<String>,
2286    pub challenge: AcmeChallenge,
2287}
2288
2289impl From<core_crypto::prelude::E2eiNewAcmeAuthz> for NewAcmeAuthz {
2290    fn from(new_authz: core_crypto::prelude::E2eiNewAcmeAuthz) -> Self {
2291        Self {
2292            identifier: new_authz.identifier,
2293            keyauth: new_authz.keyauth,
2294            challenge: new_authz.challenge.into(),
2295        }
2296    }
2297}
2298
2299impl From<NewAcmeAuthz> for core_crypto::prelude::E2eiNewAcmeAuthz {
2300    fn from(new_authz: NewAcmeAuthz) -> Self {
2301        Self {
2302            identifier: new_authz.identifier,
2303            keyauth: new_authz.keyauth,
2304            challenge: new_authz.challenge.into(),
2305        }
2306    }
2307}
2308
2309#[derive(Debug, uniffi::Record)]
2310/// See [core_crypto::e2e_identity::types::E2eiAcmeChallenge]
2311pub struct AcmeChallenge {
2312    pub delegate: Vec<u8>,
2313    pub url: String,
2314    pub target: String,
2315}
2316
2317impl From<core_crypto::prelude::E2eiAcmeChallenge> for AcmeChallenge {
2318    fn from(chall: core_crypto::prelude::E2eiAcmeChallenge) -> Self {
2319        Self {
2320            delegate: chall.delegate,
2321            url: chall.url,
2322            target: chall.target,
2323        }
2324    }
2325}
2326
2327impl From<AcmeChallenge> for core_crypto::prelude::E2eiAcmeChallenge {
2328    fn from(chall: AcmeChallenge) -> Self {
2329        Self {
2330            delegate: chall.delegate,
2331            url: chall.url,
2332            target: chall.target,
2333        }
2334    }
2335}