doublecrypt_core/
codec.rs1use 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 plaintext = codec.serialize_object(obj)?;
44 let encrypted = encrypt_object(crypto, kind, &plaintext)?;
45 let envelope_bytes = codec.serialize_object(&encrypted)?;
46
47 let block_size = store.block_size();
48 if envelope_bytes.len() > block_size {
49 return Err(FsError::DataTooLarge(envelope_bytes.len()));
50 }
51
52 let mut block = random_block(block_size);
54 let len = envelope_bytes.len() as u32;
56 block[..4].copy_from_slice(&len.to_le_bytes());
57 block[4..4 + envelope_bytes.len()].copy_from_slice(&envelope_bytes);
58
59 store.write_block(block_id, &block)
60}
61
62pub fn read_encrypted_object<T: serde::de::DeserializeOwned>(
64 store: &dyn BlockStore,
65 crypto: &dyn CryptoEngine,
66 codec: &PostcardCodec,
67 block_id: u64,
68) -> FsResult<T> {
69 let plaintext = decrypt_block_to_plaintext(store, crypto, codec, block_id)?;
70 codec.deserialize_object(&plaintext)
71}
72
73pub fn decrypt_block_to_plaintext(
76 store: &dyn BlockStore,
77 crypto: &dyn CryptoEngine,
78 codec: &PostcardCodec,
79 block_id: u64,
80) -> FsResult<Vec<u8>> {
81 let block = store.read_block(block_id)?;
82
83 if block.len() < 4 {
84 return Err(FsError::Deserialization("block too small".into()));
85 }
86
87 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
88 if len == 0 || 4 + len > block.len() {
89 return Err(FsError::ObjectNotFound(block_id));
90 }
91
92 let envelope_bytes = &block[4..4 + len];
93 let encrypted: EncryptedObject = codec.deserialize_object(envelope_bytes)?;
94 decrypt_object(crypto, &encrypted)
95}
96
97pub fn write_encrypted_raw(
99 store: &dyn BlockStore,
100 crypto: &dyn CryptoEngine,
101 codec: &PostcardCodec,
102 block_id: u64,
103 kind: ObjectKind,
104 raw_data: &[u8],
105) -> FsResult<()> {
106 let block = prepare_encrypted_block(store.block_size(), crypto, codec, kind, raw_data)?;
107 store.write_block(block_id, &block)
108}
109
110pub fn prepare_encrypted_block(
113 block_size: usize,
114 crypto: &dyn CryptoEngine,
115 codec: &PostcardCodec,
116 kind: ObjectKind,
117 raw_data: &[u8],
118) -> FsResult<Vec<u8>> {
119 let encrypted = encrypt_object(crypto, kind, raw_data)?;
120 let envelope_bytes = codec.serialize_object(&encrypted)?;
121
122 if envelope_bytes.len() > block_size {
123 return Err(FsError::DataTooLarge(envelope_bytes.len()));
124 }
125
126 let mut block = random_block(block_size);
127 let len = envelope_bytes.len() as u32;
128 block[..4].copy_from_slice(&len.to_le_bytes());
129 block[4..4 + envelope_bytes.len()].copy_from_slice(&envelope_bytes);
130
131 Ok(block)
132}
133
134pub fn read_encrypted_raw(
136 store: &dyn BlockStore,
137 crypto: &dyn CryptoEngine,
138 codec: &PostcardCodec,
139 block_id: u64,
140) -> FsResult<Vec<u8>> {
141 let block = store.read_block(block_id)?;
142
143 if block.len() < 4 {
144 return Err(FsError::Deserialization("block too small".into()));
145 }
146
147 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
148 if len == 0 || 4 + len > block.len() {
149 return Err(FsError::ObjectNotFound(block_id));
150 }
151
152 let envelope_bytes = &block[4..4 + len];
153 let encrypted: EncryptedObject = codec.deserialize_object(envelope_bytes)?;
154 decrypt_object(crypto, &encrypted)
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::block_store::MemoryBlockStore;
161 use crate::crypto::ChaChaEngine;
162
163 #[test]
164 fn test_write_read_encrypted_object() {
165 let store = MemoryBlockStore::new(4096, 16);
166 let engine = ChaChaEngine::generate().unwrap();
167 let codec = PostcardCodec;
168
169 let inode = Inode {
170 id: 42,
171 kind: InodeKind::File,
172 size: 1024,
173 directory_page_ref: ObjectRef::null(),
174 extent_map_ref: ObjectRef::new(5),
175 created_at: 1000,
176 modified_at: 2000,
177 };
178
179 write_encrypted_object(&store, &engine, &codec, 3, ObjectKind::Inode, &inode).unwrap();
180 let recovered: Inode = read_encrypted_object(&store, &engine, &codec, 3).unwrap();
181
182 assert_eq!(recovered.id, 42);
183 assert_eq!(recovered.size, 1024);
184 assert_eq!(recovered.kind, InodeKind::File);
185 }
186}