1use rand::RngCore;
2
3use crate::block_store::BlockStore;
4use crate::crypto::{decrypt_object, encrypt_object, CryptoEngine};
5use crate::error::{FsError, FsResult};
6use crate::model::*;
7
8fn random_block(size: usize) -> Vec<u8> {
10 let mut buf = vec![0u8; size];
11 rand::thread_rng().fill_bytes(&mut buf);
12 buf
13}
14
15pub trait ObjectCodec: Send + Sync {
17 fn serialize_object<T: serde::Serialize>(&self, obj: &T) -> FsResult<Vec<u8>>;
18 fn deserialize_object<T: serde::de::DeserializeOwned>(&self, bytes: &[u8]) -> FsResult<T>;
19}
20
21pub struct PostcardCodec;
23
24impl ObjectCodec for PostcardCodec {
25 fn serialize_object<T: serde::Serialize>(&self, obj: &T) -> FsResult<Vec<u8>> {
26 postcard::to_allocvec(obj).map_err(|e| FsError::Serialization(e.to_string()))
27 }
28
29 fn deserialize_object<T: serde::de::DeserializeOwned>(&self, bytes: &[u8]) -> FsResult<T> {
30 postcard::from_bytes(bytes).map_err(|e| FsError::Deserialization(e.to_string()))
31 }
32}
33
34pub fn write_encrypted_object<T: serde::Serialize>(
36 store: &dyn BlockStore,
37 crypto: &dyn CryptoEngine,
38 codec: &PostcardCodec,
39 block_id: u64,
40 kind: ObjectKind,
41 obj: &T,
42) -> FsResult<()> {
43 let block = prepare_encrypted_object(store.block_size(), crypto, codec, kind, obj)?;
44 store.write_block(block_id, &block)
45}
46
47pub fn prepare_encrypted_object<T: serde::Serialize>(
50 block_size: usize,
51 crypto: &dyn CryptoEngine,
52 codec: &PostcardCodec,
53 kind: ObjectKind,
54 obj: &T,
55) -> FsResult<Vec<u8>> {
56 let plaintext = codec.serialize_object(obj)?;
57 prepare_encrypted_block(block_size, crypto, codec, kind, &plaintext)
58}
59
60pub fn read_encrypted_object<T: serde::de::DeserializeOwned>(
62 store: &dyn BlockStore,
63 crypto: &dyn CryptoEngine,
64 codec: &PostcardCodec,
65 block_id: u64,
66) -> FsResult<T> {
67 let plaintext = decrypt_block_to_plaintext(store, crypto, codec, block_id)?;
68 codec.deserialize_object(&plaintext)
69}
70
71pub fn decrypt_block_to_plaintext(
74 store: &dyn BlockStore,
75 crypto: &dyn CryptoEngine,
76 codec: &PostcardCodec,
77 block_id: u64,
78) -> FsResult<Vec<u8>> {
79 let block = store.read_block(block_id)?;
80
81 if block.len() < 4 {
82 return Err(FsError::Deserialization("block too small".into()));
83 }
84
85 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
86 if len == 0 || 4 + len > block.len() {
87 return Err(FsError::ObjectNotFound(block_id));
88 }
89
90 let envelope_bytes = &block[4..4 + len];
91 let encrypted: EncryptedObject = codec.deserialize_object(envelope_bytes)?;
92 decrypt_object(crypto, &encrypted)
93}
94
95pub fn write_encrypted_raw(
97 store: &dyn BlockStore,
98 crypto: &dyn CryptoEngine,
99 codec: &PostcardCodec,
100 block_id: u64,
101 kind: ObjectKind,
102 raw_data: &[u8],
103) -> FsResult<()> {
104 let block = prepare_encrypted_block(store.block_size(), crypto, codec, kind, raw_data)?;
105 store.write_block(block_id, &block)
106}
107
108pub fn prepare_encrypted_block(
111 block_size: usize,
112 crypto: &dyn CryptoEngine,
113 codec: &PostcardCodec,
114 kind: ObjectKind,
115 raw_data: &[u8],
116) -> FsResult<Vec<u8>> {
117 let encrypted = encrypt_object(crypto, kind, raw_data)?;
118 let envelope_bytes = codec.serialize_object(&encrypted)?;
119
120 if envelope_bytes.len() > block_size {
121 return Err(FsError::DataTooLarge(envelope_bytes.len()));
122 }
123
124 let mut block = random_block(block_size);
125 let len = envelope_bytes.len() as u32;
126 block[..4].copy_from_slice(&len.to_le_bytes());
127 block[4..4 + envelope_bytes.len()].copy_from_slice(&envelope_bytes);
128
129 Ok(block)
130}
131
132pub fn read_encrypted_raw(
134 store: &dyn BlockStore,
135 crypto: &dyn CryptoEngine,
136 codec: &PostcardCodec,
137 block_id: u64,
138) -> FsResult<Vec<u8>> {
139 let block = store.read_block(block_id)?;
140
141 if block.len() < 4 {
142 return Err(FsError::Deserialization("block too small".into()));
143 }
144
145 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
146 if len == 0 || 4 + len > block.len() {
147 return Err(FsError::ObjectNotFound(block_id));
148 }
149
150 let envelope_bytes = &block[4..4 + len];
151 let encrypted: EncryptedObject = codec.deserialize_object(envelope_bytes)?;
152 decrypt_object(crypto, &encrypted)
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::block_store::MemoryBlockStore;
159 use crate::crypto::ChaChaEngine;
160
161 #[test]
162 fn test_write_read_encrypted_object() {
163 let store = MemoryBlockStore::new(4096, 16);
164 let engine = ChaChaEngine::generate().unwrap();
165 let codec = PostcardCodec;
166
167 let inode = Inode {
168 id: 42,
169 kind: InodeKind::File,
170 size: 1024,
171 directory_page_ref: ObjectRef::null(),
172 extent_map_ref: ObjectRef::new(5),
173 created_at: 1000,
174 modified_at: 2000,
175 };
176
177 write_encrypted_object(&store, &engine, &codec, 3, ObjectKind::Inode, &inode).unwrap();
178 let recovered: Inode = read_encrypted_object(&store, &engine, &codec, 3).unwrap();
179
180 assert_eq!(recovered.id, 42);
181 assert_eq!(recovered.size, 1024);
182 assert_eq!(recovered.kind, InodeKind::File);
183 }
184}