1#[cfg(any(feature = "serde", feature = "signers-list"))]
2extern crate serde;
3pub extern crate rsa;
4extern crate chacha20poly1305;
5extern crate sha2;
6extern crate rand;
7
8use std::borrow::BorrowMut;
9use std::fs::File;
10use std::io::{Read, Write, Seek};
11use std::path::Path;
12
13use asymetric_key::{PublicKey, PrivateKey};
14use rsa::{RsaPublicKey, RsaPrivateKey, Pss};
15use sha2::Sha512;
16#[cfg(feature = "signers-list")]
17use signers_list::SignersList;
18use trie_rs::TrieBuilder;
19use zip::{ZipArchive, ZipWriter};
20use rand::rngs::OsRng;
21
22pub use zip::{write::FileOptions, CompressionMethod, DateTime};
23
24mod symmertic_cipher;
25pub mod directory_content;
26#[cfg(feature = "signers-list")]
27pub mod signers_list;
28pub mod error;
29pub mod asymetric_key;
30
31use directory_content::{DirectoryContent, DirectoryContentPath};
32use symmertic_cipher::{SymmetricCipher, SymmetricKey};
33use error::{EncryptedFileError, EncryptedFileResult};
34
35const FILE_CONETENT_DIR: &str = "content";
36const FILE_CONTENT_NAME: &str = "file";
37const FILE_KEY_NAME: &str = "key";
38const FILE_SIGNATURE_NAME: &str = "signature";
39const FILE_DIGEST_NAME: &str = "digest";
40
41pub struct EncryptedFile {
49 file: File,
50 directory_content: Option<DirectoryContent>,
51 symmetric_cipher: SymmetricCipher,
52 associated_data: Vec<u8>,
53 file_options: FileOptions,
54}
55
56pub type DecryptFileResult = EncryptedFileResult<bool>;
57pub type DecryptFileAndVerifyResult = EncryptedFileResult<(bool, EncryptedFileResult<()>)>;
58#[cfg(feature = "signers-list")]
59pub type DecryptFileAndFindSignerResult = EncryptedFileResult<(bool, Option<String>)>;
60
61impl EncryptedFile {
62 pub fn new<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
63 let path = path.as_ref();
64 if !path.exists() {
65 let file = File::create(path)?;
66 ZipWriter::new(file);
67 }
68 Ok(Self {
69 file: File::options().read(true).write(true).open(path)?,
70 directory_content: None,
71 symmetric_cipher: SymmetricCipher::default(),
72 associated_data: b"@s3ue9lWmFBMthC%NQnes1@2!SK@drScEQV6GPr^s$v@US&N6lI$uCirVwr8@6HkqStAS%%9T#Fn5Axom%C2#3&Ss0wQQL8J&1w*QKb64Mlt!cH4DaV0v^ZFh8^oCh@Y".to_vec(),
73 file_options: FileOptions::default(),
74 })
75 }
76
77 pub fn zip_file_options(&self) -> &FileOptions {
78 &self.file_options
79 }
80
81 pub fn set_zip_file_options(&mut self, file_options: FileOptions) {
82 self.file_options = file_options
83 }
84
85 pub fn associated_data(&self) -> &[u8] {
86 &self.associated_data
87 }
88
89 pub fn change_associated_data(&mut self, associated_data: Vec<u8>) {
90 self.associated_data = associated_data;
91 }
92
93 pub fn change_buffor_size(&mut self, buffor_size: usize) {
94 self.symmetric_cipher.change_buffor_size(buffor_size);
95 }
96
97 pub fn buffor_size(&self) -> usize {
98 self.symmetric_cipher.buffor_size()
99 }
100
101 pub fn get_directory_content_soft(&self) -> Option<&DirectoryContent> {
102 self.directory_content.as_ref()
103 }
104
105 pub fn get_directory_content(&mut self) -> EncryptedFileResult<&DirectoryContent> {
106 match self.directory_content.is_some() {
107 true => Ok(self.get_directory_content_soft().unwrap()),
108 false => self.get_directory_content_hard(),
109 }
110 }
111
112 pub fn get_directory_content_hard(&mut self) -> EncryptedFileResult<&DirectoryContent> {
113 let archive = ZipArchive::new(&self.file)?;
114 let mut ans = DirectoryContent::new();
115 for name in archive.file_names() {
116 if name.len() < FILE_CONETENT_DIR.len() {
117 continue;
118 }
119 let mut path = DirectoryContentPath::from(name.get(FILE_CONETENT_DIR.len()..).expect("Check if it's long enough"));
120 if let Some(name) = path.pop() {
121 match name.as_str() {
122 FILE_CONTENT_NAME => drop(ans.get_or_create_file_mut(&path)?.content(true)),
123 FILE_KEY_NAME => drop(ans.get_or_create_file_mut(&path)?.key(true)),
124 FILE_SIGNATURE_NAME => drop(ans.get_or_create_file_mut(&path)?.signed(true)),
125 FILE_DIGEST_NAME => drop(ans.get_or_create_file_mut(&path)?.digest(true)),
126 &_ => (),
127 }
128 }
129 }
130 self.directory_content = Some(ans);
131 Ok(self.directory_content.as_ref().unwrap())
132 }
133
134 fn sign_file(&self, dst_path: &DirectoryContentPath, file_hash: &[u8; 64], private_key: &RsaPrivateKey) -> EncryptedFileResult<()> {
135 let mut zip = ZipWriter::new_append(&self.file)?;
136 zip.start_file(format!("{FILE_CONETENT_DIR}/{dst_path}/{FILE_SIGNATURE_NAME}"), self.file_options)?;
137 zip.write(&private_key.sign_with_rng(&mut OsRng, Pss::new::<Sha512>(), file_hash)?)?;
138 Ok(())
139 }
140
141 fn verify_signature(&self, src_path: &DirectoryContentPath, file_hash: &[u8; 64], public_key: &RsaPublicKey) -> EncryptedFileResult<()> {
142 let mut zip = ZipArchive::new(&self.file)?;
143 let mut buf = Vec::new();
144 zip.by_name(&format!("{FILE_CONETENT_DIR}/{src_path}/{FILE_SIGNATURE_NAME}"))?.read_to_end(&mut buf)?;
146 public_key.verify(Pss::new::<Sha512>(), file_hash, &buf)?;
147 Ok(())
148 }
149
150 fn add_file_digest<I: Read>(&self, src: I, dst_path: &DirectoryContentPath, public_key: &PublicKey) -> EncryptedFileResult<Box<[u8; 64]>> {
151 let dst = format!("{FILE_CONETENT_DIR}/{dst_path}");
152 let key = SymmetricKey::new();
153 let mut zip = ZipWriter::new_append(&self.file)?;
154 zip.start_file(format!("{}/{}", dst, FILE_CONTENT_NAME), self.file_options)?;
155 let dig = self.symmetric_cipher.encrypt_file(&key, self.associated_data(), src, &mut zip)?;
156 let key_bytes: [u8; 51] = key.into();
157 let encrypted_key = public_key.encrypt_symmetric_key(&key_bytes)?;
158 zip.start_file(format!("{}/{}", dst, FILE_KEY_NAME), self.file_options)?;
159 zip.write_all(&encrypted_key)?;
160 zip.start_file(format!("{}/{}", dst, FILE_DIGEST_NAME), self.file_options)?;
161 zip.write_all(dig.as_ref())?;
162 Ok(dig)
163 }
164
165 fn decrypt_file_digest<O: Write>(&self, src: &DirectoryContentPath, mut dst: O, private_key: &PrivateKey) -> EncryptedFileResult<(Box<[u8; 64]>, bool)> {
166 let mut zip = ZipArchive::new(&self.file)?;
167 let key = {
168 let mut zipfile = zip.by_name(format!("{FILE_CONETENT_DIR}/{src}/{FILE_KEY_NAME}").as_str())?;
169 let mut buf: Vec<u8> = Vec::new();
170 zipfile.read_to_end(&mut buf)?;
172 SymmetricKey::try_from(private_key.decrypt_symmetric_key(&buf)?)?
173 };
174 let ans = self.symmetric_cipher.decrypt_file(&key, self.associated_data(), &mut zip.by_name(format!("{FILE_CONETENT_DIR}/{src}/{FILE_CONTENT_NAME}").as_str())?, &mut dst)?;
175 let is_digest_correct = *ans == {
176 let mut zipfile = zip.by_name(format!("{FILE_CONETENT_DIR}/{src}/{FILE_DIGEST_NAME}").as_str())?;
177 let mut buf = [0; 64];
178 zipfile.read_exact(&mut buf)?;
179 buf
180 };
181 Ok((
182 ans,
183 is_digest_correct
184 ))
185 }
186
187 pub fn add_file<I: Read>(&mut self, src: I, dst_path: &DirectoryContentPath, public_key: &PublicKey) -> EncryptedFileResult<()> {
188 if self.get_directory_content().unwrap().get_file(dst_path).is_some() {
189 return Err(EncryptedFileError::FileAlreadyExists.into());
190 }
191 self.add_file_digest(src, dst_path, public_key)?;
192 self.directory_content.as_mut().unwrap().borrow_mut().add_file_with_path(dst_path)?.content(true).key(true).digest(true);
193 Ok(())
194 }
195
196 pub fn add_file_and_sign<I: Read>(&mut self, src: I, dst_path: &DirectoryContentPath, public_key: &PublicKey, private_key: &RsaPrivateKey) -> EncryptedFileResult<()> {
197 if self.get_directory_content().unwrap().get_file(dst_path).is_some() {
198 return Err(EncryptedFileError::FileAlreadyExists.into());
199 }
200 self.sign_file(dst_path, self.add_file_digest(src, dst_path, public_key)?.as_ref(), private_key)?;
201 self.directory_content.as_mut().unwrap().borrow_mut().add_file_with_path(dst_path)?.content(true).key(true).signed(true).digest(true);
202 Ok(())
203 }
204
205 pub fn decrypt_file<O: Write>(&self, src: &DirectoryContentPath, dst: O, private_key: &PrivateKey) -> DecryptFileResult {
206 if self.get_directory_content_soft().is_none() {
207 return Err(EncryptedFileError::ContentIsUnknown.into());
208 }
209 let file = self.get_directory_content_soft().unwrap().get_file(src).ok_or(EncryptedFileError::FileDoesNotExist)?;
210 if !file.has_content() {
211 Err(EncryptedFileError::FileContentIsMissing.into())
212 }
213 else if !file.has_key() {
214 Err(EncryptedFileError::FileKeyIsMissing.into())
215 }
216 else {
217 Ok(self.decrypt_file_digest(src, dst, private_key)?.1)
218 }
219 }
220
221
222 pub fn decrypt_file_and_verify<O: Write>(&self, src: &DirectoryContentPath, dst: O, private_key: &PrivateKey, public_key: &RsaPublicKey) -> DecryptFileAndVerifyResult {
223 if self.get_directory_content_soft().is_none() {
224 return Err(EncryptedFileError::ContentIsUnknown.into());
225 }
226 let file = self.get_directory_content_soft().unwrap().get_file(src).ok_or(EncryptedFileError::FileDoesNotExist)?;
227 if !file.has_content() {
228 Err(EncryptedFileError::FileContentIsMissing.into())
229 }
230 else if !file.has_key() {
231 Err(EncryptedFileError::FileKeyIsMissing.into())
232 }
233 else if !file.is_signed() {
234 Err(EncryptedFileError::FileIsNotSigned.into())
235 }
236 else {
237 let (dig, ans) = self.decrypt_file_digest(src, dst, private_key)?;
238 let signature_verification = self.verify_signature(src, dig.as_ref(), public_key);
239 Ok((ans, signature_verification))
240 }
241 }
242
243 #[cfg(feature = "signers-list")]
244 pub fn decrypt_file_and_find_signer<O: Write>(&self, src: &DirectoryContentPath, dst: O, private_key: &PrivateKey, signers_list: &SignersList) -> DecryptFileAndFindSignerResult {
245 if self.get_directory_content_soft().is_none() {
246 return Err(EncryptedFileError::ContentIsUnknown.into());
247 }
248 let file = self.get_directory_content_soft().unwrap().get_file(src).ok_or(EncryptedFileError::FileDoesNotExist)?;
249 if !file.has_content() {
250 Err(EncryptedFileError::FileContentIsMissing.into())
251 }
252 else if !file.has_key() {
253 Err(EncryptedFileError::FileKeyIsMissing.into())
254 }
255 else if !file.is_signed() {
256 Err(EncryptedFileError::FileIsNotSigned.into())
257 }
258 else {
259 let (digest, is_valid) = self.decrypt_file_digest(src, dst, private_key)?;
260 for (signer, key) in signers_list {
261 if self.verify_signature(src, &digest, &key).is_ok() {
262 return Ok((is_valid, Some(signer.to_owned())));
263 }
264 }
265 Ok((is_valid, None))
266 }
267 }
268
269 fn add_dir<P, E, R>(&mut self, src: P, mut dst: DirectoryContentPath, encryptor: &mut E) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<R>)>>
270 where P: AsRef<Path>,
271 E: FnMut(&mut Self, &Path, &DirectoryContentPath) -> EncryptedFileResult<R> {
272 if !src.as_ref().is_dir() {
273 return Err(EncryptedFileError::ThisIsNotADirectory.into());
274 }
275 dst.push(src.as_ref().file_name().ok_or(EncryptedFileError::InvalidPath)?.to_str().ok_or(EncryptedFileError::InvalidPath)?).expect("Names are not empty");
276 let mut ans = Vec::new();
277 for path in src.as_ref().read_dir()?.filter(|path| path.is_ok()).map(|path| path.unwrap()) {
278 if path.path().is_file() {
279 dst.push(path.file_name().to_str().unwrap()).expect("Names are not empty");
280 ans.push((Box::from(path.path()), encryptor(self, path.path().as_path(), &dst)));
281 dst.pop();
282 }
283 else {
284 match self.add_dir(path.path(), dst.clone(), encryptor) {
285 Ok(mut res) => ans.append(&mut res),
286 Err(err) => ans.push((Box::from(path.path()), Err(err))),
287 }
288 }
289 }
290 Ok(ans)
291 }
292
293 fn count_files<P: AsRef<Path>>(path: P) -> EncryptedFileResult<usize> {
294 let mut ans = 0;
295 for obj in path.as_ref().read_dir()?.filter(|path| path.is_ok()).map(|path| path.unwrap()) {
296 ans += match obj.path().is_dir() {
297 true => Self::count_files(obj.path())?,
298 false => 1,
299 }
300 }
301 Ok(ans)
302 }
303
304 pub fn add_directory<P: AsRef<Path>>(&mut self, src: P, dst: DirectoryContentPath, public_key: &PublicKey) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<()>)>> {
305 self.add_directory_callback(src, dst, public_key, |_| {}, |_, _, _| {}, |_| {})
306 }
307
308 pub fn add_directory_callback<P, B, E, A>(&mut self, src: P, dst: DirectoryContentPath, public_key: &PublicKey, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<()>)>>
309 where P: AsRef<Path>,
310 B: FnOnce(usize),
311 E: FnMut(&str, &DirectoryContentPath, &EncryptedFileResult<()>),
312 A: FnOnce(bool) {
313 before(Self::count_files(src.as_ref())?);
314 let ans = self.add_dir(src, dst, &mut |s, src, dst_path| {
315 let file = File::open(src)?;
316 let ans = match file.metadata() {
317 Ok(metadata) => {
318 s.file_options = s.file_options.large_file(metadata.len() >= 4*1024*1024*1024);
319 s.add_file(file, dst_path, public_key)
320 }
321 Err(err) => Err(EncryptedFileError::from(err)),
322 };
323 callback(src.to_str().unwrap(), &dst_path, &ans);
324 ans
325 });
326 after(ans.is_ok());
327 ans
328 }
329
330 pub fn add_directory_and_sign<P: AsRef<Path>>(&mut self, src: P, dst: DirectoryContentPath, public_key: &PublicKey, private_key: &RsaPrivateKey) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<()>)>> {
331 self.add_directory_and_sign_callback(src, dst, public_key, private_key, |_| {}, |_, _, _| {}, |_| {})
332 }
333
334 pub fn add_directory_and_sign_callback<P, B, E, A>(&mut self, src: P, dst: DirectoryContentPath, public_key: &PublicKey, private_key: &RsaPrivateKey, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<()>)>>
336 where P: AsRef<Path>,
337 B: FnOnce(usize),
338 E: FnMut(&str, &DirectoryContentPath, &EncryptedFileResult<()>),
339 A: FnOnce(bool) {
340 before(Self::count_files(src.as_ref())?);
341 let ans = self.add_dir(src, dst, &mut |s, src, dst_path| {
342 let file = File::open(src)?;
343 let ans = match file.metadata() {
344 Ok(metadata) => {
345 s.file_options = s.file_options.large_file(metadata.len() >= 4*1024*1024*1024);
346 s.add_file_and_sign(file, dst_path, public_key, private_key)
347 }
348 Err(err) => Err(EncryptedFileError::from(err)),
349 };
350 callback(src.to_str().unwrap(), dst_path, &ans);
351 ans
352 });
353 after(ans.is_ok());
354 ans
355 }
356
357 fn decrypt_dir<P, D, R>(&self, mut src: DirectoryContentPath, dst: P, decryptor: &mut D) -> EncryptedFileResult<Vec<(String, EncryptedFileResult<R>)>>
358 where P: AsRef<Path>,
359 D: FnMut(DirectoryContentPath, &Path) -> EncryptedFileResult<R> {
360 if self.get_directory_content_soft().ok_or(EncryptedFileError::ContentIsUnknown)?.get_dir(&src).is_none() {
361 return Err(EncryptedFileError::DirectoryDoesNotExist.into());
362 }
363 if !dst.as_ref().is_dir() {
364 return Err(EncryptedFileError::ThisIsNotADirectory.into());
365 }
366 let dst: Box<Path> = Box::from(dst.as_ref().join(src.file_name().unwrap_or("content")).as_path());
367 std::fs::create_dir_all(dst.as_ref())?;
368 let mut ans = Vec::new();
369 let directory_content = self.directory_content.as_ref().unwrap().get_dir(&src).ok_or(EncryptedFileError::DirectoryDoesNotExist)?;
370 for (name, _) in directory_content.get_files_iter() {
371 src.push(&name).expect("Names are not empty");
372 ans.push((name.to_owned(), decryptor(src.clone(), dst.join(name).as_path())));
373 src.pop();
374 }
375 for (name, _) in directory_content.get_dir_iter() {
376 src.push(&name).expect("Names are not empty");
377 ans.append(&mut self.decrypt_dir(src.clone(), dst.as_ref(), decryptor)?);
378 src.pop();
379 }
380 Ok(ans)
381 }
382
383 pub fn decrypt_directory<P: AsRef<Path>>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey) -> EncryptedFileResult<Vec<(String, DecryptFileResult)>> {
384 self.decrypt_directory_callback(src, dst, private_key, |_| {},|_, _, _| {}, |_| {})
385 }
386
387 pub fn decrypt_directory_callback<P, B, E, A>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(String, DecryptFileResult)>>
388 where P: AsRef<Path>,
389 B: FnOnce(usize),
390 E: FnMut(&DirectoryContentPath, &str, &DecryptFileResult),
391 A: FnOnce(bool) {
392 before(self.get_directory_content_soft().ok_or(EncryptedFileError::ContentIsUnknown)?.get_dir(&src).ok_or(EncryptedFileError::DirectoryDoesNotExist)?.get_total_file_count());
393 let ans = self.decrypt_dir(src, dst, &mut |src, dst| {
394 let ans = self.decrypt_file(&src, File::create(dst)?, private_key);
395 callback(&src, dst.to_str().unwrap(), &ans);
396 ans
397 });
398 after(ans.is_ok());
399 ans
400 }
401
402 pub fn decrypt_directory_and_verify<P: AsRef<Path>>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, public_key: &RsaPublicKey) -> EncryptedFileResult<Vec<(String, DecryptFileAndVerifyResult)>> {
403 self.decrypt_directory_and_verify_callback(src, dst, private_key, public_key, |_| {},|_, _, _| {}, |_| {})
404 }
405
406 pub fn decrypt_directory_and_verify_callback<P, B, E, A>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, public_key: &RsaPublicKey, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(String, DecryptFileAndVerifyResult)>>
407 where P: AsRef<Path>,
408 B: FnOnce(usize),
409 E: FnMut(&DirectoryContentPath, &str, &DecryptFileAndVerifyResult),
410 A: FnOnce(bool) {
411 before(self.get_directory_content_soft().ok_or(EncryptedFileError::ContentIsUnknown)?.get_dir(&src).ok_or(EncryptedFileError::DirectoryDoesNotExist)?.get_total_file_count());
412 let ans = self.decrypt_dir(src, dst, &mut |src, dst| {
413 let ans = self.decrypt_file_and_verify(&src, File::create(dst)?, private_key, public_key);
414 callback(&src, dst.to_str().unwrap(), &ans);
415 ans
416 });
417 after(ans.is_ok());
418 ans
419 }
420
421 #[cfg(feature = "signers-list")]
422 pub fn decrypt_directory_and_find_signer<P: AsRef<Path>>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, signers_list: &SignersList) -> EncryptedFileResult<Vec<(String, DecryptFileAndFindSignerResult)>> {
423 self.decrypt_directory_and_find_signer_callback(src, dst, private_key, signers_list, |_| {},|_, _, _| {}, |_| {})
424 }
425
426 #[cfg(feature = "signers-list")]
427 pub fn decrypt_directory_and_find_signer_callback<P, B, E, A>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, signers_list: &SignersList, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(String, DecryptFileAndFindSignerResult)>>
428 where P: AsRef<Path>,
429 B: FnOnce(usize),
430 E: FnMut(&DirectoryContentPath, &str, &DecryptFileAndFindSignerResult),
431 A: FnOnce(bool) {
432 before(self.get_directory_content_soft().ok_or(EncryptedFileError::ContentIsUnknown)?.get_dir(&src).ok_or(EncryptedFileError::DirectoryDoesNotExist)?.get_total_file_count());
433 let ans = self.decrypt_dir(src, dst, &mut |src, dst| {
434 let ans = self.decrypt_file_and_find_signer(&src, File::create(dst)?, private_key, signers_list);
435 callback(&src, dst.to_str().unwrap(), &ans);
436 ans
437 });
438 after(ans.is_ok());
439 ans
440 }
441
442 pub fn delete_path<O: Write + Seek>(&self, output: O, to_delete: &Vec<DirectoryContentPath>) -> EncryptedFileResult<()> {
460 let mut output = ZipWriter::new(output);
461 let mut zip = ZipArchive::new(&self.file)?;
462 let trie = {
463 let mut trie_builder:TrieBuilder<&str> = TrieBuilder::new();
464 for path in to_delete {
465 trie_builder.push(path.iter().map(|v| v.as_str()).collect::<Vec<&str>>());
466 }
467 trie_builder.build()
468 };
469 for file_name in zip.file_names().map(|s| s.to_owned()).collect::<Vec<String>>() {
470 if trie.common_prefix_search(&DirectoryContentPath::from(file_name.clone()).iter().skip(1).rev().skip(1).rev().map(|v| v.as_str()).collect::<Vec<&str>>()).is_empty() {
471 output.raw_copy_file(zip.by_name(&file_name)?)?;
472 }
473 }
474 Ok(())
475 }
476}
477
478#[cfg(test)]
479mod tests;