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 block = store.read_block(block_id)?;
70
71 if block.len() < 4 {
72 return Err(FsError::Deserialization("block too small".into()));
73 }
74
75 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
76 if len == 0 || 4 + len > block.len() {
77 return Err(FsError::ObjectNotFound(block_id));
78 }
79
80 let envelope_bytes = &block[4..4 + len];
81 let encrypted: EncryptedObject = codec.deserialize_object(envelope_bytes)?;
82 let plaintext = decrypt_object(crypto, &encrypted)?;
83 codec.deserialize_object(&plaintext)
84}
85
86pub fn write_encrypted_raw(
88 store: &dyn BlockStore,
89 crypto: &dyn CryptoEngine,
90 codec: &PostcardCodec,
91 block_id: u64,
92 kind: ObjectKind,
93 raw_data: &[u8],
94) -> FsResult<()> {
95 let encrypted = encrypt_object(crypto, kind, raw_data)?;
96 let envelope_bytes = codec.serialize_object(&encrypted)?;
97
98 let block_size = store.block_size();
99 if envelope_bytes.len() > block_size {
100 return Err(FsError::DataTooLarge(envelope_bytes.len()));
101 }
102
103 let mut block = random_block(block_size);
104 let len = envelope_bytes.len() as u32;
105 block[..4].copy_from_slice(&len.to_le_bytes());
106 block[4..4 + envelope_bytes.len()].copy_from_slice(&envelope_bytes);
107
108 store.write_block(block_id, &block)
109}
110
111pub fn read_encrypted_raw(
113 store: &dyn BlockStore,
114 crypto: &dyn CryptoEngine,
115 codec: &PostcardCodec,
116 block_id: u64,
117) -> FsResult<Vec<u8>> {
118 let block = store.read_block(block_id)?;
119
120 if block.len() < 4 {
121 return Err(FsError::Deserialization("block too small".into()));
122 }
123
124 let len = u32::from_le_bytes([block[0], block[1], block[2], block[3]]) as usize;
125 if len == 0 || 4 + len > block.len() {
126 return Err(FsError::ObjectNotFound(block_id));
127 }
128
129 let envelope_bytes = &block[4..4 + len];
130 let encrypted: EncryptedObject = codec.deserialize_object(envelope_bytes)?;
131 decrypt_object(crypto, &encrypted)
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::block_store::MemoryBlockStore;
138 use crate::crypto::ChaChaEngine;
139
140 #[test]
141 fn test_write_read_encrypted_object() {
142 let store = MemoryBlockStore::new(4096, 16);
143 let engine = ChaChaEngine::generate().unwrap();
144 let codec = PostcardCodec;
145
146 let inode = Inode {
147 id: 42,
148 kind: InodeKind::File,
149 size: 1024,
150 directory_page_ref: ObjectRef::null(),
151 extent_map_ref: ObjectRef::new(5),
152 created_at: 1000,
153 modified_at: 2000,
154 };
155
156 write_encrypted_object(&store, &engine, &codec, 3, ObjectKind::Inode, &inode).unwrap();
157 let recovered: Inode = read_encrypted_object(&store, &engine, &codec, 3).unwrap();
158
159 assert_eq!(recovered.id, 42);
160 assert_eq!(recovered.size, 1024);
161 assert_eq!(recovered.kind, InodeKind::File);
162 }
163}