1use std::collections::BTreeMap;
14use std::sync::Arc;
15use std::time::SystemTime;
16
17use async_trait::async_trait;
18use corium_crypt::{KeyId, Keyring, SecretKey};
19use corium_log::{LogCipher, LogError};
20use corium_store::{
21 BlobId, BlobIdStream, BlobStore, EncryptedBlobStore, KeyManifest, RootStore, StoreError,
22};
23
24use crate::backend::NodeStore;
25
26pub enum DbStore {
31 Plain(Arc<NodeStore>),
33 Encrypted(EncryptedBlobStore<Arc<NodeStore>>),
36}
37
38impl DbStore {
39 #[must_use]
42 pub fn raw(&self) -> &Arc<NodeStore> {
43 match self {
44 Self::Plain(store) => store,
45 Self::Encrypted(store) => store.inner(),
46 }
47 }
48
49 #[must_use]
51 pub fn storage_epoch(&self) -> Option<u32> {
52 match self {
53 Self::Plain(_) => None,
54 Self::Encrypted(store) => Some(store.current_epoch()),
55 }
56 }
57}
58
59#[async_trait]
60impl BlobStore for DbStore {
61 async fn put(&self, bytes: &[u8]) -> Result<BlobId, StoreError> {
62 match self {
63 Self::Plain(store) => store.put(bytes).await,
64 Self::Encrypted(store) => store.put(bytes).await,
65 }
66 }
67
68 async fn get(&self, id: &BlobId) -> Result<Option<Vec<u8>>, StoreError> {
69 match self {
70 Self::Plain(store) => store.get(id).await,
71 Self::Encrypted(store) => store.get(id).await,
72 }
73 }
74
75 async fn contains(&self, id: &BlobId) -> Result<bool, StoreError> {
76 match self {
77 Self::Plain(store) => store.contains(id).await,
78 Self::Encrypted(store) => store.contains(id).await,
79 }
80 }
81
82 async fn put_if_absent(&self, bytes: &[u8]) -> Result<BlobId, StoreError> {
83 match self {
84 Self::Plain(store) => store.put_if_absent(bytes).await,
85 Self::Encrypted(store) => store.put_if_absent(bytes).await,
86 }
87 }
88
89 async fn delete(&self, id: &BlobId) -> Result<(), StoreError> {
90 self.raw().delete(id).await
91 }
92
93 async fn list(&self) -> Result<BlobIdStream, StoreError> {
94 self.raw().list().await
95 }
96
97 async fn modified_at(&self, id: &BlobId) -> Result<Option<SystemTime>, StoreError> {
98 self.raw().modified_at(id).await
99 }
100}
101
102#[async_trait]
103impl RootStore for DbStore {
104 async fn get_root(&self, name: &str) -> Result<Option<Vec<u8>>, StoreError> {
105 self.raw().get_root(name).await
106 }
107
108 async fn cas_root(
109 &self,
110 name: &str,
111 expected: Option<&[u8]>,
112 new: &[u8],
113 ) -> Result<(), StoreError> {
114 self.raw().cas_root(name, expected, new).await
115 }
116
117 async fn delete_root(&self, name: &str) -> Result<(), StoreError> {
118 self.raw().delete_root(name).await
119 }
120
121 async fn list_roots(&self, prefix: &str) -> Result<Vec<String>, StoreError> {
122 self.raw().list_roots(prefix).await
123 }
124}
125
126pub struct DbCrypto {
128 pub store: Arc<DbStore>,
130 pub cipher: Option<Arc<LogCipher>>,
134}
135
136#[derive(Debug, thiserror::Error)]
138pub enum KeyWiringError {
139 #[error(
141 "database {db:?} is encrypted under key {kek}; \
142 start this process with --storage-key naming it"
143 )]
144 NoKeyring {
145 db: String,
147 kek: KeyId,
149 },
150 #[error("database {0:?} has a key manifest with no active storage-key epoch")]
152 NoActiveEpoch(String),
153 #[error(transparent)]
155 Store(#[from] StoreError),
156 #[error(transparent)]
158 Log(#[from] LogError),
159}
160
161#[must_use]
168pub fn log_lineage(db: &str) -> Vec<u8> {
169 db.as_bytes().to_vec()
170}
171
172pub async fn resolve_db_crypto(
184 db: &str,
185 store: &Arc<NodeStore>,
186 manifest: Option<&KeyManifest>,
187 keyring: Option<&Arc<dyn Keyring>>,
188) -> Result<DbCrypto, KeyWiringError> {
189 let Some(manifest) = manifest.filter(|manifest| !manifest.storage_keys.is_empty()) else {
190 return Ok(DbCrypto {
191 store: Arc::new(DbStore::Plain(Arc::clone(store))),
192 cipher: None,
193 });
194 };
195 let Some(keyring) = keyring else {
196 return Err(KeyWiringError::NoKeyring {
197 db: db.to_owned(),
198 kek: manifest.kek.clone(),
199 });
200 };
201 let (epoch, keys) = unwrap_active(db, manifest, keyring.as_ref()).await?;
202 Ok(DbCrypto {
203 store: Arc::new(DbStore::Encrypted(EncryptedBlobStore::new(
204 Arc::clone(store),
205 epoch,
206 keys.clone(),
207 )?)),
208 cipher: Some(Arc::new(LogCipher::new(log_lineage(db), epoch, keys)?)),
209 })
210}
211
212pub async fn reload_db_crypto(
224 db: &str,
225 crypto: &DbCrypto,
226 manifest: &KeyManifest,
227 keyring: Option<&Arc<dyn Keyring>>,
228) -> Result<Arc<DbStore>, KeyWiringError> {
229 let Some(keyring) = keyring else {
230 return Err(KeyWiringError::NoKeyring {
231 db: db.to_owned(),
232 kek: manifest.kek.clone(),
233 });
234 };
235 let (epoch, keys) = unwrap_active(db, manifest, keyring.as_ref()).await?;
236 let store = Arc::new(DbStore::Encrypted(EncryptedBlobStore::new(
237 Arc::clone(crypto.store.raw()),
238 epoch,
239 keys.clone(),
240 )?));
241 if let Some(cipher) = &crypto.cipher {
242 cipher.install(epoch, keys)?;
243 }
244 Ok(store)
245}
246
247async fn unwrap_active(
248 db: &str,
249 manifest: &KeyManifest,
250 keyring: &dyn Keyring,
251) -> Result<(u32, BTreeMap<u32, SecretKey>), KeyWiringError> {
252 let epoch = manifest
253 .active_storage_epoch()
254 .ok_or_else(|| KeyWiringError::NoActiveEpoch(db.to_owned()))?;
255 Ok((epoch, manifest.unwrap_storage_keys(keyring).await?))
256}