grafos_securestore/
blob.rs

1//! Encrypted blob storage backed by epoch-scoped keys.
2
3extern crate alloc;
4use alloc::collections::BTreeMap;
5use alloc::string::ToString;
6use alloc::vec::Vec;
7
8use grafos_locator::locator::MemRegionLocator;
9use serde::{Deserialize, Serialize};
10
11use crate::epoch::EpochId;
12use crate::error::SecureStoreError;
13use crate::manager::KeyEpochManager;
14
15/// Unique identifier for an encrypted blob.
16#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
17pub struct BlobId(pub u64);
18
19/// Metadata for an encrypted blob, including the epoch used for encryption
20/// and the cryptographic parameters needed for decryption.
21#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
22pub struct BlobInfo {
23    pub blob_id: BlobId,
24    pub epoch_id: EpochId,
25    pub ciphertext_locator: MemRegionLocator,
26    pub nonce: Vec<u8>,
27    pub aad: Vec<u8>,
28}
29
30/// Encrypted blob store that uses epoch-scoped keys for encryption.
31///
32/// Blobs are encrypted with the active epoch's key at insertion time. The
33/// epoch ID, nonce, and AAD are recorded in [`BlobInfo`] so the correct key
34/// can be resolved at decryption time.
35///
36/// If the epoch has expired or is missing, decryption fails closed.
37pub struct EncryptedBlobStore {
38    key_manager: KeyEpochManager,
39    blobs: BTreeMap<BlobId, BlobEntry>,
40}
41
42struct BlobEntry {
43    ciphertext: Vec<u8>,
44}
45
46impl EncryptedBlobStore {
47    /// Create a new blob store backed by the given key manager.
48    pub fn new(key_manager: KeyEpochManager) -> Self {
49        Self {
50            key_manager,
51            blobs: BTreeMap::new(),
52        }
53    }
54
55    /// Encrypt `plaintext` with the active epoch key and store it.
56    ///
57    /// Returns the [`BlobInfo`] needed to later decrypt the blob.
58    ///
59    /// # Errors
60    ///
61    /// - [`SecureStoreError::NoActiveEpoch`] if no epoch is active.
62    /// - [`SecureStoreError::CryptoError`] if encryption fails.
63    pub fn put(&mut self, blob_id: BlobId, plaintext: &[u8]) -> Result<BlobInfo, SecureStoreError> {
64        let active = self
65            .key_manager
66            .active_epoch()
67            .ok_or(SecureStoreError::NoActiveEpoch)?;
68        let epoch_id = active.epoch_id;
69
70        let key = self.key_manager.get_key(epoch_id)?;
71        let nonce = self.key_manager.crypto().generate_nonce();
72        let aad = Vec::new();
73
74        let ciphertext = self
75            .key_manager
76            .crypto()
77            .encrypt(key, &nonce, &aad, plaintext)
78            .map_err(|e| SecureStoreError::CryptoError(e.0.to_string()))?;
79
80        let ciphertext_locator =
81            MemRegionLocator::new(epoch_id.0 as u128, 0, ciphertext.len() as u64);
82
83        let info = BlobInfo {
84            blob_id,
85            epoch_id,
86            ciphertext_locator,
87            nonce,
88            aad,
89        };
90
91        self.blobs.insert(blob_id, BlobEntry { ciphertext });
92
93        Ok(info)
94    }
95
96    /// Decrypt and return the plaintext for a previously stored blob.
97    ///
98    /// The `blob_info` provides the epoch ID, nonce, and AAD needed for
99    /// decryption. The epoch's key must still be available (not expired).
100    ///
101    /// # Errors
102    ///
103    /// - [`SecureStoreError::EpochExpired`] if the epoch has expired.
104    /// - [`SecureStoreError::EpochNotFound`] if the epoch does not exist.
105    /// - [`SecureStoreError::StorageError`] if the blob is not found.
106    /// - [`SecureStoreError::CryptoError`] if decryption fails.
107    pub fn get(&self, blob_info: &BlobInfo) -> Result<Vec<u8>, SecureStoreError> {
108        let key = self.key_manager.get_key(blob_info.epoch_id)?;
109
110        let entry = self
111            .blobs
112            .get(&blob_info.blob_id)
113            .ok_or_else(|| SecureStoreError::StorageError("blob not found".into()))?;
114
115        self.key_manager
116            .crypto()
117            .decrypt(key, &blob_info.nonce, &blob_info.aad, &entry.ciphertext)
118            .map_err(|e| SecureStoreError::CryptoError(e.0.to_string()))
119    }
120
121    /// Remove a blob from the store.
122    ///
123    /// # Errors
124    ///
125    /// - [`SecureStoreError::StorageError`] if the blob does not exist.
126    pub fn remove(&mut self, blob_id: &BlobId) -> Result<(), SecureStoreError> {
127        self.blobs
128            .remove(blob_id)
129            .map(|_| ())
130            .ok_or_else(|| SecureStoreError::StorageError("blob not found".into()))
131    }
132
133    /// Access the underlying key manager.
134    pub fn key_manager(&self) -> &KeyEpochManager {
135        &self.key_manager
136    }
137
138    /// Mutable access to the underlying key manager (for rotation/expiry).
139    pub fn key_manager_mut(&mut self) -> &mut KeyEpochManager {
140        &mut self.key_manager
141    }
142}