Skip to main content

core_crypto/proteus/
mod.rs

1mod conversation_session;
2mod core_crypto;
3mod message;
4mod prekey;
5mod session;
6mod session_cache;
7
8use std::sync::Arc;
9
10pub use conversation_session::{ProteusConversationSession, SessionIdentifier};
11use core_crypto_keystore::{
12    Transaction,
13    entities::ProteusIdentity,
14    traits::{EntityDatabaseMutation as _, FetchFromDatabase as _},
15};
16use proteus_wasm::keys::IdentityKeyPair;
17pub(crate) use session_cache::ProteusSessionCache;
18
19use crate::{KeystoreError, ProteusError, Result};
20
21/// Proteus counterpart of [crate::mls::session::Session]
22///
23/// The big difference is that [ProteusCentral] doesn't *own* its own keystore but must borrow it from the outside.
24/// Whether it's exclusively for this struct's purposes or it's shared with our main struct,
25/// [crate::mls::session::Session]
26#[derive(Debug)]
27pub struct ProteusCentral {
28    proteus_identity: Arc<IdentityKeyPair>,
29    proteus_sessions: ProteusSessionCache,
30}
31
32impl ProteusCentral {
33    /// Initializes the [ProteusCentral]
34    pub async fn try_new(transaction: &Transaction) -> Result<Self> {
35        let proteus_identity: Arc<IdentityKeyPair> = Arc::new(Self::load_or_create_identity(transaction).await?);
36        let proteus_sessions = ProteusSessionCache::new(proteus_identity.clone());
37
38        Ok(Self {
39            proteus_identity,
40            proteus_sessions,
41        })
42    }
43
44    /// This function will try to load a proteus Identity from our keystore; If it cannot, it will create a new one
45    /// This means this function doesn't fail except in cases of deeper errors (such as in the Keystore and other crypto
46    /// errors)
47    async fn load_or_create_identity(transaction: &Transaction) -> Result<IdentityKeyPair> {
48        let Some(identity) = transaction
49            .get_unique::<ProteusIdentity>()
50            .await
51            .map_err(KeystoreError::wrap("finding proteus identity"))?
52        else {
53            return Self::create_identity(transaction);
54        };
55
56        let sk = identity
57            .sk_raw()
58            .map_err(KeystoreError::wrap("reading the stored proteus identity secret key"))?;
59        let pk = identity
60            .pk_raw()
61            .map_err(KeystoreError::wrap("reading the stored proteus identity public key"))?;
62
63        IdentityKeyPair::from_raw_key_pair(*sk, *pk)
64            .map_err(ProteusError::wrap("constructing identity keypair"))
65            .map_err(Into::into)
66    }
67
68    /// Internal function to create and save a new Proteus Identity
69    fn create_identity(transaction: &Transaction) -> Result<IdentityKeyPair> {
70        let kp = IdentityKeyPair::new();
71        let pk = kp.public_key.public_key.as_slice().to_vec();
72
73        let ks_identity = ProteusIdentity {
74            sk: kp.secret_key.to_keypair_bytes().into(),
75            pk,
76        };
77        ks_identity
78            .save(transaction)
79            .map_err(KeystoreError::wrap("saving new proteus identity"))?;
80
81        Ok(kp)
82    }
83
84    /// Proteus identity keypair
85    pub fn identity(&self) -> &IdentityKeyPair {
86        self.proteus_identity.as_ref()
87    }
88
89    /// Proteus Public key hex-encoded fingerprint
90    pub fn fingerprint(&self) -> String {
91        self.proteus_identity.as_ref().public_key.fingerprint()
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use core_crypto_keystore::DatabaseKey;
98
99    use super::*;
100    use crate::test_utils::*;
101
102    #[macro_rules_attribute::apply(smol_macros::test)]
103    async fn can_init() {
104        #[cfg(not(target_os = "unknown"))]
105        let (path, db_file) = tmp_db_file();
106        #[cfg(target_os = "unknown")]
107        let (path, _) = tmp_db_file();
108        let key = DatabaseKey::generate();
109        let keystore = core_crypto_keystore::Database::open(&path, &key).await.unwrap();
110        let tx = keystore.new_transaction().await.unwrap();
111        let central = ProteusCentral::try_new(&tx).await.unwrap();
112        let identity = (*central.proteus_identity).clone();
113        tx.commit().await.unwrap();
114
115        let keystore = core_crypto_keystore::Database::open(&path, &key).await.unwrap();
116        let tx = keystore.new_transaction().await.unwrap();
117        let central = ProteusCentral::try_new(&tx).await.unwrap();
118        tx.commit().await.unwrap();
119        assert_eq!(identity, *central.proteus_identity);
120
121        #[cfg(not(target_os = "unknown"))]
122        drop(db_file);
123    }
124}