1use std::cell::RefCell;
8use std::io::{BufWriter, Read, Seek, Write};
9use std::sync::Arc;
10
11use corecrypto::header::{HashingAlgorithm, HeaderType};
12use corecrypto::primitives::BLOCK_SIZE;
13use corecrypto::protected::Protected;
14use zip::write::FileOptions;
15
16use crate::storage::Storage;
17
18#[derive(Debug)]
19pub enum Error {
20 CreateArchive,
21 AddDirToArchive,
22 AddFileToArchive,
23 FinishArchive,
24 ReadData,
25 WriteData,
26 Encrypt(crate::encrypt::Error),
27}
28
29impl std::fmt::Display for Error {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 Error::CreateArchive => f.write_str("Unable to create archive"),
33 Error::AddDirToArchive => f.write_str("Unable to add directory to archive"),
34 Error::AddFileToArchive => f.write_str("Unable to add file to archive"),
35 Error::FinishArchive => f.write_str("Unable to finish archive"),
36 Error::ReadData => f.write_str("Unable to read data"),
37 Error::WriteData => f.write_str("Unable to write data"),
38 Error::Encrypt(inner) => write!(f, "Unable to encrypt archive: {inner}"),
39 }
40 }
41}
42
43impl std::error::Error for Error {}
44
45pub struct Request<'a, RW>
46where
47 RW: Read + Write + Seek,
48{
49 pub writer: &'a RefCell<RW>,
50 pub compress_files: Vec<crate::storage::Entry<RW>>,
51 pub compression_method: zip::CompressionMethod,
52 pub header_writer: Option<&'a RefCell<RW>>,
53 pub raw_key: Protected<Vec<u8>>,
54 pub header_type: HeaderType,
56 pub hashing_algorithm: HashingAlgorithm,
57}
58
59pub fn execute<RW>(stor: Arc<impl Storage<RW>>, req: Request<'_, RW>) -> Result<(), Error>
60where
61 RW: Read + Write + Seek,
62{
63 let tmp_file = stor.create_temp_file().map_err(|_| Error::CreateArchive)?;
65 {
66 let mut tmp_writer = tmp_file
67 .try_writer()
68 .map_err(|_| Error::CreateArchive)?
69 .borrow_mut();
70 let mut zip_writer = zip::ZipWriter::new(BufWriter::new(&mut *tmp_writer));
71
72 let options: zip::write::FileOptions<'_, ()> = FileOptions::default()
74 .compression_method(req.compression_method)
75 .large_file(true)
76 .unix_permissions(0o755);
77
78 req.compress_files.into_iter().try_for_each(|f| {
80 let file_path = f.path().to_str().ok_or(Error::ReadData)?;
81 if f.is_dir() {
82 zip_writer
83 .add_directory(file_path, options)
84 .map_err(|_| Error::AddDirToArchive)?;
85 } else {
86 zip_writer
87 .start_file(file_path, options)
88 .map_err(|_| Error::AddFileToArchive)?;
89
90 let mut reader = f.try_reader().map_err(|_| Error::ReadData)?.borrow_mut();
91 let mut buffer = vec![0u8; BLOCK_SIZE].into_boxed_slice();
92 loop {
93 let read_count = reader.read(&mut buffer).map_err(|_| Error::ReadData)?;
94 zip_writer
95 .write_all(&buffer[..read_count])
96 .map_err(|_| Error::WriteData)?;
97 if read_count != BLOCK_SIZE {
98 break;
99 }
100 }
101 }
102
103 Ok(())
104 })?;
105
106 zip_writer.finish().map_err(|_| Error::FinishArchive)?;
108 }
109
110 let buf_capacity = stor.file_len(&tmp_file).map_err(|_| Error::FinishArchive)?;
111
112 let encrypt_res = crate::encrypt::execute(crate::encrypt::Request {
114 reader: tmp_file.try_reader().map_err(|_| Error::FinishArchive)?,
115 writer: req.writer,
116 header_writer: req.header_writer,
117 raw_key: req.raw_key,
118 header_type: req.header_type,
119 hashing_algorithm: req.hashing_algorithm,
120 })
121 .map_err(Error::Encrypt);
122
123 crate::overwrite::execute(crate::overwrite::Request {
125 buf_capacity,
126 writer: tmp_file.try_writer().map_err(|_| Error::FinishArchive)?,
127 passes: 2,
128 })
129 .ok();
130
131 stor.remove_file(tmp_file).ok();
132
133 encrypt_res
134}
135
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use std::io::Read;
141
142 use corecrypto::header::{HeaderType, HeaderVersion};
143 use corecrypto::primitives::{Algorithm, Mode};
144
145 use crate::encrypt::tests::PASSWORD;
146 use crate::storage::{InMemoryStorage, Storage};
147
148 const ENCRYPTED_PACKED_BAR_DIR: [u8; 1202] = [
149 222, 5, 14, 1, 12, 1, 173, 240, 60, 45, 230, 243, 58, 160, 69, 50, 217, 192, 66, 223, 124,
150 190, 148, 91, 92, 129, 0, 0, 0, 0, 0, 0, 223, 181, 71, 240, 140, 106, 41, 36, 82, 150, 105,
151 215, 159, 108, 234, 246, 25, 19, 65, 206, 177, 146, 15, 174, 209, 129, 82, 2, 62, 76, 129,
152 34, 136, 189, 11, 98, 105, 54, 146, 71, 102, 166, 97, 177, 207, 62, 194, 132, 38, 87, 173,
153 240, 60, 45, 230, 243, 58, 160, 69, 50, 217, 192, 66, 223, 124, 190, 148, 91, 92, 129, 50,
154 126, 110, 254, 58, 206, 16, 183, 233, 128, 23, 223, 81, 30, 214, 132, 32, 104, 51, 119, 0,
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 64, 6, 177, 49,
165 139, 218, 8, 121, 228, 19, 5, 8, 117, 33, 131, 131, 70, 76, 147, 108, 49, 191, 191, 127,
166 223, 64, 127, 248, 65, 201, 130, 166, 129, 195, 245, 241, 188, 143, 148, 191, 86, 7, 102,
167 124, 253, 12, 44, 172, 79, 236, 207, 68, 229, 117, 49, 250, 55, 6, 48, 86, 48, 244, 189,
168 137, 27, 142, 241, 44, 118, 35, 5, 138, 237, 47, 248, 108, 30, 224, 42, 91, 16, 216, 14,
169 235, 132, 33, 123, 83, 188, 196, 205, 18, 71, 152, 231, 231, 127, 182, 29, 156, 157, 203,
170 178, 178, 3, 216, 51, 84, 28, 67, 91, 255, 14, 124, 180, 131, 80, 48, 27, 111, 195, 39,
171 127, 37, 231, 111, 82, 132, 168, 253, 149, 230, 199, 161, 78, 6, 175, 98, 210, 9, 25, 145,
172 199, 151, 38, 142, 199, 217, 35, 247, 168, 73, 138, 94, 175, 45, 0, 184, 252, 55, 250, 19,
173 8, 79, 247, 38, 230, 133, 143, 66, 27, 69, 96, 183, 201, 238, 81, 114, 131, 123, 229, 78,
174 39, 140, 151, 4, 196, 49, 37, 58, 12, 48, 243, 83, 111, 84, 6, 82, 249, 200, 120, 238, 190,
175 136, 135, 189, 34, 237, 52, 18, 23, 43, 164, 113, 31, 111, 221, 119, 216, 110, 0, 74, 53,
176 81, 86, 83, 234, 70, 69, 194, 224, 96, 26, 47, 133, 49, 147, 204, 96, 125, 165, 105, 182,
177 161, 2, 143, 225, 195, 95, 64, 24, 49, 236, 210, 124, 32, 214, 69, 201, 5, 73, 5, 7, 160,
178 233, 35, 202, 226, 40, 104, 45, 214, 0, 39, 55, 167, 203, 184, 145, 150, 233, 119, 115,
179 246, 55, 162, 5, 154, 147, 144, 69, 217, 185, 39, 82, 223, 87, 132, 164, 148, 85, 234, 15,
180 160, 2, 214, 133, 27, 73, 53, 27, 86, 53, 215, 96, 142, 85, 25, 127, 11, 111, 19, 1, 72,
181 74, 92, 16, 14, 98, 20, 203, 163, 227, 160, 192, 158, 223, 99, 116, 212, 137, 101, 150,
182 182, 125, 244, 59, 20, 157, 129, 149, 34, 21, 136, 185, 41, 242, 168, 45, 135, 100, 219,
183 239, 132, 211, 238, 37, 242, 139, 218, 120, 112, 158, 75, 53, 172, 162, 136, 202, 94, 117,
184 152, 175, 205, 34, 198, 99, 49, 174, 187, 80, 151, 225, 169, 120, 192, 77, 61, 38, 2, 158,
185 45, 216, 78, 215, 134, 255, 7, 46, 144, 119, 60, 168, 202, 24, 239, 147, 122, 58, 48, 50,
186 178, 58, 153, 243, 242, 169, 238, 42, 78, 123, 37, 181, 17, 109, 175, 84, 6, 212, 122, 89,
187 60, 111, 248, 41, 156, 214, 222, 151, 212, 52, 10, 221, 69, 1, 215, 170, 76, 149, 134, 241,
188 212, 217, 131, 179, 34, 240, 124, 224, 192, 105, 34, 254, 165, 211, 100, 169, 240, 171,
189 131, 50, 80, 54, 254, 128, 179, 233, 223, 22, 39, 56, 205, 221, 76, 177, 197, 164, 140,
190 181, 42, 154, 82, 239, 240, 127, 211, 45, 146, 57, 154, 151, 153, 112, 215, 222, 199, 37,
191 44, 98, 118, 182, 189, 15, 139, 88, 227, 37, 149, 107, 13, 123, 201, 51, 61, 67, 220, 161,
192 13, 72, 176, 39, 157, 128, 105, 144, 10, 46, 29, 113, 1, 76, 162, 157, 200, 213, 175, 107,
193 128, 13, 47, 170, 216, 107, 48, 241, 149, 219, 20, 186, 74, 210, 5, 210, 18, 201, 78, 159,
194 121, 180, 195, 154, 176, 154, 255, 21, 5, 86, 212, 181, 237, 131, 116, 59, 241, 57, 24,
195 102, 126, 132, 135, 154, 99, 217, 2, 201, 139, 202, 125, 64, 165, 195, 210, 255, 165, 197,
196 172, 166, 27, 200, 226, 158, 225, 224, 10, 150, 97, 2, 77, 73, 51, 112, 201, 146, 74, 245,
197 95, 191, 244, 128, 170, 109, 227, 44, 24, 11, 216, 35, 137, 61, 120, 207, 212, 57, 229, 70,
198 152, 118, 92, 235, 187, 55, 189, 231, 126, 15, 86, 66, 78, 251, 39, 181, 191, 193, 226,
199 199, 131, 61, 145, 177, 76, 168, 0, 235, 172, 21, 213, 87, 81, 176, 135, 139, 61, 3, 91,
200 67, 84, 199, 40, 113, 140, 68, 174, 34, 199, 50, 33, 187, 208, 209, 155, 237, 140, 16, 204,
201 135, 151, 241, 28, 95, 87, 91, 169, 160, 1, 206, 18, 220, 65, 236, 52, 63, 184, 226, 237,
202 129, 19, 170, 194, 11, 154, 168, 110, 242, 19, 167, 195, 205, 68, 4, 151, 99, 196, 164, 13,
203 137, 140, 175, 134, 102, 47, 63, 0, 229, 73, 218, 226, 121, 230, 98, 31, 102, 161, 40, 233,
204 229, 39, 224, 19, 92, 220, 151, 154, 193, 191, 30,
205 ];
206
207 #[test]
208 fn should_pack_bar_directory() {
209 let stor = Arc::new(InMemoryStorage::default());
210 stor.add_hello_txt();
211 stor.add_bar_foo_folder_with_hidden();
212
213 let file = stor.read_file("bar/").unwrap();
214 let mut compress_files = stor.read_dir(&file).unwrap();
215 compress_files.sort_by(|a, b| a.path().cmp(b.path()));
216
217 let output_file = stor.create_file("bar.zip.enc").unwrap();
218
219 let req = Request {
220 compress_files,
221 compression_method: zip::CompressionMethod::Stored,
222 writer: output_file.try_writer().unwrap(),
223 header_writer: None,
224 raw_key: Protected::new(PASSWORD.to_vec()),
225 header_type: HeaderType {
226 version: HeaderVersion::V5,
227 algorithm: Algorithm::XChaCha20Poly1305,
228 mode: Mode::StreamMode,
229 },
230 hashing_algorithm: HashingAlgorithm::Blake3Balloon(5),
231 };
232
233 match execute(stor, req) {
234 Ok(()) => {
235 let reader = &mut *output_file.try_writer().unwrap().borrow_mut();
236 reader.rewind().unwrap();
237
238 let mut content = vec![];
239 reader.read_to_end(&mut content).unwrap();
240
241 assert_eq!(content, ENCRYPTED_PACKED_BAR_DIR.to_vec());
242 }
243 _ => unreachable!(),
244 }
245 }
246}