coven_crypto/custody/
mod.rs1use crate::{MaterialError, MemberIdentity, SecretBytes, StoreKeyring};
4use coven_foundation::files::StoreFile;
5use std::sync::Arc;
6
7#[cfg(any(target_os = "android", test, feature = "test-utils"))]
8mod block_store;
9#[cfg(target_os = "android")]
10mod block_store_jni;
11#[cfg(any(test, feature = "test-utils"))]
12mod block_store_test_utils;
13mod error;
14mod key_session;
15mod keychain;
16mod memory;
17mod passphrase;
18mod person_entries;
19mod platform;
20mod store_custody;
21
22pub use error::{KeyError, KeychainError};
23pub use key_session::KeySession;
24pub use keychain::{Keychain, KeyringCustody, StoreKeychain};
25pub use memory::InMemoryCustody;
26pub use passphrase::{Passphrase, PassphraseCustody};
27pub use person_entries::{PersonEntries, PersonRestoreCode, RestoreCodeChange};
28pub use store_custody::{IdentityError, StoreCustody};
29
30mod sealed {
31 pub trait Sealed {}
32}
33
34pub trait KeyMaterial: sealed::Sealed + Clone + Send + Sync + 'static {
37 const ENTRY: &'static str;
39 const FILE: StoreFile;
41 fn to_secret_bytes(&self) -> SecretBytes;
43 fn from_secret_bytes(bytes: &[u8]) -> Result<Self, MaterialError>;
45}
46
47impl sealed::Sealed for StoreKeyring {}
48impl sealed::Sealed for MemberIdentity {}
49
50impl KeyMaterial for StoreKeyring {
51 const ENTRY: &'static str = "store-keys";
52 const FILE: StoreFile = StoreFile::StoreKeys;
53 fn to_secret_bytes(&self) -> SecretBytes {
54 Self::to_secret_bytes(self)
55 }
56 fn from_secret_bytes(bytes: &[u8]) -> Result<Self, MaterialError> {
57 Self::from_secret_bytes(bytes)
58 }
59}
60
61impl KeyMaterial for MemberIdentity {
62 const ENTRY: &'static str = "member-keys";
63 const FILE: StoreFile = StoreFile::MemberKeys;
64 fn to_secret_bytes(&self) -> SecretBytes {
65 Self::to_secret_bytes(self)
66 }
67 fn from_secret_bytes(bytes: &[u8]) -> Result<Self, MaterialError> {
68 Self::from_secret_bytes(bytes)
69 }
70}
71
72pub trait Custody<T: KeyMaterial>: Send + Sync {
74 fn unlock(&self) -> Result<Option<T>, KeyError>;
77 fn persist(&self, material: &T) -> Result<(), KeyError>;
79 fn forget(&self) -> Result<(), KeyError>;
81 fn close(&self);
83}
84
85pub enum CustodyChoice<T: KeyMaterial> {
87 Keyring,
89 Passphrase(Passphrase),
91 InMemory,
94 Custom(Arc<dyn Custody<T>>),
96}
97
98impl<T: KeyMaterial> std::fmt::Debug for CustodyChoice<T> {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 f.write_str(match self {
101 Self::Keyring => "CustodyChoice::Keyring",
102 Self::Passphrase(_) => "CustodyChoice::Passphrase([REDACTED])",
103 Self::InMemory => "CustodyChoice::InMemory",
104 Self::Custom(_) => "CustodyChoice::Custom(..)",
105 })
106 }
107}
108
109impl<T: KeyMaterial> Custody<T> for InMemoryCustody<T> {
110 fn unlock(&self) -> Result<Option<T>, KeyError> {
111 Ok(self.read())
112 }
113 fn persist(&self, material: &T) -> Result<(), KeyError> {
114 self.write(material);
115 Ok(())
116 }
117 fn forget(&self) -> Result<(), KeyError> {
118 self.remove();
119 Ok(())
120 }
121 fn close(&self) {
122 self.remove();
123 }
124}
125
126impl<T: KeyMaterial> Custody<T> for PassphraseCustody<T> {
127 fn unlock(&self) -> Result<Option<T>, KeyError> {
128 self.read(T::ENTRY)?
129 .map(|bytes| T::from_secret_bytes(bytes.as_bytes()).map_err(KeyError::from))
130 .transpose()
131 }
132 fn persist(&self, material: &T) -> Result<(), KeyError> {
133 self.write(T::ENTRY, material.to_secret_bytes().as_bytes())
134 }
135 fn forget(&self) -> Result<(), KeyError> {
136 self.remove()
137 }
138 fn close(&self) {
139 self.release();
140 }
141}
142
143impl<T: KeyMaterial> Custody<T> for KeyringCustody<T> {
144 fn unlock(&self) -> Result<Option<T>, KeyError> {
145 self.read(T::ENTRY)?
146 .map(|bytes| T::from_secret_bytes(bytes.as_bytes()).map_err(KeyError::from))
147 .transpose()
148 }
149 fn persist(&self, material: &T) -> Result<(), KeyError> {
150 self.write(T::ENTRY, material.to_secret_bytes().as_bytes())
151 }
152 fn forget(&self) -> Result<(), KeyError> {
153 self.remove(T::ENTRY)
154 }
155 fn close(&self) {}
156}
157
158#[cfg(test)]
159#[path = "mod_tests.rs"]
160mod tests;