Skip to main content

coven_crypto/custody/
key_session.rs

1//! One unlocked custody session shared by the handle's workers.
2
3use super::{Custody, KeyError, KeyMaterial};
4use coven_foundation::locks::lock_unpoisoned;
5use std::sync::{Arc, Mutex};
6
7/// Held keys and the capability that saves them, released together on close.
8/// Consumers read memory; only opening calls the configured custody's unlock.
9/// Share this owner across every worker using the same custody.
10pub struct KeySession<T: KeyMaterial> {
11    held: Mutex<Option<Held<T>>>,
12}
13
14struct Held<T: KeyMaterial> {
15    keys: Option<T>,
16    custody: Arc<dyn Custody<T>>,
17}
18
19impl<T: KeyMaterial> KeySession<T> {
20    /// Unlock custody once at the opening composition root.
21    pub fn open(custody: Arc<dyn Custody<T>>) -> Result<Self, KeyError> {
22        // The guard also closes a backend that fails partway through unlocking.
23        let mut held = Held { keys: None, custody };
24        held.keys = held.custody.unlock()?;
25        Ok(Self { held: Mutex::new(Some(held)) })
26    }
27
28    /// Read an independent copy of the held material without custody IO.
29    pub fn read(&self) -> Result<Option<T>, KeyError> {
30        let held = lock_unpoisoned(&self.held);
31        Ok(held.as_ref().ok_or(KeyError::StoreClosed)?.keys.clone())
32    }
33
34    /// Save changed keys before installing them in the shared session.
35    /// A failed save leaves the held material unchanged and reaches the caller.
36    pub fn persist(&self, keys: &T) -> Result<(), KeyError> {
37        let mut held = lock_unpoisoned(&self.held);
38        let held = held.as_mut().ok_or(KeyError::StoreClosed)?;
39        held.custody.persist(keys)?;
40        held.keys = Some(keys.clone());
41        Ok(())
42    }
43
44    /// Remove persisted keys before erasing the session's copy. A failed removal
45    /// retains the held keys. The persistence capability lasts until close, so
46    /// acquiring keys again does not require another passphrase derivation.
47    pub fn forget(&self) -> Result<(), KeyError> {
48        let mut held = lock_unpoisoned(&self.held);
49        let held = held.as_mut().ok_or(KeyError::StoreClosed)?;
50        held.custody.forget()?;
51        held.keys = None;
52        Ok(())
53    }
54
55    /// Erase the held material and close custody after its users finish.
56    /// Also happens when the last session owner is dropped. Performs no IO.
57    pub fn close(&self) {
58        lock_unpoisoned(&self.held).take();
59    }
60}
61
62impl<T: KeyMaterial> Drop for Held<T> {
63    fn drop(&mut self) {
64        self.keys.take();
65        self.custody.close();
66    }
67}
68
69impl<T: KeyMaterial> std::fmt::Debug for KeySession<T> {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        f.write_str("KeySession([REDACTED])")
72    }
73}
74
75#[cfg(test)]
76#[path = "key_session_tests.rs"]
77mod tests;