pithos_lib/
lib.rs

1mod crypt4gh;
2pub mod helpers;
3pub mod pithos;
4pub mod readwrite;
5pub mod streamreadwrite;
6pub mod transformer;
7pub mod transformers;
8
9#[cfg(test)]
10mod tests {
11    use std::io::SeekFrom;
12
13    use crate::helpers::footer_parser::{Footer, FooterParser, FooterParserState};
14    use crate::helpers::notifications::{DirOrFileIdx, Message};
15    use crate::helpers::structs::{EncryptionKey, FileContext, Range};
16    use crate::pithos::structs::FileContextVariants;
17    use crate::readwrite::GenericReadWriter;
18    use crate::streamreadwrite::GenericStreamReadWriter;
19    use crate::transformer::ReadWriter;
20    use crate::transformers::decrypt::ChaCha20Dec;
21    use crate::transformers::decrypt_resilient::ChaChaResilient;
22    use crate::transformers::decrypt_with_parts::ChaCha20DecParts;
23    use crate::transformers::encrypt::{encrypt_chunk, ChaCha20Enc};
24    use crate::transformers::filter::Filter;
25    use crate::transformers::footer::FooterGenerator;
26    use crate::transformers::footer_extractor::FooterExtractor;
27    use crate::transformers::footer_updater::FooterUpdater;
28    use crate::transformers::gzip_comp::GzipEnc;
29    use crate::transformers::hashing_transformer::HashingTransformer;
30    use crate::transformers::pithos_comp_enc::PithosTransformer;
31    use crate::transformers::size_probe::SizeProbe;
32    use crate::transformers::tar::TarEnc;
33    use crate::transformers::zstd_comp::ZstdEnc;
34    use crate::transformers::zstd_decomp::ZstdDec;
35    use base64::prelude::*;
36    use bytes::Bytes;
37    use digest::Digest;
38    use futures::{StreamExt, TryStreamExt};
39    use md5::Md5;
40    use sha2::Sha256;
41    use tempfile::TempDir;
42    use tokio::fs::File;
43    use tokio::io::{AsyncReadExt, AsyncSeekExt};
44
45    #[tokio::test]
46    async fn e2e_compressor_test_with_file() {
47        // File handling
48        let temp_dir = TempDir::new().unwrap();
49        let out_path = temp_dir.path().join("test.txt.comp");
50        let out_file = File::create(&out_path).await.unwrap();
51        let in_file = File::open("test.txt").await.unwrap();
52
53        // Create a new GenericReadWriter to compress and decompress the input
54        GenericReadWriter::new_with_writer(in_file, out_file)
55            .add_transformer(ZstdEnc::new())
56            .add_transformer(ZstdDec::new())
57            .process()
58            .await
59            .unwrap();
60
61        // Assert that input and output is equal
62        let mut original_file = File::open("test.txt").await.unwrap();
63        let mut original_bytes = String::new();
64        original_file
65            .read_to_string(&mut original_bytes)
66            .await
67            .unwrap();
68
69        let mut also_original_file = File::open(out_path).await.unwrap();
70        let mut also_original_bytes = String::new();
71        also_original_file
72            .read_to_string(&mut also_original_bytes)
73            .await
74            .unwrap();
75
76        assert_eq!(original_bytes, also_original_bytes)
77    }
78
79    #[tokio::test]
80    async fn e2e_encrypt_test_with_vec_no_pad() {
81        let input = b"This is a very very important test".to_vec();
82        let mut output = Vec::new();
83
84        // Create a new GenericReadWriter
85        GenericReadWriter::new_with_writer(input.as_ref(), &mut output)
86            .add_transformer(
87                ChaCha20Enc::new_with_fixed(
88                    b"99wj3485nxgyq5ub9zd3e7jsrq7a92ea"
89                        .to_vec()
90                        .to_vec()
91                        .try_into()
92                        .unwrap(),
93                )
94                .unwrap(),
95            )
96            .add_transformer(
97                ChaCha20Dec::new_with_fixed(
98                    b"99wj3485nxgyq5ub9zd3e7jsrq7a92ea"
99                        .to_vec()
100                        .to_vec()
101                        .try_into()
102                        .unwrap(),
103                )
104                .unwrap(),
105            )
106            .process()
107            .await
108            .unwrap();
109
110        assert_eq!(input, output);
111    }
112
113    #[tokio::test]
114    async fn e2e_encrypt_test_with_file_no_pad() {
115        // File handling
116        let temp_dir = TempDir::new().unwrap();
117        let out_path = temp_dir.path().join("test.txt.out");
118        let file_out = File::create(&out_path).await.unwrap();
119        let file_in = File::open("test.txt").await.unwrap();
120
121        // Create a new GenericReadWriter
122        GenericReadWriter::new_with_writer(file_in, file_out)
123            .add_transformer(
124                ChaCha20Enc::new_with_fixed(
125                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
126                        .to_vec()
127                        .to_vec()
128                        .try_into()
129                        .unwrap(),
130                )
131                .unwrap(),
132            )
133            .add_transformer(
134                ChaCha20Dec::new_with_fixed(
135                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
136                        .to_vec()
137                        .to_vec()
138                        .try_into()
139                        .unwrap(),
140                )
141                .unwrap(),
142            )
143            .process()
144            .await
145            .unwrap();
146
147        let mut original_file = File::open("test.txt").await.unwrap();
148        let mut also_original_file = File::open(out_path).await.unwrap();
149        let mut buf1 = String::new();
150        let mut buf2 = String::new();
151        original_file.read_to_string(&mut buf1).await.unwrap();
152        also_original_file.read_to_string(&mut buf2).await.unwrap();
153
154        assert_eq!(buf1, buf2)
155    }
156
157    #[tokio::test]
158    async fn e2e_test_roundtrip_with_file() {
159        // File handling
160        let temp_dir = TempDir::new().unwrap();
161        let out_path = temp_dir.path().join("test.txt.out");
162        let file_out = File::create(&out_path).await.unwrap();
163        let file_in = File::open("test.txt").await.unwrap();
164
165        // Create a new GenericReadWriter
166        GenericReadWriter::new_with_writer(file_in, file_out)
167            .add_transformer(ZstdEnc::new())
168            .add_transformer(ZstdEnc::new())
169            .add_transformer(
170                ChaCha20Enc::new_with_fixed(
171                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
172                        .to_vec()
173                        .to_vec()
174                        .try_into()
175                        .unwrap(),
176                )
177                .unwrap(),
178            )
179            .add_transformer(
180                ChaCha20Enc::new_with_fixed(
181                    b"99wj3485nxgyq5ub9zd3e7jsrq7a92ea"
182                        .to_vec()
183                        .to_vec()
184                        .try_into()
185                        .unwrap(),
186                )
187                .unwrap(),
188            )
189            .add_transformer(
190                ChaCha20Dec::new_with_fixed(
191                    b"99wj3485nxgyq5ub9zd3e7jsrq7a92ea"
192                        .to_vec()
193                        .to_vec()
194                        .try_into()
195                        .unwrap(),
196                )
197                .unwrap(),
198            )
199            .add_transformer(
200                ChaCha20Dec::new_with_fixed(
201                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
202                        .to_vec()
203                        .to_vec()
204                        .try_into()
205                        .unwrap(),
206                )
207                .unwrap(),
208            )
209            .add_transformer(ZstdDec::new())
210            .add_transformer(ZstdDec::new())
211            .process()
212            .await
213            .unwrap();
214
215        let mut file = File::open("test.txt").await.unwrap();
216        let mut file2 = File::open(out_path).await.unwrap();
217        let mut buf1 = String::new();
218        let mut buf2 = String::new();
219        file.read_to_string(&mut buf1).await.unwrap();
220        file2.read_to_string(&mut buf2).await.unwrap();
221
222        assert_eq!(buf1, buf2)
223    }
224
225    #[tokio::test]
226    async fn test_with_vec() {
227        let input = b"This is a very very important test".to_vec();
228        let mut output = Vec::new();
229
230        // Create a new GenericReadWriter
231        GenericReadWriter::new_with_writer(input.as_ref(), &mut output)
232            .add_transformer(ZstdEnc::new())
233            .add_transformer(ZstdEnc::new()) // Double compression because we can
234            .add_transformer(
235                ChaCha20Enc::new_with_fixed(
236                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
237                        .to_vec()
238                        .to_vec()
239                        .try_into()
240                        .unwrap(),
241                )
242                .unwrap(),
243            )
244            .add_transformer(
245                ChaCha20Enc::new_with_fixed(
246                    b"99wj3485nxgyq5ub9zd3e7jsrq7a92ea"
247                        .to_vec()
248                        .to_vec()
249                        .try_into()
250                        .unwrap(),
251                )
252                .unwrap(),
253            )
254            .add_transformer(
255                ChaCha20Dec::new_with_fixed(
256                    b"99wj3485nxgyq5ub9zd3e7jsrq7a92ea"
257                        .to_vec()
258                        .to_vec()
259                        .try_into()
260                        .unwrap(),
261                )
262                .unwrap(),
263            )
264            .add_transformer(
265                ChaCha20Dec::new_with_fixed(
266                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
267                        .to_vec()
268                        .to_vec()
269                        .try_into()
270                        .unwrap(),
271                )
272                .unwrap(),
273            )
274            .add_transformer(ZstdDec::new())
275            .add_transformer(ZstdDec::new()) // Double decompression because we can
276            .process()
277            .await
278            .unwrap();
279
280        assert_eq!(input, output)
281    }
282
283    #[tokio::test]
284    async fn test_footer_parsing() {
285        // File handling
286        let temp_dir = TempDir::new().unwrap();
287        let out_path = temp_dir.path().join("test.txt.out");
288        let file_out = File::create(&out_path).await.unwrap();
289        let file_in = File::open("test.txt").await.unwrap();
290
291        GenericReadWriter::new_with_writer(file_in, file_out)
292            .add_transformer(ZstdEnc::new())
293            .add_transformer(
294                FooterGenerator::new_with_ctx(FileContext {
295                    file_path: "test.txt".to_string(),
296                    ..Default::default()
297                })
298                .unwrap(),
299            )
300            .process()
301            .await
302            .unwrap();
303
304        let mut footer_file = File::open(out_path).await.unwrap();
305        footer_file.seek(SeekFrom::End(-65536 * 2)).await.unwrap();
306
307        let buf: &mut [u8; 65536 * 2] = &mut [0; 65536 * 2];
308        footer_file.read_exact(buf).await.unwrap();
309
310        let mut fp = FooterParser::new(buf).unwrap();
311        fp = fp.parse().unwrap();
312
313        assert!(matches!(fp.state, FooterParserState::Decoded))
314    }
315
316    #[tokio::test]
317    async fn test_footer_parsing_encrypted() {
318        // File handling
319        let temp_dir = TempDir::new().unwrap();
320        let out_path = temp_dir.path().join("test.txt.out");
321        let file_out = File::create(&out_path).await.unwrap();
322        let file_in = File::open("test.txt").await.unwrap();
323
324        // Compression + Encryption + Footer
325        GenericReadWriter::new_with_writer(file_in, file_out)
326            .add_transformer(ZstdEnc::new())
327            .add_transformer(
328                ChaCha20Enc::new_with_fixed(
329                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
330                        .to_vec()
331                        .try_into()
332                        .unwrap(),
333                )
334                .unwrap(),
335            )
336            .add_transformer(
337                FooterGenerator::new_with_ctx(FileContext {
338                    file_path: "test.txt".to_string(),
339                    encryption_key: EncryptionKey::Same(
340                        b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
341                            .to_vec()
342                            .try_into()
343                            .unwrap(),
344                    ),
345                    ..Default::default()
346                })
347                .unwrap(),
348            )
349            .process()
350            .await
351            .unwrap();
352
353        let mut footer_file = File::open(out_path).await.unwrap();
354        footer_file
355            .seek(SeekFrom::End((-65536 - 28) * 2))
356            .await
357            .unwrap();
358
359        let buf: &mut [u8; (65536 + 28) * 2] = &mut [0; (65536 + 28) * 2];
360        footer_file.read_exact(buf).await.unwrap();
361
362        let mut fp = FooterParser::new(buf).unwrap();
363        fp = fp.add_recipient(b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea");
364        fp = fp.parse().unwrap();
365        assert!(matches!(fp.state, FooterParserState::Decoded));
366
367        let _: Footer = fp.try_into().unwrap();
368    }
369
370    #[tokio::test]
371    async fn test_simple_filter_range() {
372        // Filter from start
373        let input1 = b"This is a very very important test".to_vec();
374        let mut output_01 = Vec::new();
375        GenericReadWriter::new_with_writer(input1.as_ref(), &mut output_01)
376            .add_transformer(Filter::new_with_range(Range { from: 0, to: 3 }))
377            .process()
378            .await
379            .unwrap();
380        assert_eq!(output_01, b"Thi".to_vec());
381
382        // Filter in middle
383        let input2 = b"This is a very very important test".to_vec();
384        let mut output_02 = Vec::new();
385        GenericReadWriter::new_with_writer(input2.as_ref(), &mut output_02)
386            .add_transformer(Filter::new_with_range(Range { from: 6, to: 16 }))
387            .process()
388            .await
389            .unwrap();
390        assert_eq!(output_02, b"s a very v".to_vec());
391
392        // Filter until end
393        let input3 = b"This is a very very important test".to_vec();
394        let mut output_03 = Vec::new();
395        GenericReadWriter::new_with_writer(input3.as_ref(), &mut output_03)
396            .add_transformer(Filter::new_with_range(Range {
397                from: 25,
398                to: input3.len() as u64,
399            }))
400            .process()
401            .await
402            .unwrap();
403        assert_eq!(output_03, b"tant test".to_vec());
404    }
405
406    #[tokio::test]
407    async fn test_complex_filter() {
408        let input = b"This is a very very important test".to_vec();
409        let mut output = Vec::new();
410
411        // Create a new GenericReadWriter
412        GenericReadWriter::new_with_writer(input.as_ref(), &mut output)
413            .add_transformer(ZstdEnc::new())
414            .add_transformer(ZstdEnc::new()) // Double compression because we can
415            .add_transformer(
416                ChaCha20Enc::new_with_fixed(
417                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
418                        .to_vec()
419                        .to_vec()
420                        .try_into()
421                        .unwrap(),
422                )
423                .unwrap(),
424            )
425            .add_transformer(
426                ChaCha20Enc::new_with_fixed(
427                    b"99wj3485nxgyq5ub9zd3e7jsrq7a92ea"
428                        .to_vec()
429                        .to_vec()
430                        .try_into()
431                        .unwrap(),
432                )
433                .unwrap(),
434            )
435            .add_transformer(
436                ChaCha20Dec::new_with_fixed(
437                    b"99wj3485nxgyq5ub9zd3e7jsrq7a92ea"
438                        .to_vec()
439                        .to_vec()
440                        .try_into()
441                        .unwrap(),
442                )
443                .unwrap(),
444            )
445            .add_transformer(
446                ChaCha20Dec::new_with_fixed(
447                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
448                        .to_vec()
449                        .to_vec()
450                        .try_into()
451                        .unwrap(),
452                )
453                .unwrap(),
454            )
455            .add_transformer(ZstdDec::new())
456            .add_transformer(ZstdDec::new())
457            .add_transformer(Filter::new_with_range(Range { from: 0, to: 3 }))
458            .process()
459            .await
460            .unwrap();
461
462        assert_eq!(output, b"Thi".to_vec());
463    }
464
465    #[tokio::test]
466    async fn test_read_write_multifile() {
467        let file1 = b"Lorem ipsum dolor sit amet, consetetur sadipscing elitr.".to_vec();
468        let file2 = b"Stet clita kasd gubergren, no sea takimata sanctus.".to_vec();
469        let mut output: Vec<u8> = Vec::new();
470
471        let combined = Vec::from_iter(file1.clone().into_iter().chain(file2.clone()));
472
473        let (sx, rx) = async_channel::bounded(10);
474        sx.send(Message::FileContext(FileContext {
475            file_path: "file1.txt".to_string(),
476            compressed_size: file1.len() as u64,
477            decompressed_size: file1.len() as u64,
478            compression: true,
479            ..Default::default()
480        }))
481        .await
482        .unwrap();
483
484        sx.send(Message::FileContext(FileContext {
485            file_path: "file2.txt".to_string(),
486            compressed_size: file2.len() as u64,
487            decompressed_size: file2.len() as u64,
488            compression: false,
489            ..Default::default()
490        }))
491        .await
492        .unwrap();
493
494        // Create a new GenericReadWriter
495        let mut aswr = GenericReadWriter::new_with_writer(combined.as_ref(), &mut output);
496        aswr.add_message_receiver(rx).await.unwrap();
497        aswr = aswr
498            .add_transformer(ZstdEnc::new())
499            .add_transformer(
500                ChaCha20Enc::new_with_fixed(
501                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
502                        .to_vec()
503                        .to_vec()
504                        .try_into()
505                        .unwrap(),
506                )
507                .unwrap(),
508            )
509            .add_transformer(
510                ChaCha20Dec::new_with_fixed(
511                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
512                        .to_vec()
513                        .to_vec()
514                        .try_into()
515                        .unwrap(),
516                )
517                .unwrap(),
518            )
519            .add_transformer(ZstdDec::new());
520        aswr.process().await.unwrap();
521        drop(aswr);
522
523        assert_eq!(output, combined);
524    }
525
526    #[tokio::test]
527    async fn stream_test() {
528        use futures::stream;
529
530        let bytes_stream = stream::iter(vec![
531            Ok(Bytes::from(
532                b"One morning, when Gregor Samsa woke from troubled dreams, ".to_vec(),
533            )),
534            Ok(Bytes::from(
535                b"he found himself transformed in his bed into a horrible vermin.".to_vec(),
536            )),
537        ]);
538
539        // Create a new GenericStreamReadWriter
540        let mut output = Vec::new();
541        GenericStreamReadWriter::new_with_writer(bytes_stream, &mut output)
542            .add_transformer(ZstdEnc::new())
543            .add_transformer(
544                ChaCha20Enc::new_with_fixed(
545                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
546                        .to_vec()
547                        .to_vec()
548                        .try_into()
549                        .unwrap(),
550                )
551                .unwrap(),
552            )
553            .add_transformer(
554                ChaCha20Dec::new_with_fixed(
555                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
556                        .to_vec()
557                        .to_vec()
558                        .try_into()
559                        .unwrap(),
560                )
561                .unwrap(),
562            )
563            .add_transformer(ZstdDec::new())
564            .process()
565            .await
566            .unwrap();
567
568        assert_eq!(
569            output,
570            b"One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.".to_vec()
571        );
572    }
573
574    #[tokio::test]
575    async fn e2e_test_read_write_multifile_tar_small() {
576        // File handling
577        let temp_dir = TempDir::new().unwrap();
578        let out_path = temp_dir.path().join("test.txt.out");
579        let mut file_out = File::create(&out_path).await.unwrap();
580
581        let file1 = b"The quick, brown fox jumps over a lazy dog.".to_vec();
582        let file2 =
583            b"Junk MTV quiz graced by fox whelps. Bawds jog, flick quartz, vex nymphs.".to_vec();
584        let combined = Vec::from_iter(file1.clone().into_iter().chain(file2.clone()));
585
586        // File context input
587        let (sx, rx) = async_channel::bounded(10);
588        sx.send(Message::FileContext(FileContext {
589            file_path: "file1.txt".to_string(),
590            compressed_size: file1.len() as u64,
591            decompressed_size: file1.len() as u64,
592            compression: true,
593            encryption_key: EncryptionKey::Same(
594                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
595                    .to_vec()
596                    .to_vec()
597                    .try_into()
598                    .unwrap(),
599            ),
600            ..Default::default()
601        }))
602        .await
603        .unwrap();
604
605        sx.send(Message::FileContext(FileContext {
606            file_path: "file2.txt".to_string(),
607            compressed_size: file2.len() as u64,
608            decompressed_size: file2.len() as u64,
609            compression: false,
610            encryption_key: EncryptionKey::Same(
611                b"xxwj3485nxgyq5ub9zd3e7jsrq7a92ea"
612                    .to_vec()
613                    .to_vec()
614                    .try_into()
615                    .unwrap(),
616            ),
617            ..Default::default()
618        }))
619        .await
620        .unwrap();
621
622        // Create a new GenericReadWriter
623        let mut aswr = GenericReadWriter::new_with_writer(combined.as_ref(), &mut file_out)
624            .add_transformer(TarEnc::new());
625        aswr.add_message_receiver(rx).await.unwrap();
626        aswr.process().await.unwrap();
627    }
628
629    #[tokio::test]
630    async fn e2e_test_read_write_multifile_tar_real() {
631        // File handling
632        let temp_dir = TempDir::new().unwrap();
633        let out_path = temp_dir.path().join("test.txt.out");
634        let mut file_out = File::create(&out_path).await.unwrap();
635
636        let mut file1 = File::open("test.txt").await.unwrap();
637        let mut file2 = File::open("test.txt").await.unwrap();
638        let mut combined = Vec::new();
639        file1.read_to_end(&mut combined).await.unwrap();
640        file2.read_to_end(&mut combined).await.unwrap();
641
642        // File context input
643        let (sx, rx) = async_channel::bounded(10);
644        sx.send(Message::FileContext(FileContext {
645            file_path: "file1.txt".to_string(),
646            compressed_size: file1.metadata().await.unwrap().len(),
647            decompressed_size: file1.metadata().await.unwrap().len(),
648            compression: true,
649            encryption_key: EncryptionKey::Same(
650                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
651                    .to_vec()
652                    .to_vec()
653                    .try_into()
654                    .unwrap(),
655            ),
656            ..Default::default()
657        }))
658        .await
659        .unwrap();
660
661        sx.send(Message::FileContext(FileContext {
662            file_path: "file2.txt".to_string(),
663            compressed_size: file2.metadata().await.unwrap().len(),
664            decompressed_size: file2.metadata().await.unwrap().len(),
665            compression: false,
666            encryption_key: EncryptionKey::Same(
667                b"xxwj3485nxgyq5ub9zd3e7jsrq7a92ea"
668                    .to_vec()
669                    .to_vec()
670                    .try_into()
671                    .unwrap(),
672            ),
673            ..Default::default()
674        }))
675        .await
676        .unwrap();
677
678        // Create a new GenericReadWriter
679        let mut aswr = GenericReadWriter::new_with_writer(combined.as_ref(), &mut file_out)
680            .add_transformer(TarEnc::new());
681        aswr.add_message_receiver(rx).await.unwrap();
682        aswr.process().await.unwrap();
683    }
684
685    #[tokio::test]
686    async fn e2e_test_stream_write_multifile_tar_real() {
687        // File handling
688        let temp_dir = TempDir::new().unwrap();
689        let out_path = temp_dir.path().join("test.txt.out");
690        let mut file_out = File::create(&out_path).await.unwrap();
691
692        let file1 = File::open("test.txt").await.unwrap();
693        let file2 = File::open("test.txt").await.unwrap();
694        let file1_size = file1.metadata().await.unwrap().len();
695        let file2_size = file2.metadata().await.unwrap().len();
696        let stream1 = tokio_util::io::ReaderStream::new(file1);
697        let stream2 = tokio_util::io::ReaderStream::new(file2);
698        let chained = stream1.chain(stream2);
699        let mapped = chained
700            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
701
702        // File context input
703        let (sx, rx) = async_channel::bounded(10);
704        sx.send(Message::FileContext(FileContext {
705            file_path: "file1.txt".to_string(),
706            compressed_size: file1_size,
707            decompressed_size: file1_size,
708            ..Default::default()
709        }))
710        .await
711        .unwrap();
712
713        sx.send(Message::FileContext(FileContext {
714            file_path: "file2.txt".to_string(),
715            compressed_size: file2_size,
716            decompressed_size: file2_size,
717            ..Default::default()
718        }))
719        .await
720        .unwrap();
721
722        // Create a new GenericStreamReadWriter
723        let mut aswr = GenericStreamReadWriter::new_with_writer(mapped, &mut file_out)
724            .add_transformer(TarEnc::new());
725        aswr.add_message_receiver(rx).await.unwrap();
726        aswr.process().await.unwrap();
727    }
728
729    #[tokio::test]
730    async fn e2e_test_stream_tar_gz() {
731        // File handling
732        let temp_dir = TempDir::new().unwrap();
733        let out_path = temp_dir.path().join("test.txt.out");
734        let mut file_out = File::create(&out_path).await.unwrap();
735
736        let file1 = File::open("test.txt").await.unwrap();
737        let file2 = File::open("test.txt").await.unwrap();
738        let file1_size = file1.metadata().await.unwrap().len();
739        let file2_size = file2.metadata().await.unwrap().len();
740        let stream1 = tokio_util::io::ReaderStream::new(file1);
741        let stream2 = tokio_util::io::ReaderStream::new(file2);
742        let chained = stream1.chain(stream2);
743        let mapped = chained
744            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
745
746        let (sx, rx) = async_channel::bounded(10);
747        sx.send(Message::FileContext(FileContext {
748            file_path: "file1.txt".to_string(),
749            compressed_size: file1_size,
750            decompressed_size: file1_size,
751            ..Default::default()
752        }))
753        .await
754        .unwrap();
755
756        sx.send(Message::FileContext(FileContext {
757            file_path: "file2.txt".to_string(),
758            compressed_size: file2_size,
759            decompressed_size: file2_size,
760            ..Default::default()
761        }))
762        .await
763        .unwrap();
764
765        // Create a new GenericStreamReadWriter
766        let mut aswr = GenericStreamReadWriter::new_with_writer(mapped, &mut file_out)
767            .add_transformer(TarEnc::new())
768            .add_transformer(GzipEnc::new());
769        aswr.add_message_receiver(rx).await.unwrap();
770        aswr.process().await.unwrap();
771    }
772
773    #[tokio::test]
774    async fn hashing_transformer_test() {
775        let input = b"Lorem ipsum dolor sit amet, consectetuer adipiscing elit.".to_vec();
776        let mut output = Vec::new();
777
778        let (size_probe, rx) = SizeProbe::new();
779        let (md5_transformer, md5_rcv) =
780            HashingTransformer::new_with_backchannel(Md5::new(), "md5".to_string());
781        let (sha256_transformer, sha256_rcv) =
782            HashingTransformer::new_with_backchannel(Sha256::new(), "sha256".to_string());
783
784        // Create a new GenericReadWriter
785        GenericReadWriter::new_with_writer(input.as_ref(), &mut output)
786            .add_transformer(size_probe)
787            .add_transformer(md5_transformer)
788            .add_transformer(sha256_transformer)
789            .process()
790            .await
791            .unwrap();
792
793        let size = rx.try_recv().unwrap();
794        assert_eq!(size, 57);
795
796        let md5 = md5_rcv.try_recv().unwrap();
797        assert_eq!(md5, "a84e9dae73341f1e9764f349701a5adf".to_string());
798
799        let sha256 = sha256_rcv.try_recv().unwrap();
800        assert_eq!(
801            sha256,
802            "1d32dc481e105799b079b5a1b18c2e302bc43bc5feac01450c7ffa50a1c65b92".to_string()
803        );
804    }
805
806    #[tokio::test]
807    async fn e2e_test_stream_tar_folder() {
808        // File handling
809        let temp_dir = TempDir::new().unwrap();
810        let out_path = temp_dir.path().join("test.txt.out");
811        let mut file_out = File::create(&out_path).await.unwrap();
812
813        let file1 = File::open("test.txt").await.unwrap();
814        let file2 = File::open("test.txt").await.unwrap();
815        let file1_size = file1.metadata().await.unwrap().len();
816        let file2_size = file2.metadata().await.unwrap().len();
817        let stream1 = tokio_util::io::ReaderStream::new(file1);
818        let stream2 = tokio_util::io::ReaderStream::new(file2);
819        let chained = stream1.chain(stream2);
820        let mapped = chained
821            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
822
823        // File context input
824        let (sx, rx) = async_channel::bounded(10);
825        sx.send(Message::FileContext(FileContext {
826            file_path: "blup/".to_string(),
827            compressed_size: 0,
828            decompressed_size: 0,
829            is_dir: true,
830            ..Default::default()
831        }))
832        .await
833        .unwrap();
834
835        sx.send(Message::FileContext(FileContext {
836            file_path: "blup/file1.txt".to_string(),
837            compressed_size: file1_size,
838            decompressed_size: file1_size,
839            ..Default::default()
840        }))
841        .await
842        .unwrap();
843
844        sx.send(Message::FileContext(FileContext {
845            file_path: "blip/".to_string(),
846            compressed_size: 0,
847            decompressed_size: 0,
848            is_dir: true,
849            ..Default::default()
850        }))
851        .await
852        .unwrap();
853
854        sx.send(Message::FileContext(FileContext {
855            file_path: "blip/file2.txt".to_string(),
856            compressed_size: file2_size,
857            decompressed_size: file2_size,
858            ..Default::default()
859        }))
860        .await
861        .unwrap();
862
863        // Create a new GenericStreamReadWriter
864        let mut aswr = GenericStreamReadWriter::new_with_writer(mapped, &mut file_out)
865            .add_transformer(TarEnc::new());
866        aswr.add_message_receiver(rx).await.unwrap();
867        aswr.process().await.unwrap();
868    }
869
870    #[tokio::test]
871    async fn e2e_pithos_tar_gz() {
872        // File handling
873        let temp_dir = TempDir::new().unwrap();
874        let pithos_out_path = temp_dir.path().join("test.txt.out.pto");
875        let tar_gz_out_path = temp_dir.path().join("test.txt.out.tar.gz");
876        let mut pithos_out = File::create(&pithos_out_path).await.unwrap();
877
878        let file1 = File::open("test.txt").await.unwrap();
879        let file2 = File::open("test.txt").await.unwrap();
880        let file1_size = file1.metadata().await.unwrap().len();
881        let file2_size = file2.metadata().await.unwrap().len();
882        let stream1 = tokio_util::io::ReaderStream::new(file1);
883        let stream2 = tokio_util::io::ReaderStream::new(file2);
884        let chained = stream1.chain(stream2);
885        let mapped = chained
886            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
887
888        // File context input
889        let (sx, rx) = async_channel::bounded(10);
890        let privkey_bytes = BASE64_STANDARD
891            .decode("MC4CAQAwBQYDK2VuBCIEIFDnbf0aEpZxwEdy1qG4xpV8gVNq7zEREtMjLzCE6R5x")
892            .unwrap();
893        let privkey: [u8; 32] = privkey_bytes[privkey_bytes.len() - 32..]
894            .to_vec()
895            .try_into()
896            .unwrap();
897
898        let pubkey_bytes = BASE64_STANDARD
899            .decode("MCowBQYDK2VuAyEA2laqNukb4+2am7QdC6eDANu1DDuKdC5LPtYQM+XE5k8=")
900            .unwrap();
901        let pubkey: [u8; 32] = pubkey_bytes[pubkey_bytes.len() - 32..]
902            .to_vec()
903            .try_into()
904            .unwrap();
905
906        sx.send(Message::FileContext(FileContext {
907            file_path: "file1.txt".to_string(),
908            compressed_size: file1_size,
909            decompressed_size: file1_size,
910            recipients_pubkeys: vec![pubkey],
911            encryption_key: EncryptionKey::Same(
912                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
913                    .to_vec()
914                    .to_vec()
915                    .try_into()
916                    .unwrap(),
917            ),
918            ..Default::default()
919        }))
920        .await
921        .unwrap();
922
923        sx.send(Message::FileContext(FileContext {
924            file_path: "file2.txt".to_string(),
925            compressed_size: file2_size,
926            decompressed_size: file2_size,
927            recipients_pubkeys: vec![pubkey],
928            encryption_key: EncryptionKey::Same(
929                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
930                    .to_vec()
931                    .to_vec()
932                    .try_into()
933                    .unwrap(),
934            ),
935            ..Default::default()
936        }))
937        .await
938        .unwrap();
939
940        // Create a new GenericStreamReadWriter
941        let mut aswr = GenericStreamReadWriter::new_with_writer(mapped, &mut pithos_out)
942            .add_transformer(PithosTransformer::new())
943            .add_transformer(FooterGenerator::new(None));
944        aswr.add_message_receiver(rx).await.unwrap();
945        aswr.process().await.unwrap();
946
947        // Parse Footer
948        let mut pithos_in = File::open(&pithos_out_path).await.unwrap();
949        let file_meta = pithos_in.metadata().await.unwrap();
950        let footer_prediction = if file_meta.len() < 65536 * 2 {
951            file_meta.len() // 131072 always fits in i64 ...
952        } else {
953            65536 * 2
954        };
955
956        // Read footer bytes in FooterParser
957        pithos_in
958            .seek(SeekFrom::End(-(footer_prediction as i64)))
959            .await
960            .unwrap();
961        let buf = &mut vec![0; footer_prediction as usize];
962        pithos_in.read_exact(buf).await.unwrap();
963
964        let mut parser = FooterParser::new(buf).unwrap();
965        parser = parser.add_recipient(&privkey);
966        parser = parser.parse().unwrap();
967
968        // Check if bytes are missing
969        let mut missing_buf;
970        if let FooterParserState::Missing(missing_bytes) = parser.state {
971            let needed_bytes = footer_prediction + missing_bytes as u64;
972            pithos_in
973                .seek(SeekFrom::End(-(needed_bytes as i64)))
974                .await
975                .unwrap();
976            missing_buf = vec![0; missing_bytes];
977            pithos_in.read_exact(&mut missing_buf).await.unwrap();
978
979            parser = parser.add_bytes(&missing_buf).unwrap();
980            parser = parser.parse().unwrap()
981        }
982
983        // Parse the footer bytes and display Table of Contents
984        let footer: Footer = parser.try_into().unwrap();
985
986        let keys = footer
987            .encryption_keys
988            .map(|keys| {
989                keys.keys
990                    .iter()
991                    .filter_map(|(k, idx)| {
992                        if let DirOrFileIdx::File(i) = idx {
993                            Some((k.clone(), *i))
994                        } else {
995                            None
996                        }
997                    })
998                    .collect::<Vec<_>>()
999            })
1000            .unwrap_or_default();
1001
1002        let mut tar_gz_file = File::create(&tar_gz_out_path).await.unwrap();
1003        let read_stream = tokio_util::io::ReaderStream::new(pithos_in)
1004            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
1005
1006        let (sx2, rx2) = async_channel::bounded(10);
1007        let mut reader = GenericStreamReadWriter::new_with_writer(read_stream, &mut tar_gz_file)
1008            .add_transformer(ChaCha20Dec::new_with_fixed_list(keys).unwrap())
1009            .add_transformer(ZstdDec::new())
1010            .add_transformer(TarEnc::new())
1011            .add_transformer(GzipEnc::new());
1012        reader.add_message_receiver(rx2).await.unwrap();
1013
1014        for (idx, file) in footer.table_of_contents.files.into_iter().enumerate() {
1015            if let FileContextVariants::FileDecrypted(file) = file {
1016                sx2.send(Message::FileContext(
1017                    file.try_into_file_context(idx).unwrap(),
1018                ))
1019                .await
1020                .unwrap();
1021            }
1022        }
1023        reader.process().await.unwrap();
1024    }
1025
1026    #[tokio::test]
1027    async fn e2e_pithos_rewrite_footer() {
1028        // File handling
1029        let temp_dir = TempDir::new().unwrap();
1030        let pithos_out_path1 = temp_dir.path().join("test.txt.out.pto");
1031        let pithos_out_path2 = temp_dir.path().join("test.txt.out.upd.pto");
1032        let mut pithos_out = File::create(&pithos_out_path1).await.unwrap();
1033
1034        let input_file = File::open("test.txt").await.unwrap();
1035        let input_size = input_file.metadata().await.unwrap().len();
1036        let input_stream = tokio_util::io::ReaderStream::new(input_file)
1037            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
1038
1039        // File context input
1040        let privkey_bytes = BASE64_STANDARD
1041            .decode("MC4CAQAwBQYDK2VuBCIEIFDnbf0aEpZxwEdy1qG4xpV8gVNq7zEREtMjLzCE6R5x")
1042            .unwrap();
1043        let privkey: [u8; 32] = privkey_bytes[privkey_bytes.len() - 32..]
1044            .to_vec()
1045            .try_into()
1046            .unwrap();
1047
1048        let pubkey_bytes = BASE64_STANDARD
1049            .decode("MCowBQYDK2VuAyEA2laqNukb4+2am7QdC6eDANu1DDuKdC5LPtYQM+XE5k8=")
1050            .unwrap();
1051        let pubkey: [u8; 32] = pubkey_bytes[pubkey_bytes.len() - 32..]
1052            .to_vec()
1053            .try_into()
1054            .unwrap();
1055
1056        let (sx, rx) = async_channel::bounded(10);
1057        sx.send(Message::FileContext(FileContext {
1058            file_path: "file1.txt".to_string(),
1059            compressed_size: input_size,
1060            decompressed_size: input_size,
1061            recipients_pubkeys: vec![pubkey],
1062            encryption_key: EncryptionKey::Same(
1063                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1064                    .to_vec()
1065                    .to_vec()
1066                    .try_into()
1067                    .unwrap(),
1068            ),
1069            ..Default::default()
1070        }))
1071        .await
1072        .unwrap();
1073
1074        // Create a new GenericStreamReadWriter
1075        let mut aswr = GenericStreamReadWriter::new_with_writer(input_stream, &mut pithos_out)
1076            .add_transformer(PithosTransformer::new())
1077            .add_transformer(FooterGenerator::new(None));
1078        aswr.add_message_receiver(rx).await.unwrap();
1079        aswr.process().await.unwrap();
1080
1081        // Parse Footer
1082        let mut pithos_input = File::open(&pithos_out_path1).await.unwrap();
1083        let file_meta = pithos_input.metadata().await.unwrap();
1084
1085        let footer_prediction = if file_meta.len() < 65536 * 2 {
1086            file_meta.len() // 131072 always fits in i64 ...
1087        } else {
1088            65536 * 2
1089        };
1090
1091        // Read footer bytes in FooterParser
1092        pithos_input
1093            .seek(SeekFrom::End(-(footer_prediction as i64)))
1094            .await
1095            .unwrap();
1096        let buf = &mut vec![0; footer_prediction as usize];
1097        pithos_input.read_exact(buf).await.unwrap();
1098
1099        let mut parser = FooterParser::new(buf).unwrap();
1100        parser = parser.add_recipient(&privkey);
1101        parser = parser.parse().unwrap();
1102
1103        // Check if bytes are missing
1104        let mut missing_buf;
1105        if let FooterParserState::Missing(missing_bytes) = parser.state {
1106            let needed_bytes = footer_prediction + missing_bytes as u64;
1107            pithos_input
1108                .seek(SeekFrom::End(-(needed_bytes as i64)))
1109                .await
1110                .unwrap();
1111            missing_buf = vec![0; missing_bytes];
1112            pithos_input.read_exact(&mut missing_buf).await.unwrap();
1113
1114            parser = parser.add_bytes(&missing_buf).unwrap();
1115            parser = parser.parse().unwrap()
1116        }
1117
1118        // Parse the footer bytes and display Table of Contents ...
1119        let footer: Footer = parser.try_into().unwrap();
1120
1121        // Create reader stream for Pithos file
1122        pithos_input.seek(SeekFrom::Start(0)).await.unwrap();
1123        let read_stream = tokio_util::io::ReaderStream::new(pithos_input)
1124            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
1125
1126        let privkey_bytes_2 = BASE64_STANDARD
1127            .decode("MC4CAQAwBQYDK2VuBCIEIMhHHRAu72qdkx9I4D08RD3OQniJxGUI420aPlZwAJtX")
1128            .unwrap();
1129        let privkey_2: [u8; 32] = privkey_bytes_2[privkey_bytes_2.len() - 32..]
1130            .to_vec()
1131            .try_into()
1132            .unwrap();
1133
1134        let pubkey_bytes_2 = BASE64_STANDARD
1135            .decode("MCowBQYDK2VuAyEAoqu7pzwam2uks5EseS06jQP6ISX42f613KKWm8cLM1M=")
1136            .unwrap();
1137        let pubkey_2: [u8; 32] = pubkey_bytes_2[pubkey_bytes_2.len() - 32..]
1138            .to_vec()
1139            .try_into()
1140            .unwrap();
1141
1142        let (_, rx2) = async_channel::bounded(10);
1143        let mut pithos_out_2 = File::create(&pithos_out_path2).await.unwrap();
1144        let mut reader = GenericStreamReadWriter::new_with_writer(read_stream, &mut pithos_out_2)
1145            .add_transformer(FooterUpdater::new(vec![pubkey_2], footer));
1146        reader.add_message_receiver(rx2).await.unwrap();
1147        reader.process().await.unwrap();
1148
1149        // Parse Footer
1150        let mut pithos_input = File::open(pithos_out_path2).await.unwrap();
1151        let file_meta = pithos_input.metadata().await.unwrap();
1152
1153        let footer_prediction = if file_meta.len() < 65536 * 2 {
1154            file_meta.len() // 131072 always fits in i64 ...
1155        } else {
1156            65536 * 2
1157        };
1158
1159        // Read footer bytes in FooterParser
1160        pithos_input
1161            .seek(SeekFrom::End(-(footer_prediction as i64)))
1162            .await
1163            .unwrap();
1164        let buf = &mut vec![0; footer_prediction as usize]; // Has to be vec as length is defined by dynamic value
1165        pithos_input.read_exact(buf).await.unwrap();
1166
1167        let mut parser = FooterParser::new(buf).unwrap();
1168        parser = parser.add_recipient(&privkey_2);
1169        parser = parser.parse().unwrap();
1170
1171        let footer: Footer = parser.try_into().unwrap();
1172        assert!(footer.encryption_keys.unwrap().keys.len() > 0)
1173    }
1174
1175    #[tokio::test]
1176    async fn e2e_pithos_extractor() {
1177        // File handling
1178        let temp_dir = TempDir::new().unwrap();
1179        let pithos_out_path = temp_dir.path().join("test.txt.pto");
1180        let mut pithos_out = File::create(&pithos_out_path).await.unwrap();
1181
1182        let input_file = File::open("test.txt").await.unwrap();
1183        let input_size = input_file.metadata().await.unwrap().len();
1184        let input_stream = tokio_util::io::ReaderStream::new(input_file)
1185            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
1186
1187        // File context input
1188        let privkey_bytes = BASE64_STANDARD
1189            .decode("MC4CAQAwBQYDK2VuBCIEIFDnbf0aEpZxwEdy1qG4xpV8gVNq7zEREtMjLzCE6R5x")
1190            .unwrap();
1191        let privkey: [u8; 32] = privkey_bytes[privkey_bytes.len() - 32..]
1192            .to_vec()
1193            .try_into()
1194            .unwrap();
1195
1196        let pubkey_bytes = BASE64_STANDARD
1197            .decode("MCowBQYDK2VuAyEA2laqNukb4+2am7QdC6eDANu1DDuKdC5LPtYQM+XE5k8=")
1198            .unwrap();
1199        let pubkey: [u8; 32] = pubkey_bytes[pubkey_bytes.len() - 32..]
1200            .to_vec()
1201            .try_into()
1202            .unwrap();
1203
1204        let (sx, rx) = async_channel::bounded(10);
1205        sx.send(Message::FileContext(FileContext {
1206            file_path: "file1.txt".to_string(),
1207            compressed_size: input_size,
1208            decompressed_size: input_size,
1209            recipients_pubkeys: vec![pubkey],
1210            encryption_key: EncryptionKey::Same(
1211                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1212                    .to_vec()
1213                    .to_vec()
1214                    .try_into()
1215                    .unwrap(),
1216            ),
1217            ..Default::default()
1218        }))
1219        .await
1220        .unwrap();
1221
1222        // Create a new GenericStreamReadWriter
1223        let mut aswr = GenericStreamReadWriter::new_with_writer(input_stream, &mut pithos_out)
1224            .add_transformer(PithosTransformer::new())
1225            .add_transformer(FooterGenerator::new(None));
1226        aswr.add_message_receiver(rx).await.unwrap();
1227        aswr.process().await.unwrap();
1228
1229        // Parse Footer
1230        let mut pithos_input = File::open(pithos_out_path).await.unwrap();
1231        let file_meta = pithos_input.metadata().await.unwrap();
1232
1233        let footer_prediction = if file_meta.len() < 65536 * 2 {
1234            file_meta.len() // 131072 always fits in i64 ...
1235        } else {
1236            65536 * 2
1237        };
1238
1239        // Read footer bytes in FooterParser
1240        pithos_input
1241            .seek(SeekFrom::End(-(footer_prediction as i64)))
1242            .await
1243            .unwrap();
1244        let buf = &mut vec![0; footer_prediction as usize];
1245        pithos_input.read_exact(buf).await.unwrap();
1246
1247        let mut parser = FooterParser::new(buf).unwrap();
1248        parser = parser.add_recipient(&privkey);
1249        parser = parser.parse().unwrap();
1250
1251        // Parse the footer bytes and display Table of Contents
1252        let footer: Footer = parser.try_into().unwrap();
1253
1254        // Read footer with FooterExtractor
1255        let mut vec = Vec::new();
1256        pithos_input.seek(SeekFrom::Start(0)).await.unwrap();
1257        let input_stream = tokio_util::io::ReaderStream::new(pithos_input)
1258            .map_err(|_| Box::<dyn std::error::Error + Send + Sync + 'static>::from("a_str_error"));
1259
1260        let (extractor, rcv) = FooterExtractor::new(Some(privkey));
1261        GenericStreamReadWriter::new_with_writer(input_stream, &mut vec)
1262            .add_transformer(extractor)
1263            .process()
1264            .await
1265            .unwrap();
1266
1267        let extracted_footer = rcv.recv_blocking().unwrap();
1268        assert_eq!(extracted_footer, footer);
1269    }
1270
1271    #[tokio::test]
1272    async fn e2e_test_parts_decryptor() {
1273        let file = File::open("test.txt").await.unwrap();
1274        let file2 = vec![];
1275
1276        let repeated: Vec<u64> = vec![65564u64 * 60, 65564 * 16, 65564, 50860];
1277
1278        // Create a new GenericReadWriter
1279        GenericReadWriter::new_with_writer(file, file2)
1280            .add_transformer(
1281                ChaCha20Enc::new_with_fixed(
1282                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1283                        .to_vec()
1284                        .try_into()
1285                        .unwrap(),
1286                )
1287                .unwrap(),
1288            )
1289            .add_transformer(ChaCha20DecParts::new_with_lengths(
1290                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1291                    .to_vec()
1292                    .try_into()
1293                    .unwrap(),
1294                repeated,
1295            ))
1296            .process()
1297            .await
1298            .unwrap();
1299    }
1300
1301    #[tokio::test]
1302    async fn e2e_test_resilient_decryptor_no_lengths() {
1303        // Encrypt test file
1304        let temp_dir = TempDir::new().unwrap();
1305        let enc_file_path = temp_dir.path().join("test.txt.enc");
1306
1307        let file_in = File::open("test.txt").await.unwrap();
1308        let mut enc_out = File::options()
1309            .create(true)
1310            .write(true)
1311            .truncate(true)
1312            .open(&enc_file_path)
1313            .await
1314            .unwrap();
1315
1316        GenericReadWriter::new_with_writer(file_in, &mut enc_out)
1317            .add_transformer(
1318                ChaCha20Enc::new_with_fixed(
1319                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1320                        .to_vec()
1321                        .try_into()
1322                        .unwrap(),
1323                )
1324                .unwrap(),
1325            )
1326            .process()
1327            .await
1328            .unwrap();
1329
1330        // Read encrypted parts in buffer
1331        let mut input = Vec::new();
1332        let mut enc_file = File::open(enc_file_path).await.unwrap();
1333        enc_file.read_to_end(&mut input).await.unwrap();
1334
1335        // Create a new GenericReadWriter that decrypts the parts
1336        let mut output = vec![];
1337        let part_lengths: Vec<u64> = vec![];
1338        GenericReadWriter::new_with_writer(input.as_slice(), &mut output)
1339            .add_transformer(ChaChaResilient::new_with_lengths(
1340                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1341                    .to_vec()
1342                    .try_into()
1343                    .unwrap(),
1344                part_lengths,
1345            ))
1346            .process()
1347            .await
1348            .unwrap();
1349
1350        // Assert output is same as original file
1351        let mut original_file = File::open("test.txt").await.unwrap();
1352        let file_size = original_file.metadata().await.unwrap().len();
1353        assert_eq!(output.len() as u64, file_size);
1354
1355        let mut original_bytes = Vec::new();
1356        original_file
1357            .read_to_end(&mut original_bytes)
1358            .await
1359            .unwrap();
1360        assert_eq!(output, original_bytes);
1361    }
1362
1363    #[tokio::test]
1364    async fn e2e_test_resilient_decryptor_single() {
1365        // Encrypt single part
1366        let temp_dir = TempDir::new().unwrap();
1367        let part_1_path = temp_dir.path().join("test.1.txt.enc");
1368        let mut part_1_out = File::create(&part_1_path).await.unwrap();
1369
1370        let mut input = File::open("test.txt").await.unwrap();
1371        let mut part_1_bytes = vec![0; 70113];
1372        input.read_exact(&mut part_1_bytes).await.unwrap();
1373
1374        GenericReadWriter::new_with_writer(part_1_bytes.as_ref(), &mut part_1_out)
1375            .add_transformer(
1376                ChaCha20Enc::new_with_fixed(
1377                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1378                        .to_vec()
1379                        .try_into()
1380                        .unwrap(),
1381                )
1382                .unwrap(),
1383            )
1384            .process()
1385            .await
1386            .unwrap();
1387
1388        // Read encrypted part in buffer
1389        let mut input = Vec::new();
1390        let mut file_part_1 = File::open(part_1_path).await.unwrap();
1391        file_part_1.read_to_end(&mut input).await.unwrap();
1392
1393        let mut output = vec![];
1394        let part_lengths: Vec<u64> = vec![part_1_bytes.len() as u64];
1395
1396        // Create a new GenericReadWriter that decrypts the part
1397        GenericReadWriter::new_with_writer(input.as_slice(), &mut output)
1398            .add_transformer(ChaChaResilient::new_with_lengths(
1399                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1400                    .to_vec()
1401                    .try_into()
1402                    .unwrap(),
1403                part_lengths,
1404            ))
1405            .process()
1406            .await
1407            .unwrap();
1408
1409        assert_eq!(output, part_1_bytes);
1410    }
1411
1412    #[tokio::test]
1413    async fn e2e_test_resilient_decryptor_multi() {
1414        // Create temp paths for encrypted parts
1415        let temp_dir = TempDir::new().unwrap();
1416        let part_1_path = temp_dir.path().join("test.1.txt.enc");
1417        let part_2_path = temp_dir.path().join("test.2.txt.enc");
1418        let mut input = File::open("test.txt").await.unwrap();
1419
1420        // Encrypt parts individually
1421        let mut part_1_bytes = vec![0; 70113];
1422        input.read_exact(&mut part_1_bytes).await.unwrap();
1423        let part_1_out = File::create(&part_1_path).await.unwrap();
1424
1425        let mut part_2_bytes = Vec::new();
1426        input.read_to_end(&mut part_2_bytes).await.unwrap();
1427        let part_2_out = File::create(&part_2_path).await.unwrap();
1428
1429        for (input, mut output) in vec![
1430            (part_1_bytes.as_ref(), part_1_out),
1431            (part_2_bytes.as_ref(), part_2_out),
1432        ] {
1433            GenericReadWriter::new_with_writer(input, &mut output)
1434                .add_transformer(
1435                    ChaCha20Enc::new_with_fixed(
1436                        b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1437                            .to_vec()
1438                            .try_into()
1439                            .unwrap(),
1440                    )
1441                    .unwrap(),
1442                )
1443                .process()
1444                .await
1445                .unwrap();
1446        }
1447
1448        // Read encrypted parts in buffer
1449        let mut input = Vec::new();
1450        let mut file_part_1 = File::open(part_1_path).await.unwrap();
1451        file_part_1.read_to_end(&mut input).await.unwrap();
1452        let mut file_part_2 = File::open(part_2_path).await.unwrap();
1453        file_part_2.read_to_end(&mut input).await.unwrap();
1454
1455        let mut output = vec![];
1456        let part_lengths: Vec<u64> = vec![4605, 46283];
1457
1458        // Create a new GenericReadWriter that decrypts the parts
1459        GenericReadWriter::new_with_writer(input.as_slice(), &mut output)
1460            .add_transformer(ChaChaResilient::new_with_lengths(
1461                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1462                    .to_vec()
1463                    .try_into()
1464                    .unwrap(),
1465                part_lengths,
1466            ))
1467            .process()
1468            .await
1469            .unwrap();
1470
1471        // Assert output is same as original file
1472        let mut original_file = File::open("test.txt").await.unwrap();
1473        let file_size = original_file.metadata().await.unwrap().len();
1474        assert_eq!(output.len() as u64, file_size);
1475
1476        let mut original_bytes = Vec::new();
1477        original_file
1478            .read_to_end(&mut original_bytes)
1479            .await
1480            .unwrap();
1481        assert_eq!(output, original_bytes);
1482    }
1483
1484    #[tokio::test]
1485    async fn e2e_test_resilient_decryptor_multi_with_compression() {
1486        // Create temp paths for encrypted parts
1487        let temp_dir = TempDir::new().unwrap();
1488        let part_1_path = temp_dir.path().join("test.1.txt.enc");
1489        let part_2_path = temp_dir.path().join("test.2.txt.enc");
1490        let mut input = File::open("test.txt").await.unwrap();
1491
1492        // Encrypt parts individually
1493        let mut part_1_bytes = vec![0; 70113];
1494        input.read_exact(&mut part_1_bytes).await.unwrap();
1495        let part_1_out = File::create(&part_1_path).await.unwrap();
1496
1497        let mut part_2_bytes = Vec::new();
1498        input.read_to_end(&mut part_2_bytes).await.unwrap();
1499        let part_2_out = File::create(&part_2_path).await.unwrap();
1500
1501        for (input, mut output) in vec![
1502            (part_1_bytes.as_ref(), part_1_out),
1503            (part_2_bytes.as_ref(), part_2_out),
1504        ] {
1505            GenericReadWriter::new_with_writer(input, &mut output)
1506                .add_transformer(ZstdEnc::new())
1507                .add_transformer(
1508                    ChaCha20Enc::new_with_fixed(
1509                        b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1510                            .to_vec()
1511                            .try_into()
1512                            .unwrap(),
1513                    )
1514                    .unwrap(),
1515                )
1516                .process()
1517                .await
1518                .unwrap();
1519        }
1520
1521        // Read encrypted parts in buffer
1522        let mut input = Vec::new();
1523        let mut file_part_1 = File::open(part_1_path).await.unwrap();
1524        let file_part_1_size = file_part_1.metadata().await.unwrap().len();
1525        file_part_1.read_to_end(&mut input).await.unwrap();
1526
1527        let mut file_part_2 = File::open(part_2_path).await.unwrap();
1528        let file_part_2_size = file_part_2.metadata().await.unwrap().len();
1529        file_part_2.read_to_end(&mut input).await.unwrap();
1530
1531        let mut output = vec![];
1532        let part_lengths: Vec<u64> = vec![file_part_1_size % 65564, file_part_2_size % 65564]; //vec![4605, 46283];
1533
1534        // Create a new GenericReadWriter that decrypts the parts
1535        GenericReadWriter::new_with_writer(input.as_slice(), &mut output)
1536            .add_transformer(ChaChaResilient::new_with_lengths(
1537                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1538                    .to_vec()
1539                    .try_into()
1540                    .unwrap(),
1541                part_lengths,
1542            ))
1543            .add_transformer(ZstdDec::new())
1544            .process()
1545            .await
1546            .unwrap();
1547
1548        // Assert output is same as original file
1549        let mut original_file = File::open("test.txt").await.unwrap();
1550        let file_size = original_file.metadata().await.unwrap().len();
1551        assert_eq!(output.len() as u64, file_size);
1552
1553        let mut original_bytes = Vec::new();
1554        original_file
1555            .read_to_end(&mut original_bytes)
1556            .await
1557            .unwrap();
1558        assert_eq!(output, original_bytes);
1559    }
1560
1561    #[tokio::test]
1562    async fn transformer_output_comparison() {
1563        let original_file = File::open("test.txt").await.unwrap();
1564        let file_size = original_file.metadata().await.unwrap().len();
1565
1566        // Compress and encrypt with ZstdEnc+ChaChaEnc
1567        let mut comp_enc_out = Vec::new();
1568        GenericReadWriter::new_with_writer(original_file, &mut comp_enc_out)
1569            .add_transformer(ZstdEnc::new())
1570            .add_transformer(
1571                ChaCha20Enc::new_with_fixed(
1572                    b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1573                        .to_vec()
1574                        .to_vec()
1575                        .try_into()
1576                        .unwrap(),
1577                )
1578                .unwrap(),
1579            )
1580            .process()
1581            .await
1582            .unwrap();
1583
1584        // Compress and encrypt with PithosTransformer
1585        let (sx, rx) = async_channel::bounded(10);
1586        sx.send(Message::FileContext(FileContext {
1587            file_path: "test.txt".to_string(),
1588            compressed_size: file_size,
1589            decompressed_size: file_size,
1590            recipients_pubkeys: vec![],
1591            encryption_key: EncryptionKey::Same(
1592                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1593                    .to_vec()
1594                    .to_vec()
1595                    .try_into()
1596                    .unwrap(),
1597            ),
1598            ..Default::default()
1599        }))
1600        .await
1601        .unwrap();
1602
1603        let original_file = File::open("test.txt").await.unwrap();
1604        let mut pithos_out = Vec::new();
1605        let mut aswr = GenericReadWriter::new_with_writer(original_file, &mut pithos_out)
1606            .add_transformer(PithosTransformer::new());
1607        aswr.add_message_receiver(rx).await.unwrap();
1608        aswr.process().await.unwrap();
1609
1610        drop(aswr);
1611        //assert_eq!(comp_enc_out.len(), pithos_out.len())
1612
1613        let mut decrypted1 = Vec::new();
1614        // Create a new GenericReadWriter that decrypts the parts
1615        GenericReadWriter::new_with_writer(comp_enc_out.as_slice(), &mut decrypted1)
1616            .add_transformer(ChaChaResilient::new_with_lengths(
1617                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1618                    .to_vec()
1619                    .try_into()
1620                    .unwrap(),
1621                vec![comp_enc_out.len() as u64],
1622            ))
1623            .add_transformer(ZstdDec::new())
1624            .process()
1625            .await
1626            .unwrap();
1627        assert_eq!(file_size, decrypted1.len() as u64);
1628
1629        let mut decrypted2 = Vec::new();
1630        // Create a new GenericReadWriter that decrypts the parts
1631        GenericReadWriter::new_with_writer(pithos_out.as_slice(), &mut decrypted2)
1632            .add_transformer(ChaChaResilient::new_with_lengths(
1633                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1634                    .to_vec()
1635                    .try_into()
1636                    .unwrap(),
1637                vec![pithos_out.len() as u64],
1638            ))
1639            .add_transformer(ZstdDec::new())
1640            .process()
1641            .await
1642            .unwrap();
1643        assert_eq!(file_size, decrypted2.len() as u64);
1644
1645        assert_eq!(decrypted1, decrypted2);
1646    }
1647
1648    #[tokio::test]
1649    async fn e2e_test_resilient_decryptor_single_chunk() {
1650        // Encrypt test file in single chunk
1651        let mut file_in = File::open("test.txt").await.unwrap();
1652        let mut file_bytes = Vec::new();
1653        file_in.read_to_end(&mut file_bytes).await.unwrap();
1654
1655        let encrypted_bytes =
1656            encrypt_chunk(&file_bytes, b"", b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea", false).unwrap();
1657
1658        // Create a new GenericReadWriter that decrypts the parts
1659        let mut output = vec![];
1660        let part_lengths: Vec<u64> = vec![encrypted_bytes.len() as u64]; //vec![5099288];
1661        GenericReadWriter::new_with_writer(encrypted_bytes.to_vec().as_slice(), &mut output)
1662            .add_transformer(ChaChaResilient::new_with_lengths(
1663                b"wvwj3485nxgyq5ub9zd3e7jsrq7a92ea"
1664                    .to_vec()
1665                    .try_into()
1666                    .unwrap(),
1667                part_lengths,
1668            ))
1669            .process()
1670            .await
1671            .unwrap();
1672
1673        // Assert output is same as original file
1674        assert_eq!(file_bytes.len(), output.len());
1675        assert_eq!(output, file_bytes);
1676    }
1677}