Skip to main content

coven_crypto/custody/
store_custody.rs

1//! Member identity and host secrets, without exposing retained custody.
2
3use super::{KeyError, KeySession, StoreKeychain};
4use crate::{CryptoError, MemberIdentity, StoreKeyring};
5use coven_foundation::id_source::MemberId;
6use std::sync::Arc;
7
8/// Initializing this device's member identity failed (E11).
9#[derive(Debug, thiserror::Error)]
10pub enum IdentityError {
11    /// Custody already holds member keys.
12    #[error("member identity already initialized")]
13    AlreadyInitialized,
14    /// Making the two key pairs failed.
15    #[error(transparent)]
16    Crypto(#[from] CryptoError),
17    /// Reading or persisting identity custody failed.
18    #[error(transparent)]
19    Custody(KeyError),
20    /// Initializing the identity panicked: a bug, which the caller reports
21    /// (E15).
22    #[error(transparent)]
23    Bug(#[from] coven_foundation::task::Bug),
24}
25
26impl From<KeyError> for IdentityError {
27    fn from(error: KeyError) -> Self {
28        match error {
29            KeyError::Bug(bug) => Self::Bug(bug),
30            error => Self::Custody(error),
31        }
32    }
33}
34
35impl coven_foundation::task::CarriesBug for IdentityError {
36    fn bug(&self) -> Option<&coven_foundation::task::Bug> {
37        match self {
38            Self::Bug(bug) => Some(bug),
39            Self::Custody(error) => error.bug(),
40            Self::AlreadyInitialized | Self::Crypto(_) => None,
41        }
42    }
43}
44
45/// The store's identity and host secrets, retaining their custody and keychain.
46/// Shares the unlocked sessions used by the handle and its workers.
47pub struct StoreCustody {
48    identity: Arc<KeySession<MemberIdentity>>,
49    keys: Arc<KeySession<StoreKeyring>>,
50    keychain: Arc<StoreKeychain>,
51}
52
53impl StoreCustody {
54    /// Compose already chosen custody at the application opening root.
55    pub fn new(
56        keys: Arc<KeySession<StoreKeyring>>,
57        identity: Arc<KeySession<MemberIdentity>>,
58        keychain: Arc<StoreKeychain>,
59    ) -> Self {
60        Self { keys, identity, keychain }
61    }
62
63    /// Release both sessions after the handle has stopped their users.
64    pub fn close(&self) {
65        self.keys.close();
66        self.identity.close();
67    }
68
69    /// Makes this member's two key pairs for the independently generated
70    /// `member`, for the person creating a store, who then puts them in
71    /// identity custody. Fails if custody already holds keys. It writes
72    /// nothing, so a creation that fails here has nothing of its own to
73    /// take back. Joining and restoring put their identity there
74    /// themselves.
75    pub fn new_identity(&self, member: MemberId) -> Result<MemberIdentity, IdentityError> {
76        if self.identity.read()?.is_some() {
77            return Err(IdentityError::AlreadyInitialized);
78        }
79        Ok(MemberIdentity::generate(member)?)
80    }
81
82    /// Records the name before saving the value in its own device-only entry,
83    /// so deleting the store can remove every saved secret. Names are arbitrary
84    /// strings; each value retains the platform's per-entry size limit.
85    pub fn set_host_secret(&self, name: &str, value: &str) -> Result<(), KeyError> {
86        self.keychain.set_host_secret(name, value)
87    }
88
89    /// The secret, or `None` if it was never set.
90    pub fn host_secret(&self, name: &str) -> Result<Option<String>, KeyError> {
91        self.keychain.host_secret(name)
92    }
93
94    /// Deletes the secret before its recorded name; succeeds if absent.
95    /// Failure may leave an extra name; retrying is safe.
96    pub fn delete_host_secret(&self, name: &str) -> Result<(), KeyError> {
97        self.keychain.delete_host_secret(name)
98    }
99}
100
101#[cfg(test)]
102#[path = "store_custody_tests.rs"]
103mod tests;