core_crypto/proteus/
mod.rs1mod 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::{Database, entities::ProteusIdentity, traits::FetchFromDatabase as _};
12use proteus_wasm::keys::IdentityKeyPair;
13pub(crate) use session_cache::ProteusSessionCache;
14
15use crate::{KeystoreError, ProteusError, Result};
16
17#[derive(Debug)]
23pub struct ProteusCentral {
24 proteus_identity: Arc<IdentityKeyPair>,
25 proteus_sessions: ProteusSessionCache,
26}
27
28impl ProteusCentral {
29 pub async fn try_new(keystore: &Database) -> Result<Self> {
31 let proteus_identity: Arc<IdentityKeyPair> = Arc::new(Self::load_or_create_identity(keystore).await?);
32 let proteus_sessions = ProteusSessionCache::new(proteus_identity.clone());
33
34 Ok(Self {
35 proteus_identity,
36 proteus_sessions,
37 })
38 }
39
40 async fn load_or_create_identity(keystore: &Database) -> Result<IdentityKeyPair> {
44 let Some(identity) = keystore
45 .get_unique::<ProteusIdentity>()
46 .await
47 .map_err(KeystoreError::wrap("finding proteus identity"))?
48 else {
49 return Self::create_identity(keystore).await;
50 };
51
52 let sk = identity.sk_raw();
53 let pk = identity.pk_raw();
54
55 IdentityKeyPair::from_raw_key_pair(*sk, *pk)
57 .map_err(ProteusError::wrap("constructing identity keypair"))
58 .map_err(Into::into)
59 }
60
61 async fn create_identity(keystore: &Database) -> Result<IdentityKeyPair> {
63 let kp = IdentityKeyPair::new();
64 let pk = kp.public_key.public_key.as_slice().to_vec();
65
66 let ks_identity = ProteusIdentity {
67 sk: kp.secret_key.to_keypair_bytes().into(),
68 pk,
69 };
70 keystore
71 .save(ks_identity)
72 .await
73 .map_err(KeystoreError::wrap("saving new proteus identity"))?;
74
75 Ok(kp)
76 }
77
78 pub fn identity(&self) -> &IdentityKeyPair {
80 self.proteus_identity.as_ref()
81 }
82
83 pub fn fingerprint(&self) -> String {
85 self.proteus_identity.as_ref().public_key.fingerprint()
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use core_crypto_keystore::{ConnectionType, DatabaseKey};
92
93 use super::*;
94 use crate::test_utils::*;
95
96 #[macro_rules_attribute::apply(smol_macros::test)]
97 async fn can_init() {
98 #[cfg(not(target_os = "unknown"))]
99 let (path, db_file) = tmp_db_file();
100 #[cfg(target_os = "unknown")]
101 let (path, _) = tmp_db_file();
102 let key = DatabaseKey::generate();
103 let keystore = core_crypto_keystore::Database::open(ConnectionType::Persistent(&path), &key)
104 .await
105 .unwrap();
106 keystore.new_transaction().await.unwrap();
107 let central = ProteusCentral::try_new(&keystore).await.unwrap();
108 let identity = (*central.proteus_identity).clone();
109 keystore.commit_transaction().await.unwrap();
110
111 let keystore = core_crypto_keystore::Database::open(ConnectionType::Persistent(&path), &key)
112 .await
113 .unwrap();
114 keystore.new_transaction().await.unwrap();
115 let central = ProteusCentral::try_new(&keystore).await.unwrap();
116 keystore.commit_transaction().await.unwrap();
117 assert_eq!(identity, *central.proteus_identity);
118
119 keystore.wipe().await.unwrap();
120 #[cfg(not(target_os = "unknown"))]
121 drop(db_file);
122 }
123}