1use std::collections::HashMap;
236use std::convert::TryFrom;
237use std::io;
238use std::io::{Read, Seek, SeekFrom, Write};
239#[macro_use]
240extern crate bitflags;
241use crypto::hybrid::MLAEncryptionPublicKey;
242use layers::compress::COMPRESSION_LAYER_MAGIC;
243use layers::encrypt::ENCRYPTION_LAYER_MAGIC;
244use layers::strip_head_tail::StripHeadTailReader;
245use layers::traits::InnerReaderTrait;
246
247pub mod entry;
248use entry::{
249 ArchiveEntry, ArchiveEntryDataReader, ArchiveEntryId, EntryName, deserialize_entry_name,
250 serialize_entry_name,
251};
252
253mod base64;
254
255pub(crate) mod layers;
256use crate::crypto::mlakey::{MLASignatureVerificationPublicKey, MLASigningPrivateKey};
257use crate::layers::compress::{
258 CompressionLayerReader, CompressionLayerTruncatedReader, CompressionLayerWriter,
259};
260use crate::layers::encrypt::{
261 EncryptionLayerReader, EncryptionLayerTruncatedReader, EncryptionLayerWriter,
262};
263use crate::layers::position::PositionLayerWriter;
264use crate::layers::raw::{RawLayerReader, RawLayerTruncatedReader, RawLayerWriter};
265use crate::layers::signature::{
266 SIGNATURE_LAYER_MAGIC, SignatureLayerReader, SignatureLayerTruncatedReader,
267 SignatureLayerWriter,
268};
269use crate::layers::traits::{
270 InnerWriterTrait, InnerWriterType, LayerReader, LayerTruncatedReader, LayerWriter,
271};
272pub mod errors;
273use crate::errors::{Error, TruncatedReadError};
274
275pub mod config;
276use crate::config::{ArchiveReaderConfig, ArchiveWriterConfig, TruncatedReaderConfig};
277
278pub mod crypto;
279use crate::crypto::hash::{HashWrapperReader, HashWrapperWriter, Sha256Hash};
280use sha2::{Digest, Sha256, Sha512};
281
282mod format;
283pub mod helpers;
284use format::ArchiveHeader;
285
286#[cfg(test)]
287#[macro_use]
288extern crate hex_literal;
289
290const MLA_MAGIC: &[u8; 8] = b"MLAFAAAA";
293const MLA_FORMAT_VERSION: u32 = 2;
294const END_MLA_MAGIC: &[u8; 8] = b"EMLAAAAA";
295const ENTRY_NAME_MAX_SIZE: u64 = 1024;
300
301const ENTRIES_LAYER_MAGIC: &[u8; 8] = b"MLAENAAA";
302
303const EMPTY_OPTS_SERIALIZATION: &[u8; 1] = &[0];
304const EMPTY_TAIL_OPTS_SERIALIZATION: &[u8; 9] = &[0, 1, 0, 0, 0, 0, 0, 0, 0];
305
306#[derive(Debug)]
307struct Opts;
308
309impl Opts {
310 fn from_reader(mut src: impl Read) -> Result<Self, Error> {
311 let discriminant = u8::deserialize(&mut src)?;
312 match discriminant {
313 0 => (),
314 1 => {
315 let mut n = [0; 8];
316 src.read_exact(&mut n)?;
317 let n = u64::from_le_bytes(n);
318 let mut v = Vec::new();
319 src.take(n).read_to_end(&mut v)?;
320 }
322 _ => return Err(Error::DeserializationError),
323 }
324 Ok(Opts)
325 }
326
327 #[allow(clippy::unused_self)]
328 fn dump(&mut self, mut src: impl Write) -> Result<u64, Error> {
329 src.write_all(EMPTY_OPTS_SERIALIZATION)?;
331 Ok(1)
332 }
333}
334
335struct ArchiveFooter {
338 entries_info: HashMap<EntryName, EntryInfo>,
340}
341
342impl ArchiveFooter {
343 fn serialize_into<W: Write>(
349 mut dest: W,
350 entries_info: &HashMap<EntryName, ArchiveEntryId>,
351 ids_info: &HashMap<ArchiveEntryId, EntryInfo>,
352 ) -> Result<(), Error> {
353 let mut tmp = Vec::new();
356 for (k, i) in entries_info {
357 let v = ids_info.get(i).ok_or_else(|| {
358 Error::WrongWriterState(
359 "[ArchiveFooter seriliaze] Unable to find the ID".to_string(),
360 )
361 })?;
362 tmp.push((k, v));
363 }
364 tmp.sort_by_key(|(k, _)| *k);
365
366 tmp.len().serialize(&mut dest)?;
367 let mut footer_serialization_length: u64 = 8;
368 for (k, i) in tmp {
369 footer_serialization_length = footer_serialization_length
370 .checked_add(serialize_entry_name(k, &mut dest)?)
371 .ok_or(Error::SerializationError)?;
372 footer_serialization_length = footer_serialization_length
373 .checked_add(i.serialize(&mut dest)?)
374 .ok_or(Error::SerializationError)?;
375 }
376 footer_serialization_length
377 .checked_add(1)
378 .ok_or(Error::SerializationError)?
379 .serialize(&mut dest)?; Ok(())
381 }
382
383 pub fn deserialize_from<R: Read + Seek>(mut src: R) -> Result<Option<ArchiveFooter>, Error> {
385 let index_present = u8::deserialize(&mut src)?;
386 if index_present == 1 {
387 let n = u64::deserialize(&mut src)?;
389 let entries_info = (0..n)
390 .map(|_| {
391 let name = deserialize_entry_name(&mut src)?;
392 let info = EntryInfo::deserialize(&mut src)?;
393 Ok::<_, Error>((name, info))
394 })
395 .collect::<Result<HashMap<_, _>, Error>>()?;
396
397 Ok(Some(ArchiveFooter { entries_info }))
398 } else {
399 Ok(None)
400 }
401 }
402}
403
404#[derive(Debug)]
408enum ArchiveEntryBlockType {
409 EntryStart,
410 EntryContent,
411
412 EndOfArchiveData,
413 EndOfEntry,
414}
415
416impl<W: Write> MLASerialize<W> for ArchiveEntryBlockType {
417 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
418 let byte: u8 = match self {
419 ArchiveEntryBlockType::EntryStart => 0,
420 ArchiveEntryBlockType::EntryContent => 1,
421 ArchiveEntryBlockType::EndOfArchiveData => 0xFE,
422 ArchiveEntryBlockType::EndOfEntry => 0xFF,
423 };
424 byte.serialize(dest)
425 }
426}
427
428impl<R: Read> MLADeserialize<R> for ArchiveEntryBlockType {
429 fn deserialize(src: &mut R) -> Result<Self, Error> {
430 let serialized_block_type = u8::deserialize(src)?;
431 match serialized_block_type {
432 0 => Ok(ArchiveEntryBlockType::EntryStart),
433 1 => Ok(ArchiveEntryBlockType::EntryContent),
434 0xFE => Ok(ArchiveEntryBlockType::EndOfArchiveData),
435 0xFF => Ok(ArchiveEntryBlockType::EndOfEntry),
436 _ => Err(Error::WrongBlockSubFileType),
437 }
438 }
439}
440
441use format::ArchiveEntryBlock;
442
443#[derive(Debug, Clone)]
444enum ArchiveWriterState {
445 OpenedFiles {
447 ids: Vec<ArchiveEntryId>,
448 hashes: HashMap<ArchiveEntryId, Sha256>,
449 },
450 Finalized,
452}
453
454impl ArchiveWriterState {
455 fn wrap_with_hash<R: Read>(
457 &mut self,
458 id: ArchiveEntryId,
459 src: R,
460 ) -> Result<HashWrapperReader<'_, R>, Error> {
461 let hash = match self {
462 ArchiveWriterState::OpenedFiles { hashes, .. } => match hashes.get_mut(&id) {
463 Some(hash) => hash,
464 None => {
465 return Err(Error::WrongWriterState(
466 "[wrap_with_hash] Unable to find the ID".to_string(),
467 ));
468 }
469 },
470 ArchiveWriterState::Finalized => {
471 return Err(Error::WrongWriterState(
472 "[wrap_with_hash] Wrong state".to_string(),
473 ));
474 }
475 };
476
477 Ok(HashWrapperReader::new(src, hash))
478 }
479}
480
481macro_rules! check_state {
486 ( $x:expr, $y:ident ) => {{
487 match $x {
488 ArchiveWriterState::$y { .. } => (),
489 _ => {
490 return Err(Error::WrongArchiveWriterState {
491 current_state: format!("{:?}", $x).to_string(),
492 expected_state: format! {"{}", "ArchiveWriterState::$y"}.to_string(),
493 });
494 }
495 }
496 }};
497}
498
499macro_rules! check_state_file_opened {
504 ( $x:expr, $y:expr ) => {{
505 match $x {
506 ArchiveWriterState::OpenedFiles { ids, hashes } => {
507 if !ids.contains($y) || !hashes.contains_key($y) {
508 return Err(Error::WrongArchiveWriterState {
509 current_state: format!("{:?}", $x).to_string(),
510 expected_state: "ArchiveWriterState with id $y".to_string(),
511 });
512 }
513 }
514 _ => {
515 return Err(Error::WrongArchiveWriterState {
516 current_state: format!("{:?}", $x).to_string(),
517 expected_state: "ArchiveWriterState with id $y".to_string(),
518 });
519 }
520 }
521 }};
522}
523
524pub struct ArchiveWriter<'a, W: 'a + InnerWriterTrait> {
530 dest: Box<PositionLayerWriter<'a, W>>,
537 state: ArchiveWriterState,
539 entries_info: HashMap<EntryName, ArchiveEntryId>,
543 ids_info: HashMap<ArchiveEntryId, EntryInfo>,
552 next_id: ArchiveEntryId,
554 current_id: ArchiveEntryId,
556}
557
558fn vec_remove_item<T: std::cmp::PartialEq>(vec: &mut Vec<T>, item: &T) -> Option<T> {
561 let pos = vec.iter().position(|x| *x == *item)?;
562 Some(vec.remove(pos))
563}
564
565impl<W: InnerWriterTrait> ArchiveWriter<'_, W> {
566 pub fn from_config(dest: W, config: ArchiveWriterConfig) -> Result<Self, Error> {
568 let dest: InnerWriterType<W> = Box::new(RawLayerWriter::new(dest));
569
570 let archive_header = ArchiveHeader {
571 format_version_number: MLA_FORMAT_VERSION,
572 };
573 let mut archive_header_hash = Sha512::new();
574 let mut dest = HashWrapperWriter::new(dest, &mut archive_header_hash);
575 archive_header.serialize(&mut dest)?;
576 let mut dest = dest.into_inner();
577
578 dest = match config.signature {
580 Some(signature_config) => Box::new(SignatureLayerWriter::new(
581 dest,
582 signature_config,
583 archive_header_hash,
584 )?),
585 None => dest,
586 };
587 dest = match config.encryption {
588 Some(encryption_config) => {
589 Box::new(EncryptionLayerWriter::new(dest, &encryption_config)?)
590 }
591 None => dest,
592 };
593 dest = match config.compression {
594 Some(cfg) => Box::new(CompressionLayerWriter::new(dest, &cfg)?),
595 None => dest,
596 };
597
598 let mut final_dest = Box::new(PositionLayerWriter::new(dest));
600 final_dest.reset_position();
601
602 final_dest.write_all(ENTRIES_LAYER_MAGIC)?;
604 let _ = Opts.dump(&mut final_dest)?;
605
606 Ok(ArchiveWriter {
608 dest: final_dest,
609 state: ArchiveWriterState::OpenedFiles {
610 ids: Vec::new(),
611 hashes: HashMap::new(),
612 },
613 entries_info: HashMap::new(),
614 ids_info: HashMap::new(),
615 next_id: ArchiveEntryId(0),
616 current_id: ArchiveEntryId(0),
617 })
618 }
619
620 pub fn new(
630 dest: W,
631 encryption_public_keys: &[MLAEncryptionPublicKey],
632 signing_private_keys: &[MLASigningPrivateKey],
633 ) -> Result<Self, Error> {
634 let config = ArchiveWriterConfig::with_encryption_with_signature(
635 encryption_public_keys,
636 signing_private_keys,
637 )?;
638 Self::from_config(dest, config)
639 }
640
641 pub fn finalize(mut self) -> Result<W, Error> {
645 check_state!(self.state, OpenedFiles);
647 match &mut self.state {
648 ArchiveWriterState::OpenedFiles { ids, hashes } => {
649 if !ids.is_empty() || !hashes.is_empty() {
650 return Err(Error::WrongWriterState(
651 "[Finalize] At least one file is still open".to_string(),
652 ));
653 }
654 }
655 ArchiveWriterState::Finalized => {
656 return Err(Error::WrongWriterState(
658 "[Finalize] State have changes inside finalize".to_string(),
659 ));
660 }
661 }
662 self.state = ArchiveWriterState::Finalized;
663
664 ArchiveEntryBlock::EndOfArchiveData::<std::io::Empty> {}.dump(&mut self.dest)?;
668
669 self.dest.write_all(&[1])?; ArchiveFooter::serialize_into(&mut self.dest, &self.entries_info, &self.ids_info)?;
671
672 self.dest.write_all(EMPTY_TAIL_OPTS_SERIALIZATION)?; let mut final_dest = self.dest.finalize()?;
676 final_dest.write_all(EMPTY_TAIL_OPTS_SERIALIZATION)?; final_dest.write_all(END_MLA_MAGIC)?;
678 Ok(final_dest)
679 }
680
681 fn record_offset_and_size_in_index(
684 &mut self,
685 id: ArchiveEntryId,
686 size: u64,
687 ) -> Result<(), Error> {
688 let offset = self.dest.position();
689 match self.ids_info.get_mut(&id) {
690 Some(file_info) => file_info.offsets_and_sizes.push((offset, size)),
691 None => {
692 return Err(Error::WrongWriterState(
693 "[mark_continuous_block] Unable to find the ID".to_string(),
694 ));
695 }
696 }
697 self.current_id = id;
698 Ok(())
699 }
700
701 pub fn start_entry(&mut self, name: EntryName) -> Result<ArchiveEntryId, Error> {
707 check_state!(self.state, OpenedFiles);
708
709 if self.entries_info.contains_key(&name) {
710 return Err(Error::DuplicateEntryName);
711 }
712
713 let id = self.next_id;
715 self.next_id = ArchiveEntryId(
716 self.next_id
717 .0
718 .checked_add(1)
719 .ok_or(Error::SerializationError)?,
720 );
721 self.current_id = id;
722 self.entries_info.insert(name.clone(), id);
723
724 self.ids_info.insert(
726 id,
727 EntryInfo {
728 offsets_and_sizes: vec![(self.dest.position(), 0)],
729 },
730 );
731 ArchiveEntryBlock::EntryStart::<std::io::Empty> {
733 name,
734 id,
735 opts: Opts,
736 }
737 .dump(&mut self.dest)?;
738
739 match &mut self.state {
740 ArchiveWriterState::OpenedFiles { ids, hashes } => {
741 ids.push(id);
742 hashes.insert(id, Sha256::default());
743 }
744 ArchiveWriterState::Finalized => {
745 return Err(Error::WrongWriterState(
747 "[StartFile] State have changes inside start_file".to_string(),
748 ));
749 }
750 }
751 Ok(id)
752 }
753
754 pub fn append_entry_content<U: Read>(
759 &mut self,
760 id: ArchiveEntryId,
761 size: u64,
762 src: U,
763 ) -> Result<(), Error> {
764 check_state_file_opened!(&self.state, &id);
765
766 if size == 0 {
767 return Ok(());
769 }
770
771 self.record_offset_and_size_in_index(id, size)?;
772 let src = self.state.wrap_with_hash(id, src)?;
773
774 ArchiveEntryBlock::EntryContent {
775 id,
776 length: size,
777 data: Some(src),
778 opts: Opts,
779 }
780 .dump(&mut self.dest)
781 }
782
783 pub fn end_entry(&mut self, id: ArchiveEntryId) -> Result<(), Error> {
785 check_state_file_opened!(&self.state, &id);
786
787 let hash = match &mut self.state {
788 ArchiveWriterState::OpenedFiles { ids, hashes } => {
789 let hash = hashes.remove(&id).ok_or_else(|| {
790 Error::WrongWriterState("[EndFile] Unable to retrieve the hash".to_string())
791 })?;
792 vec_remove_item(ids, &id);
793 hash.finalize().into()
794 }
795 ArchiveWriterState::Finalized => {
796 return Err(Error::WrongWriterState(
798 "[EndFile] State have changes inside end_file".to_string(),
799 ));
800 }
801 };
802
803 self.record_offset_and_size_in_index(id, 0)?;
804 ArchiveEntryBlock::EndOfEntry::<std::io::Empty> {
806 id,
807 hash,
808 opts: Opts,
809 }
810 .dump(&mut self.dest)?;
811
812 Ok(())
813 }
814
815 pub fn add_entry<U: Read>(&mut self, name: EntryName, size: u64, src: U) -> Result<(), Error> {
817 let id = self.start_entry(name)?;
818 self.append_entry_content(id, size, src)?;
819 self.end_entry(id)
820 }
821
822 pub fn flush(&mut self) -> io::Result<()> {
825 self.dest.flush()
826 }
827}
828
829trait MLASerialize<W: Write> {
830 fn serialize(&self, dest: &mut W) -> Result<u64, Error>;
831}
832
833trait MLADeserialize<R: Read> {
834 fn deserialize(src: &mut R) -> Result<Self, Error>
835 where
836 Self: std::marker::Sized;
837}
838
839impl<W: Write> MLASerialize<W> for u8 {
840 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
841 dest.write_all(&[*self])?;
842 Ok(1)
843 }
844}
845
846impl<W: Write> MLASerialize<W> for u64 {
847 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
848 dest.write_all(&self.to_le_bytes())?;
849 Ok(8)
850 }
851}
852
853impl<R: Read> MLADeserialize<R> for u64 {
854 fn deserialize(src: &mut R) -> Result<Self, Error> {
855 let mut n = [0; 8];
856 src.read_exact(&mut n)
857 .map_err(|_| Error::DeserializationError)?;
858 Ok(u64::from_le_bytes(n))
859 }
860}
861
862impl<W: Write> MLASerialize<W> for usize {
863 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
864 let u64self = u64::try_from(*self).map_err(|_| Error::SerializationError)?;
865 dest.write_all(&u64self.to_le_bytes())?;
866 Ok(8)
867 }
868}
869
870impl<W: Write> MLASerialize<W> for u32 {
871 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
872 dest.write_all(&self.to_le_bytes())?;
873 Ok(4)
874 }
875}
876
877impl<R: Read> MLADeserialize<R> for u32 {
878 fn deserialize(src: &mut R) -> Result<Self, Error> {
879 let mut n = [0; 4];
880 src.read_exact(&mut n)
881 .map_err(|_| Error::DeserializationError)?;
882 Ok(u32::from_le_bytes(n))
883 }
884}
885
886impl<W: Write> MLASerialize<W> for u16 {
887 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
888 dest.write_all(&self.to_le_bytes())?;
889 Ok(2)
890 }
891}
892
893impl<R: Read> MLADeserialize<R> for u16 {
894 fn deserialize(src: &mut R) -> Result<Self, Error> {
895 let mut n = [0; 2];
896 src.read_exact(&mut n)
897 .map_err(|_| Error::DeserializationError)?;
898 Ok(u16::from_le_bytes(n))
899 }
900}
901
902impl<R: Read> MLADeserialize<R> for u8 {
903 fn deserialize(src: &mut R) -> Result<Self, Error> {
904 let mut n = [0; 1];
905 src.read_exact(&mut n)
906 .map_err(|_| Error::DeserializationError)?;
907 Ok(u8::from_le_bytes(n))
908 }
909}
910
911impl<W: Write, A: MLASerialize<W>, B: MLASerialize<W>> MLASerialize<W> for (A, B) {
912 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
913 let mut serialization_length = self.0.serialize(dest)?;
914 serialization_length = serialization_length
915 .checked_add(self.1.serialize(dest)?)
916 .ok_or(Error::SerializationError)?;
917 Ok(serialization_length)
918 }
919}
920
921impl<W: Write, T: MLASerialize<W>> MLASerialize<W> for Vec<T> {
922 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
923 let u64len = u64::try_from(self.len()).map_err(|_| Error::SerializationError)?;
924 let mut serialization_length = u64len.serialize(dest)?;
925 serialization_length = serialization_length
926 .checked_add(self.as_slice().serialize(dest)?)
927 .ok_or(Error::SerializationError)?;
928 Ok(serialization_length)
929 }
930}
931
932impl<W: Write, T: MLASerialize<W>> MLASerialize<W> for &[T] {
933 fn serialize(&self, dest: &mut W) -> Result<u64, Error> {
934 let mut serialization_length: u64 = 0;
935 for e in *self {
936 serialization_length = serialization_length
937 .checked_add(e.serialize(dest)?)
938 .ok_or(Error::SerializationError)?;
939 }
940 Ok(serialization_length)
941 }
942}
943
944impl<R: Read, T: MLADeserialize<R>> MLADeserialize<R> for Vec<T> {
945 fn deserialize(src: &mut R) -> Result<Self, Error> {
946 let n = u64::deserialize(src)?;
947 let v: Result<Vec<T>, Error> = (0..n).map(|_| T::deserialize(src)).collect();
948 v
949 }
950}
951
952impl<R: Read, const N: usize> MLADeserialize<R> for [u8; N] {
953 fn deserialize(src: &mut R) -> Result<Self, Error> {
954 let mut a = [0; N];
955 for e in &mut a {
956 *e = u8::deserialize(src)?;
957 }
958 Ok(a)
959 }
960}
961
962impl<R: Read, T1: MLADeserialize<R>, T2: MLADeserialize<R>> MLADeserialize<R> for (T1, T2) {
963 fn deserialize(src: &mut R) -> Result<Self, Error> {
964 Ok((T1::deserialize(src)?, T2::deserialize(src)?))
965 }
966}
967
968#[cfg_attr(test, derive(PartialEq, Eq, Debug, Clone))]
971pub(crate) struct EntryInfo {
972 offsets_and_sizes: Vec<(u64, u64)>,
976}
977
978impl<W: Write> MLASerialize<W> for EntryInfo {
979 fn serialize(&self, mut dest: &mut W) -> Result<u64, Error> {
980 self.offsets_and_sizes.serialize(&mut dest)
981 }
982}
983
984impl<R: Read> MLADeserialize<R> for EntryInfo {
985 fn deserialize(src: &mut R) -> Result<Self, Error> {
986 let offsets_and_sizes = MLADeserialize::deserialize(src)?;
987
988 Ok(Self { offsets_and_sizes })
989 }
990}
991
992fn read_layer_magic<R: Read>(src: &mut R) -> Result<[u8; 8], Error> {
993 let mut buf = [0; 8];
994 src.read_exact(&mut buf)?;
995 Ok(buf)
996}
997
998pub struct ArchiveReader<'a, R: 'a + InnerReaderTrait> {
1000 src: Box<dyn 'a + LayerReader<'a, R>>,
1004 metadata: Option<ArchiveFooter>,
1006}
1007
1008fn read_mla_entries_header(mut src: impl Read) -> Result<(), Error> {
1009 let mut magic = [0u8; 8];
1011 src.read_exact(&mut magic)?;
1012 if magic != *ENTRIES_LAYER_MAGIC {
1013 return Err(Error::WrongMagic);
1014 }
1015 read_mla_entries_header_skip_magic(src)
1016}
1017
1018fn read_mla_entries_header_skip_magic(mut src: impl Read) -> Result<(), Error> {
1019 let _ = Opts::from_reader(&mut src)?; Ok(())
1021}
1022
1023impl<'b, R: 'b + InnerReaderTrait> ArchiveReader<'b, R> {
1024 pub fn from_config(
1026 mut src: R,
1027 config: ArchiveReaderConfig,
1028 ) -> Result<(Self, Vec<MLASignatureVerificationPublicKey>), Error> {
1029 src.rewind()?;
1031
1032 let mut archive_header_hash = Sha512::new();
1034 let mut src = HashWrapperReader::new(src, &mut archive_header_hash);
1035 ArchiveHeader::deserialize(&mut src)?;
1036
1037 let mut raw_src = Box::new(RawLayerReader::new(src.into_inner()));
1039 raw_src.reset_position()?;
1040 let mut src: Box<dyn 'b + LayerReader<'b, R>> = raw_src;
1041
1042 let end_magic_position = src.seek(SeekFrom::End(-8))?;
1044 let end_magic = read_layer_magic(&mut src)?;
1045 if &end_magic != END_MLA_MAGIC {
1046 return Err(Error::WrongEndMagic);
1047 }
1048 src.seek(SeekFrom::End(-16))?;
1049 let mla_footer_options_length = u64::deserialize(&mut src)?;
1050 let mla_tail_len = mla_footer_options_length
1051 .checked_add(16)
1052 .ok_or(Error::DeserializationError)?;
1053 let inner_len = end_magic_position
1054 .checked_add(8)
1055 .ok_or(Error::DeserializationError)?;
1056 src.seek(SeekFrom::Start(0))?;
1057 src = Box::new(StripHeadTailReader::new(
1058 src,
1059 0,
1060 mla_tail_len,
1061 inner_len,
1062 0,
1063 )?);
1064
1065 let mut src = HashWrapperReader::new(src, &mut archive_header_hash);
1069 let mut magic = read_layer_magic(&mut src)?;
1070 let mut src = src.into_inner();
1071 let accept_unencrypted = config.accept_unencrypted;
1072
1073 let mut signed_persistent_encryption_config = None;
1079 let mut keys_with_valid_signatures = Vec::new();
1080
1081 if magic == SIGNATURE_LAYER_MAGIC {
1082 let (sig_layer, new_keys_with_valid_signatures, read_persistent_encryption_config) =
1083 SignatureLayerReader::new_skip_magic(
1084 src,
1085 &config.signature_reader_config,
1086 archive_header_hash,
1087 )?;
1088 signed_persistent_encryption_config = read_persistent_encryption_config;
1089 keys_with_valid_signatures = new_keys_with_valid_signatures;
1090 src = Box::new(sig_layer);
1091 src.initialize()?;
1092 magic = read_layer_magic(&mut src)?;
1093 } else if config.signature_reader_config.signature_check {
1094 return Err(Error::SignatureVerificationAskedButNoSignatureLayerFound);
1095 }
1096
1097 if &magic == ENCRYPTION_LAYER_MAGIC {
1098 src = Box::new(EncryptionLayerReader::new_skip_magic(
1099 src,
1100 config.encrypt,
1101 signed_persistent_encryption_config,
1102 )?);
1103 src.initialize()?;
1104 magic = read_layer_magic(&mut src)?;
1105 } else if !accept_unencrypted {
1106 return Err(Error::EncryptionAskedButNotMarkedPresent);
1107 }
1108
1109 if &magic == COMPRESSION_LAYER_MAGIC {
1110 src = Box::new(CompressionLayerReader::new_skip_magic(src)?);
1111 src.initialize()?;
1112 }
1113
1114 src.seek(SeekFrom::End(-8))?;
1116 let entries_footer_options_length = u64::deserialize(&mut src)?;
1117 let entries_footer_length_offset_from_end =
1121 (-16i64) .checked_sub_unsigned(entries_footer_options_length)
1123 .ok_or(Error::DeserializationError)?;
1124 src.seek(SeekFrom::End(entries_footer_length_offset_from_end))?;
1126 let entries_footer_length = u64::deserialize(&mut src)?;
1127 let start_of_entries_footer_from_current = (-8i64)
1129 .checked_sub_unsigned(entries_footer_length)
1130 .ok_or(Error::DeserializationError)?;
1131 src.seek(SeekFrom::Current(start_of_entries_footer_from_current))?;
1132 let metadata = ArchiveFooter::deserialize_from(&mut src)?;
1133
1134 src.rewind()?;
1136
1137 read_mla_entries_header(&mut src)?;
1138
1139 Ok((ArchiveReader { src, metadata }, keys_with_valid_signatures))
1140 }
1141
1142 pub fn list_entries(&self) -> Result<impl Iterator<Item = &EntryName>, Error> {
1146 if let Some(ArchiveFooter { entries_info, .. }) = &self.metadata {
1147 Ok(entries_info.keys())
1148 } else {
1149 Err(Error::MissingMetadata)
1150 }
1151 }
1152
1153 pub fn get_hash(&mut self, name: &EntryName) -> Result<Option<Sha256Hash>, Error> {
1155 if let Some(ArchiveFooter { entries_info }) = &self.metadata {
1156 let Some(file_info) = entries_info.get(name) else {
1158 return Ok(None);
1159 };
1160
1161 let eoe_offset = file_info
1163 .offsets_and_sizes
1164 .last()
1165 .ok_or(Error::DeserializationError)?
1166 .0;
1167 self.src.seek(SeekFrom::Start(eoe_offset))?;
1168
1169 match ArchiveEntryBlock::from(&mut self.src)? {
1171 ArchiveEntryBlock::EndOfEntry { hash, .. } => Ok(Some(hash)),
1172 _ => Err(Error::WrongReaderState(
1173 "[ArchiveReader] last offset must point to a EndOfEntry".to_string(),
1174 )),
1175 }
1176 } else {
1177 Err(Error::MissingMetadata)
1178 }
1179 }
1180
1181 pub fn get_entry(
1187 &mut self,
1188 name: EntryName,
1189 ) -> Result<Option<ArchiveEntry<'_, impl InnerReaderTrait>>, Error> {
1190 if let Some(ArchiveFooter { entries_info }) = &self.metadata {
1191 let Some(file_info) = entries_info.get(&name) else {
1193 return Ok(None);
1194 };
1195 if file_info.offsets_and_sizes.is_empty() {
1196 return Err(Error::WrongReaderState(
1197 "[ArchiveReader] A file must have at least one offset".to_string(),
1198 ));
1199 }
1200
1201 let reader = ArchiveEntryDataReader::new(&mut self.src, &file_info.offsets_and_sizes)?;
1203 Ok(Some(ArchiveEntry { name, data: reader }))
1204 } else {
1205 Err(Error::MissingMetadata)
1206 }
1207 }
1208}
1209
1210pub struct TruncatedArchiveReader<'a, R: 'a + Read> {
1214 src: Box<dyn 'a + LayerTruncatedReader<'a, R>>,
1218}
1219
1220const CACHE_SIZE: usize = 8 * 1024 * 1024; macro_rules! update_error {
1228 ( $x:ident = $y:expr ) => {
1229 #[allow(clippy::single_match)]
1230 match $x {
1231 TruncatedReadError::NoError => {
1232 $x = $y;
1233 }
1234 _ => {}
1235 }
1236 };
1237}
1238
1239impl<'b, R: 'b + Read> TruncatedArchiveReader<'b, R> {
1240 pub fn from_config(mut src: R, config: TruncatedReaderConfig) -> Result<Self, Error> {
1242 ArchiveHeader::deserialize(&mut src)?;
1243
1244 let mut src: Box<dyn 'b + LayerTruncatedReader<'b, R>> =
1246 Box::new(RawLayerTruncatedReader::new(src));
1247 let accept_unencrypted = config.accept_unencrypted;
1248 let truncated_decryption_mode = config.truncated_decryption_mode;
1249 let mut magic = read_layer_magic(&mut src)?;
1250 if magic == SIGNATURE_LAYER_MAGIC {
1251 src = Box::new(SignatureLayerTruncatedReader::new_skip_magic(src)?);
1252 magic = read_layer_magic(&mut src)?;
1253 }
1254 if &magic == ENCRYPTION_LAYER_MAGIC {
1255 src = Box::new(EncryptionLayerTruncatedReader::new_skip_magic(
1256 src,
1257 config.encrypt,
1258 None,
1259 truncated_decryption_mode,
1260 )?);
1261 magic = read_layer_magic(&mut src)?;
1262 } else if !accept_unencrypted {
1263 return Err(Error::EncryptionAskedButNotMarkedPresent);
1264 }
1265 if &magic == COMPRESSION_LAYER_MAGIC {
1266 src = Box::new(CompressionLayerTruncatedReader::new_skip_magic(src)?);
1267 magic = read_layer_magic(&mut src)?;
1268 }
1269
1270 if &magic != ENTRIES_LAYER_MAGIC {
1271 return Err(Error::DeserializationError);
1272 }
1273
1274 read_mla_entries_header_skip_magic(&mut src)?;
1276
1277 Ok(Self { src })
1278 }
1279
1280 #[allow(clippy::cognitive_complexity)]
1284 pub fn convert_to_archive<W: InnerWriterTrait>(
1285 &mut self,
1286 mut output: ArchiveWriter<W>,
1287 ) -> Result<TruncatedReadError, Error> {
1288 let mut error = TruncatedReadError::NoError;
1289
1290 let mut id_truncated2id_output: HashMap<ArchiveEntryId, ArchiveEntryId> = HashMap::new();
1293 let mut id_truncated2entryname: HashMap<ArchiveEntryId, EntryName> = HashMap::new();
1295 let mut id_truncated_done = Vec::new();
1297 let mut id_truncated2hash: HashMap<ArchiveEntryId, Sha256> = HashMap::new();
1299
1300 'read_block: loop {
1301 match ArchiveEntryBlock::from(&mut self.src) {
1302 Err(Error::IOError(err)) => {
1303 if let std::io::ErrorKind::UnexpectedEof = err.kind() {
1304 update_error!(error = TruncatedReadError::UnexpectedEOFOnNextBlock);
1305 break;
1306 }
1307 update_error!(error = TruncatedReadError::IOErrorOnNextBlock(err));
1308 break;
1309 }
1310 Err(err) => {
1311 update_error!(error = TruncatedReadError::ErrorOnNextBlock(err));
1312 break;
1313 }
1314 Ok(block) => {
1315 match block {
1316 ArchiveEntryBlock::EntryStart { name, id, opts: _ } => {
1317 if let Some(_id_output) = id_truncated2id_output.get(&id) {
1318 update_error!(error = TruncatedReadError::ArchiveEntryIDReuse(id));
1319 break 'read_block;
1320 }
1321 if id_truncated_done.contains(&id) {
1322 update_error!(
1323 error = TruncatedReadError::ArchiveEntryIDAlreadyClosed(id)
1324 );
1325 break 'read_block;
1326 }
1327
1328 id_truncated2entryname.insert(id, name.clone());
1329 let id_output = match output.start_entry(name.clone()) {
1330 Err(Error::DuplicateEntryName) => {
1331 update_error!(
1332 error = TruncatedReadError::EntryNameReuse(
1333 name.raw_content_to_escaped_string()
1334 )
1335 );
1336 break 'read_block;
1337 }
1338 Err(err) => {
1339 return Err(err);
1340 }
1341 Ok(id) => id,
1342 };
1343 id_truncated2id_output.insert(id, id_output);
1344 id_truncated2hash.insert(id, Sha256::default());
1345 }
1346 ArchiveEntryBlock::EntryContent { length, id, .. } => {
1347 let id_output = if let Some(id_output) = id_truncated2id_output.get(&id)
1348 {
1349 *id_output
1350 } else {
1351 update_error!(
1352 error = TruncatedReadError::ContentForUnknownFile(id)
1353 );
1354 break 'read_block;
1355 };
1356
1357 if id_truncated_done.contains(&id) {
1358 update_error!(
1359 error = TruncatedReadError::ArchiveEntryIDAlreadyClosed(id)
1360 );
1361 break 'read_block;
1362 }
1363 let entry = id_truncated2entryname.get(&id).expect(
1364 "`id_truncated2entryname` not more sync with `id_truncated2id_output`",
1365 );
1366 let hash = id_truncated2hash.get_mut(&id).expect(
1367 "`id_truncated2hash` not more sync with `id_truncated2id_output`",
1368 );
1369
1370 let src = &mut (&mut self.src).take(length);
1372
1373 let mut buf = vec![0; CACHE_SIZE];
1389 'content: loop {
1390 let mut next_write_pos = 0;
1391 'buf_fill: loop {
1392 match src.read(&mut buf[next_write_pos..]) {
1393 Ok(read) => {
1394 if read == 0 {
1395 break 'buf_fill;
1397 }
1398 next_write_pos = next_write_pos
1399 .checked_add(read)
1400 .ok_or(Error::DeserializationError)?;
1401 }
1402 Err(err) => {
1403 output.append_entry_content(
1405 id_output,
1406 next_write_pos as u64,
1407 &buf[..next_write_pos],
1408 )?;
1409 update_error!(
1410 error = TruncatedReadError::ErrorInFile(
1411 err,
1412 entry.raw_content_to_escaped_string()
1413 )
1414 );
1415 break 'read_block;
1416 }
1417 }
1418 if next_write_pos >= CACHE_SIZE {
1420 break 'buf_fill;
1421 }
1422 }
1423 output.append_entry_content(
1424 id_output,
1425 next_write_pos as u64,
1426 &buf[..next_write_pos],
1427 )?;
1428 hash.update(&buf[..next_write_pos]);
1429 if next_write_pos < CACHE_SIZE {
1430 break 'content;
1432 }
1433 }
1434 }
1435 ArchiveEntryBlock::EndOfEntry {
1436 id,
1437 hash,
1438 opts: Opts,
1439 } => {
1440 let id_output = if let Some(id_output) = id_truncated2id_output.get(&id)
1441 {
1442 *id_output
1443 } else {
1444 update_error!(error = TruncatedReadError::EOFForUnknownFile(id));
1445 break 'read_block;
1446 };
1447
1448 if id_truncated_done.contains(&id) {
1449 update_error!(
1450 error = TruncatedReadError::ArchiveEntryIDAlreadyClosed(id)
1451 );
1452 break 'read_block;
1453 }
1454 if let Some(hash_archive) = id_truncated2hash.remove(&id) {
1455 let computed_hash = hash_archive.finalize();
1456 if computed_hash.as_slice() != hash {
1457 update_error!(
1458 error = TruncatedReadError::HashDiffers {
1459 expected: Vec::from(computed_hash.as_slice()),
1460 obtained: Vec::from(&hash[..]),
1461 }
1462 );
1463 break 'read_block;
1464 }
1465 } else {
1466 update_error!(
1468 error = TruncatedReadError::TruncatedReadInternalError
1469 );
1470 break 'read_block;
1471 }
1472
1473 output.end_entry(id_output)?;
1474 id_truncated_done.push(id);
1475 }
1476 ArchiveEntryBlock::EndOfArchiveData => {
1477 update_error!(error = TruncatedReadError::EndOfOriginalArchiveData);
1479 break 'read_block;
1480 }
1481 }
1482 }
1483 }
1484 }
1485
1486 let mut unfinished_files = Vec::new();
1487
1488 for (id_truncated, id_output) in id_truncated2id_output {
1490 if id_truncated_done.contains(&id_truncated) {
1491 continue;
1493 }
1494
1495 let entry = id_truncated2entryname
1496 .get(&id_truncated)
1497 .expect("`id_truncated2entryname` not more sync with `id_truncated2id_output`");
1498 output.end_entry(id_output)?;
1499
1500 unfinished_files.push(entry.clone());
1501 }
1502
1503 if !unfinished_files.is_empty() {
1505 error = TruncatedReadError::UnfinishedEntries {
1506 names: unfinished_files,
1507 stopping_error: Box::new(error),
1508 };
1509 }
1510
1511 output.finalize()?;
1512 Ok(error)
1513 }
1514}
1515
1516pub mod info;
1518
1519#[cfg(test)]
1520pub(crate) mod tests {
1521 use crate::config::TruncatedReaderDecryptionMode;
1522 use crate::crypto::mlakey::{MLAPrivateKey, MLAPublicKey, generate_mla_keypair_from_seed};
1523
1524 use super::*;
1525 use crypto::hybrid::generate_keypair_from_seed;
1526 use rand::distributions::{Distribution, Standard};
1527 use rand::{RngCore, SeedableRng};
1528 use rand_chacha::ChaChaRng;
1529 #[cfg(feature = "send")]
1530 use static_assertions;
1531 #[cfg(feature = "send")]
1532 use std::fs::File;
1533 use std::io::{Cursor, Empty, Read};
1534
1535 #[test]
1536 fn read_dump_header() {
1537 let header = ArchiveHeader {
1538 format_version_number: MLA_FORMAT_VERSION,
1539 };
1540 let mut buf = Vec::new();
1541 header.serialize(&mut buf).unwrap();
1542 println!("{buf:?}");
1543
1544 let header_rebuild = ArchiveHeader::deserialize(&mut buf.as_slice()).unwrap();
1545 assert_eq!(header_rebuild.format_version_number, MLA_FORMAT_VERSION);
1546 }
1547
1548 #[test]
1549 fn dump_block() {
1550 let mut buf = Vec::new();
1551 let id = ArchiveEntryId(0);
1552 let hash = Sha256Hash::default();
1553
1554 ArchiveEntryBlock::EntryStart::<Empty> {
1556 id,
1557 name: EntryName::from_path("foobaré.exe").unwrap(),
1558 opts: Opts,
1559 }
1560 .dump(&mut buf)
1561 .unwrap();
1562
1563 let fake_content = vec![1, 2, 3, 4];
1564 let mut block = ArchiveEntryBlock::EntryContent {
1565 id,
1566 length: fake_content.len() as u64,
1567 data: Some(fake_content.as_slice()),
1568 opts: Opts,
1569 };
1570 block.dump(&mut buf).unwrap();
1571
1572 ArchiveEntryBlock::EndOfEntry::<Empty> {
1574 id,
1575 hash,
1576 opts: Opts,
1577 }
1578 .dump(&mut buf)
1579 .unwrap();
1580
1581 println!("{buf:?}");
1582 }
1583
1584 #[test]
1585 fn new_mla() {
1586 let file = Vec::new();
1587 let (private_key, public_key) = generate_keypair_from_seed([0; 32]);
1589 let config = ArchiveWriterConfig::with_encryption_without_signature(&[public_key]).unwrap();
1590 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
1591
1592 let fake_file = vec![1, 2, 3, 4];
1593 mla.add_entry(
1594 EntryName::from_path("my_file").unwrap(),
1595 fake_file.len() as u64,
1596 fake_file.as_slice(),
1597 )
1598 .unwrap();
1599 let fake_file = vec![5, 6, 7, 8];
1600 let fake_entry2 = vec![9, 10, 11, 12];
1601 let id = mla
1602 .start_entry(EntryName::from_path("my_entry2").unwrap())
1603 .unwrap();
1604 mla.append_entry_content(id, fake_file.len() as u64, fake_file.as_slice())
1605 .unwrap();
1606 mla.append_entry_content(id, fake_entry2.len() as u64, fake_entry2.as_slice())
1607 .unwrap();
1608 mla.end_entry(id).unwrap();
1609
1610 let dest = mla.finalize().unwrap();
1611 let buf = Cursor::new(dest.as_slice());
1612 let config =
1613 ArchiveReaderConfig::without_signature_verification().with_encryption(&[private_key]);
1614 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
1615
1616 let mut file = mla_read
1617 .get_entry(EntryName::from_path("my_file").unwrap())
1618 .unwrap()
1619 .unwrap();
1620 let mut rez = Vec::new();
1621 file.data.read_to_end(&mut rez).unwrap();
1622 assert_eq!(rez, vec![1, 2, 3, 4]);
1623 drop(file);
1626 let mut entry2 = mla_read
1627 .get_entry(EntryName::from_path("my_entry2").unwrap())
1628 .unwrap()
1629 .unwrap();
1630 let mut rez2 = Vec::new();
1631 entry2.data.read_to_end(&mut rez2).unwrap();
1632 assert_eq!(rez2, vec![5, 6, 7, 8, 9, 10, 11, 12]);
1633 }
1634
1635 #[test]
1636 fn new_encryption_only_mla() {
1637 let file = Vec::new();
1638 let (private_key, public_key) = generate_keypair_from_seed([0; 32]);
1640 let config = ArchiveWriterConfig::with_encryption_without_signature(&[public_key])
1641 .unwrap()
1642 .without_compression();
1643 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
1644
1645 let fake_file = vec![1, 2, 3, 4];
1646 mla.add_entry(
1647 EntryName::from_path("my_file").unwrap(),
1648 fake_file.len() as u64,
1649 fake_file.as_slice(),
1650 )
1651 .unwrap();
1652 let fake_file = vec![5, 6, 7, 8];
1653 let fake_entry2 = vec![9, 10, 11, 12];
1654 let id = mla
1655 .start_entry(EntryName::from_path("my_entry2").unwrap())
1656 .unwrap();
1657 mla.append_entry_content(id, fake_file.len() as u64, fake_file.as_slice())
1658 .unwrap();
1659 mla.append_entry_content(id, fake_entry2.len() as u64, fake_entry2.as_slice())
1660 .unwrap();
1661 mla.end_entry(id).unwrap();
1662
1663 let dest = mla.finalize().unwrap();
1664 let buf = Cursor::new(dest.as_slice());
1665 let config =
1666 ArchiveReaderConfig::without_signature_verification().with_encryption(&[private_key]);
1667 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
1668
1669 let mut file = mla_read
1670 .get_entry(EntryName::from_path("my_file").unwrap())
1671 .unwrap()
1672 .unwrap();
1673 let mut rez = Vec::new();
1674 file.data.read_to_end(&mut rez).unwrap();
1675 assert_eq!(rez, vec![1, 2, 3, 4]);
1676 drop(file);
1679 let mut entry2 = mla_read
1680 .get_entry(EntryName::from_path("my_entry2").unwrap())
1681 .unwrap()
1682 .unwrap();
1683 let mut rez2 = Vec::new();
1684 entry2.data.read_to_end(&mut rez2).unwrap();
1685 assert_eq!(rez2, vec![5, 6, 7, 8, 9, 10, 11, 12]);
1686 }
1687
1688 #[test]
1689 fn new_naked_mla() {
1690 let file = Vec::new();
1691 let config = ArchiveWriterConfig::without_encryption_without_signature()
1693 .unwrap()
1694 .without_compression();
1695 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
1696
1697 let fake_file = vec![1, 2, 3, 4];
1698 mla.add_entry(
1699 EntryName::from_path("my_file").unwrap(),
1700 fake_file.len() as u64,
1701 fake_file.as_slice(),
1702 )
1703 .unwrap();
1704 let fake_file = vec![5, 6, 7, 8];
1705 let fake_entry2 = vec![9, 10, 11, 12];
1706 let id = mla
1707 .start_entry(EntryName::from_path("my_entry2").unwrap())
1708 .unwrap();
1709 mla.append_entry_content(id, fake_file.len() as u64, fake_file.as_slice())
1710 .unwrap();
1711 mla.append_entry_content(id, fake_entry2.len() as u64, fake_entry2.as_slice())
1712 .unwrap();
1713 mla.end_entry(id).unwrap();
1714
1715 let dest = mla.finalize().unwrap();
1716 let buf = Cursor::new(dest.as_slice());
1717 let config = ArchiveReaderConfig::without_signature_verification().without_encryption();
1718 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
1719
1720 let mut file = mla_read
1721 .get_entry(EntryName::from_path("my_file").unwrap())
1722 .unwrap()
1723 .unwrap();
1724 let mut rez = Vec::new();
1725 file.data.read_to_end(&mut rez).unwrap();
1726 assert_eq!(rez, vec![1, 2, 3, 4]);
1727 drop(file);
1730 let mut entry2 = mla_read
1731 .get_entry(EntryName::from_path("my_entry2").unwrap())
1732 .unwrap()
1733 .unwrap();
1734 let mut rez2 = Vec::new();
1735 entry2.data.read_to_end(&mut rez2).unwrap();
1736 assert_eq!(rez2, vec![5, 6, 7, 8, 9, 10, 11, 12]);
1737 }
1738
1739 #[test]
1740 fn new_compression_only_mla() {
1741 let file = Vec::new();
1742 let config = ArchiveWriterConfig::without_encryption_without_signature().unwrap();
1744 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
1745
1746 let fake_file = vec![1, 2, 3, 4];
1747 mla.add_entry(
1748 EntryName::from_path("my_file").unwrap(),
1749 fake_file.len() as u64,
1750 fake_file.as_slice(),
1751 )
1752 .unwrap();
1753 let fake_file = vec![5, 6, 7, 8];
1754 let fake_entry2 = vec![9, 10, 11, 12];
1755 let id = mla
1756 .start_entry(EntryName::from_path("my_entry2").unwrap())
1757 .unwrap();
1758 mla.append_entry_content(id, fake_file.len() as u64, fake_file.as_slice())
1759 .unwrap();
1760 mla.append_entry_content(id, fake_entry2.len() as u64, fake_entry2.as_slice())
1761 .unwrap();
1762 mla.end_entry(id).unwrap();
1763
1764 let dest = mla.finalize().unwrap();
1765 let buf = Cursor::new(dest.as_slice());
1766 let config = ArchiveReaderConfig::without_signature_verification().without_encryption();
1767 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
1768
1769 let mut file = mla_read
1770 .get_entry(EntryName::from_path("my_file").unwrap())
1771 .unwrap()
1772 .unwrap();
1773 let mut rez = Vec::new();
1774 file.data.read_to_end(&mut rez).unwrap();
1775 assert_eq!(rez, vec![1, 2, 3, 4]);
1776 drop(file);
1779 let mut entry2 = mla_read
1780 .get_entry(EntryName::from_path("my_entry2").unwrap())
1781 .unwrap()
1782 .unwrap();
1783 let mut rez2 = Vec::new();
1784 entry2.data.read_to_end(&mut rez2).unwrap();
1785 assert_eq!(rez2, vec![5, 6, 7, 8, 9, 10, 11, 12]);
1786 }
1787
1788 #[test]
1789 fn new_sig_mla() {
1790 let file = Vec::new();
1791 let (private_key, public_key) = generate_mla_keypair_from_seed([0; 32]);
1793 let config = ArchiveWriterConfig::without_encryption_with_signature(&[private_key
1794 .get_signing_private_key()
1795 .clone()])
1796 .unwrap();
1797 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
1798
1799 let fake_file = vec![1, 2, 3, 4];
1800 mla.add_entry(
1801 EntryName::from_path("my_file").unwrap(),
1802 fake_file.len() as u64,
1803 fake_file.as_slice(),
1804 )
1805 .unwrap();
1806 let fake_file = vec![5, 6, 7, 8];
1807 let fake_entry2 = vec![9, 10, 11, 12];
1808 let id = mla
1809 .start_entry(EntryName::from_path("my_entry2").unwrap())
1810 .unwrap();
1811 mla.append_entry_content(id, fake_file.len() as u64, fake_file.as_slice())
1812 .unwrap();
1813 mla.append_entry_content(id, fake_entry2.len() as u64, fake_entry2.as_slice())
1814 .unwrap();
1815 mla.end_entry(id).unwrap();
1816
1817 let dest = mla.finalize().unwrap();
1818 let buf = Cursor::new(dest.as_slice());
1819 let config = ArchiveReaderConfig::with_signature_verification(&[public_key
1820 .get_signature_verification_public_key()
1821 .clone()])
1822 .without_encryption();
1823 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
1824
1825 let mut file = mla_read
1826 .get_entry(EntryName::from_path("my_file").unwrap())
1827 .unwrap()
1828 .unwrap();
1829 let mut rez = Vec::new();
1830 file.data.read_to_end(&mut rez).unwrap();
1831 assert_eq!(rez, vec![1, 2, 3, 4]);
1832 drop(file);
1835 let mut entry2 = mla_read
1836 .get_entry(EntryName::from_path("my_entry2").unwrap())
1837 .unwrap()
1838 .unwrap();
1839 let mut rez2 = Vec::new();
1840 entry2.data.read_to_end(&mut rez2).unwrap();
1841 assert_eq!(rez2, vec![5, 6, 7, 8, 9, 10, 11, 12]);
1842 }
1843
1844 #[allow(clippy::type_complexity)]
1845 pub(crate) fn build_archive(
1846 compression: bool,
1847 encryption: bool,
1848 signature: bool,
1849 interleaved: bool,
1850 ) -> (
1851 Vec<u8>,
1852 (MLAPrivateKey, MLAPublicKey),
1853 (MLAPrivateKey, MLAPublicKey),
1854 Vec<(EntryName, Vec<u8>)>,
1855 ) {
1856 let (written_archive, sender_keys, receiver_keys, entries_content, _, _) =
1857 build_archive2(compression, encryption, signature, interleaved);
1858 (written_archive, sender_keys, receiver_keys, entries_content)
1859 }
1860
1861 #[allow(clippy::type_complexity)]
1862 pub(crate) fn build_archive2(
1863 compression: bool,
1864 encryption: bool,
1865 signature: bool,
1866 interleaved: bool,
1867 ) -> (
1868 Vec<u8>,
1869 (MLAPrivateKey, MLAPublicKey),
1870 (MLAPrivateKey, MLAPublicKey),
1871 Vec<(EntryName, Vec<u8>)>,
1872 HashMap<EntryName, ArchiveEntryId>,
1873 HashMap<ArchiveEntryId, EntryInfo>,
1874 ) {
1875 let file = Vec::new();
1877 let (sender_private_key, sender_public_key) = generate_mla_keypair_from_seed([0; 32]);
1879 let (receiver_private_key, receiver_public_key) = generate_mla_keypair_from_seed([1; 32]);
1880 let config = if encryption {
1881 if signature {
1882 ArchiveWriterConfig::with_encryption_with_signature(
1883 &[receiver_public_key.get_encryption_public_key().clone()],
1884 &[sender_private_key.get_signing_private_key().clone()],
1885 )
1886 } else {
1887 ArchiveWriterConfig::with_encryption_without_signature(&[receiver_public_key
1888 .get_encryption_public_key()
1889 .clone()])
1890 }
1891 } else if signature {
1892 ArchiveWriterConfig::without_encryption_with_signature(&[sender_private_key
1893 .get_signing_private_key()
1894 .clone()])
1895 } else {
1896 ArchiveWriterConfig::without_encryption_without_signature()
1897 }
1898 .unwrap();
1899 let config = if compression {
1900 config
1901 } else {
1902 config.without_compression()
1903 };
1904 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
1905
1906 let entry1 = EntryName::from_arbitrary_bytes(b"my_entry1").unwrap();
1907 let entry2 = EntryName::from_arbitrary_bytes(b"my_entry2").unwrap();
1908 let entry3 = EntryName::from_arbitrary_bytes(b"my_entry3").unwrap();
1909 let fake_file_part1 = vec![1, 2, 3];
1910 let fake_file_part2 = vec![4, 5, 6, 7, 8];
1911 let mut fake_entry1 = Vec::new();
1912 fake_entry1.extend_from_slice(fake_file_part1.as_slice());
1913 fake_entry1.extend_from_slice(fake_file_part2.as_slice());
1914 let fake_entry2 = vec![9, 10, 11, 12];
1915 let fake_entry3 = vec![13, 14, 15];
1916
1917 if interleaved {
1918 let id_entry1 = mla.start_entry(entry1.clone()).unwrap();
1930 mla.append_entry_content(
1931 id_entry1,
1932 fake_file_part1.len() as u64,
1933 fake_file_part1.as_slice(),
1934 )
1935 .unwrap();
1936 let id_entry2 = mla.start_entry(entry2.clone()).unwrap();
1937 mla.append_entry_content(id_entry2, fake_entry2.len() as u64, fake_entry2.as_slice())
1938 .unwrap();
1939 mla.add_entry(
1940 entry3.clone(),
1941 fake_entry3.len() as u64,
1942 fake_entry3.as_slice(),
1943 )
1944 .unwrap();
1945 mla.append_entry_content(
1946 id_entry1,
1947 fake_file_part2.len() as u64,
1948 fake_file_part2.as_slice(),
1949 )
1950 .unwrap();
1951 mla.end_entry(id_entry1).unwrap();
1952 mla.end_entry(id_entry2).unwrap();
1953 } else {
1954 mla.add_entry(
1955 entry1.clone(),
1956 fake_entry1.len() as u64,
1957 fake_entry1.as_slice(),
1958 )
1959 .unwrap();
1960 mla.add_entry(
1961 entry2.clone(),
1962 fake_entry2.len() as u64,
1963 fake_entry2.as_slice(),
1964 )
1965 .unwrap();
1966 mla.add_entry(
1967 entry3.clone(),
1968 fake_entry3.len() as u64,
1969 fake_entry3.as_slice(),
1970 )
1971 .unwrap();
1972 }
1973 let entries_info = mla.entries_info.clone();
1974 let ids_info = mla.ids_info.clone();
1975 let written_archive = mla.finalize().unwrap();
1976
1977 (
1978 written_archive,
1979 (sender_private_key, sender_public_key),
1980 (receiver_private_key, receiver_public_key),
1981 vec![
1982 (entry1, fake_entry1),
1983 (entry2, fake_entry2),
1984 (entry3, fake_entry3),
1985 ],
1986 entries_info,
1987 ids_info,
1988 )
1989 }
1990
1991 #[test]
1992 fn interleaved_files() {
1993 let (mla, _sender_key, receiver_key, files) = build_archive(true, true, false, true);
1995
1996 let buf = Cursor::new(mla.as_slice());
1997 let config = ArchiveReaderConfig::without_signature_verification()
1998 .with_encryption(&[receiver_key.0.get_decryption_private_key().clone()]);
1999 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
2000
2001 for (entry, content) in files {
2002 let mut file = mla_read.get_entry(entry).unwrap().unwrap();
2003 let mut rez = Vec::new();
2004 file.data.read_to_end(&mut rez).unwrap();
2005 assert_eq!(rez, content);
2006 }
2007 }
2008
2009 #[test]
2010 fn mla_all_layer_combinations() {
2011 let (privkey, pubkey) = generate_mla_keypair_from_seed([42; 32]);
2013
2014 let combinations = [
2016 (false, false, false), (true, false, false), (false, true, false), (false, false, true), (true, true, false), (true, false, true), (false, true, true), (true, true, true), ];
2025
2026 for (compress, encrypt, sign) in combinations {
2027 let mut config = match (encrypt, sign) {
2028 (true, true) => ArchiveWriterConfig::with_encryption_with_signature(
2029 &[pubkey.get_encryption_public_key().clone()],
2030 &[privkey.get_signing_private_key().clone()],
2031 )
2032 .unwrap(),
2033 (true, false) => ArchiveWriterConfig::with_encryption_without_signature(&[pubkey
2034 .get_encryption_public_key()
2035 .clone()])
2036 .unwrap(),
2037 (false, true) => ArchiveWriterConfig::without_encryption_with_signature(&[privkey
2038 .get_signing_private_key()
2039 .clone()])
2040 .unwrap(),
2041 (false, false) => {
2042 ArchiveWriterConfig::without_encryption_without_signature().unwrap()
2043 }
2044 };
2045 if !compress {
2046 config = config.without_compression();
2047 }
2048
2049 let file = Vec::new();
2051 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
2052 let data = vec![1, 2, 3, 4, 5];
2053 mla.add_entry(
2054 EntryName::from_path("testfile").unwrap(),
2055 data.len() as u64,
2056 data.as_slice(),
2057 )
2058 .unwrap();
2059 let archive = mla.finalize().unwrap();
2060
2061 let buf = Cursor::new(archive);
2063 let reader_config = match (encrypt, sign) {
2064 (true, true) => ArchiveReaderConfig::with_signature_verification(&[pubkey
2065 .get_signature_verification_public_key()
2066 .clone()])
2067 .with_encryption(&[privkey.get_decryption_private_key().clone()]),
2068 (true, false) => ArchiveReaderConfig::without_signature_verification()
2069 .with_encryption(&[privkey.get_decryption_private_key().clone()]),
2070 (false, true) => ArchiveReaderConfig::with_signature_verification(&[pubkey
2071 .get_signature_verification_public_key()
2072 .clone()])
2073 .without_encryption(),
2074 (false, false) => {
2075 ArchiveReaderConfig::without_signature_verification().without_encryption()
2076 }
2077 };
2078 let mut mla_read = ArchiveReader::from_config(buf, reader_config).unwrap().0;
2079 let mut file = mla_read
2080 .get_entry(EntryName::from_path("testfile").unwrap())
2081 .unwrap()
2082 .unwrap();
2083 let mut out = Vec::new();
2084 file.data.read_to_end(&mut out).unwrap();
2085 assert_eq!(
2086 out,
2087 vec![1, 2, 3, 4, 5],
2088 "Failed for combination compress={compress}, encrypt={encrypt}, sign={sign}"
2089 );
2090 }
2091 }
2092
2093 #[test]
2094 fn list_and_read_files() {
2095 let (mla, _sender_key, receiver_key, files) = build_archive(true, true, false, false);
2097
2098 let buf = Cursor::new(mla.as_slice());
2099 let config = ArchiveReaderConfig::without_signature_verification()
2100 .with_encryption(&[receiver_key.0.get_decryption_private_key().clone()]);
2101 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
2102
2103 let mut sorted_list: Vec<EntryName> = mla_read.list_entries().unwrap().cloned().collect();
2105 sorted_list.sort();
2106 assert_eq!(
2107 sorted_list,
2108 files.iter().map(|(x, _y)| x.clone()).collect::<Vec<_>>(),
2109 );
2110
2111 for (entry, content) in files.iter().rev() {
2113 let mut mla_file = mla_read.get_entry(entry.clone()).unwrap().unwrap();
2114 assert_eq!(mla_file.name, entry.clone());
2115 let mut buf = Vec::new();
2116 mla_file.data.read_to_end(&mut buf).unwrap();
2117 assert_eq!(&buf, content);
2118 }
2119 }
2120
2121 #[test]
2122 fn convert_truncated() {
2123 let (dest, _sender_key, receiver_key, files) = build_archive(true, true, false, false);
2125
2126 let config = TruncatedReaderConfig::without_signature_verification_with_encryption(
2128 &[receiver_key.0.get_decryption_private_key().clone()],
2129 TruncatedReaderDecryptionMode::OnlyAuthenticatedData,
2130 );
2131 let mut mla_fsread = TruncatedArchiveReader::from_config(dest.as_slice(), config).unwrap();
2132
2133 let mut dest_w = Vec::new();
2135 let config = ArchiveWriterConfig::with_encryption_without_signature(&[receiver_key
2136 .1
2137 .get_encryption_public_key()
2138 .clone()])
2139 .unwrap();
2140 let mla_w = ArchiveWriter::from_config(&mut dest_w, config).expect("Writer init failed");
2141
2142 match mla_fsread.convert_to_archive(mla_w).unwrap() {
2144 TruncatedReadError::EndOfOriginalArchiveData => {
2145 }
2148 status => {
2149 panic!("Unexpected status: {status}");
2150 }
2151 }
2152
2153 let buf2 = Cursor::new(dest_w.as_slice());
2155 let config = ArchiveReaderConfig::without_signature_verification()
2156 .with_encryption(&[receiver_key.0.get_decryption_private_key().clone()]);
2157 let mut mla_read = ArchiveReader::from_config(buf2, config).unwrap().0;
2158
2159 let mut sorted_list: Vec<EntryName> = mla_read.list_entries().unwrap().cloned().collect();
2161 sorted_list.sort();
2162 assert_eq!(
2163 sorted_list,
2164 files.iter().map(|(x, _y)| x.clone()).collect::<Vec<_>>(),
2165 );
2166
2167 for (entry, content) in files.iter().rev() {
2169 let mut mla_file = mla_read.get_entry(entry.clone()).unwrap().unwrap();
2170 assert_eq!(mla_file.name, entry.clone());
2171 let mut buf = Vec::new();
2172 mla_file.data.read_to_end(&mut buf).unwrap();
2173 assert_eq!(&buf, content);
2174 }
2175 }
2176
2177 #[test]
2178 fn convert_trunc_truncated() {
2179 for interleaved in &[false, true] {
2180 let (dest, _sender_key, receiver_key, files, entries_info, ids_info) =
2182 build_archive2(false, true, false, *interleaved);
2183 let footer_size = {
2185 let mut cursor = Cursor::new(Vec::new());
2186 ArchiveFooter::serialize_into(&mut cursor, &entries_info, &ids_info).unwrap();
2187 usize::try_from(cursor.position())
2188 .expect("Failed to convert cursor position to usize")
2189 };
2190
2191 for remove in &[1, 10, 30, 50, 70, 95, 100] {
2192 let config = TruncatedReaderConfig::without_signature_verification_with_encryption(
2193 &[receiver_key.0.get_decryption_private_key().clone()],
2194 TruncatedReaderDecryptionMode::DataEvenUnauthenticated,
2195 );
2196 let mut mla_fsread = TruncatedArchiveReader::from_config(
2197 &dest[..dest.len() - footer_size - remove],
2198 config,
2199 )
2200 .expect("Unable to create");
2201
2202 let mut dest_w = Vec::new();
2204 let config_w =
2205 ArchiveWriterConfig::with_encryption_without_signature(&[receiver_key
2206 .1
2207 .get_encryption_public_key()
2208 .clone()])
2209 .expect("Writer init failed");
2210 let mla_w = ArchiveWriter::from_config(&mut dest_w, config_w).unwrap();
2211
2212 let _status = mla_fsread.convert_to_archive(mla_w).unwrap();
2214
2215 let buf2 = Cursor::new(dest_w.as_slice());
2217 let config = ArchiveReaderConfig::without_signature_verification()
2218 .with_encryption(&[receiver_key.0.get_decryption_private_key().clone()]);
2219 let mut mla_read = ArchiveReader::from_config(buf2, config).unwrap().0;
2220
2221 let expected = files.iter().map(|(x, _y)| x.clone()).collect::<Vec<_>>();
2223 let mut file_list = mla_read
2224 .list_entries()
2225 .unwrap()
2226 .cloned()
2227 .collect::<Vec<_>>();
2228 file_list.sort();
2229 assert_eq!(
2230 file_list[..],
2231 expected[..file_list.len()],
2232 "File lists not equal {} interleaving and {} bytes removed",
2233 if *interleaved { "with" } else { "without" },
2234 remove
2235 );
2236
2237 for (entry, content) in files.iter().rev() {
2239 let Some(mut mla_file) = mla_read.get_entry(entry.clone()).unwrap() else {
2241 continue;
2242 };
2243 assert_eq!(mla_file.name, entry.clone());
2246 let mut buf = Vec::new();
2247 mla_file.data.read_to_end(&mut buf).unwrap();
2248 assert_ne!(
2249 buf.len(),
2250 0,
2251 "Read 0 bytes from entry {} {} interleaving and {} bytes removed",
2252 mla_file.name.raw_content_to_escaped_string(),
2253 if *interleaved { "with" } else { "without" },
2254 remove
2255 );
2256 assert_eq!(&buf[..], &content[..buf.len()]);
2257 }
2258 }
2259 }
2261 }
2262
2263 #[test]
2264 fn avoid_duplicate_entryname() {
2265 let buf = Vec::new();
2266 let config = ArchiveWriterConfig::without_encryption_without_signature()
2267 .unwrap()
2268 .without_compression();
2269 let mut mla = ArchiveWriter::from_config(buf, config).unwrap();
2270 mla.add_entry(
2271 EntryName::from_path("Test").unwrap(),
2272 4,
2273 vec![1, 2, 3, 4].as_slice(),
2274 )
2275 .unwrap();
2276 assert!(
2277 mla.add_entry(
2278 EntryName::from_path("Test").unwrap(),
2279 4,
2280 vec![1, 2, 3, 4].as_slice()
2281 )
2282 .is_err()
2283 );
2284 assert!(
2285 mla.start_entry(EntryName::from_path("Test").unwrap())
2286 .is_err()
2287 );
2288 }
2289
2290 #[test]
2291 fn check_file_size() {
2292 for interleaved in &[false, true] {
2295 let (dest, _sender_key, receiver_key, files, entries_info, ids_info) =
2296 build_archive2(true, true, false, *interleaved);
2297
2298 for (entry, data) in &files {
2299 let id = entries_info.get(entry).unwrap();
2300 let size: u64 = ids_info
2301 .get(id)
2302 .unwrap()
2303 .offsets_and_sizes
2304 .iter()
2305 .map(|p| p.1)
2306 .sum();
2307 assert_eq!(size, data.len() as u64);
2308 }
2309
2310 let buf = Cursor::new(dest.as_slice());
2311 let config = ArchiveReaderConfig::without_signature_verification()
2312 .with_encryption(&[receiver_key.0.get_decryption_private_key().clone()]);
2313 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
2314
2315 for (entry, data) in &files {
2316 let mla_file = mla_read.get_entry(entry.clone()).unwrap().unwrap();
2317 assert_eq!(mla_file.get_size(), data.len() as u64);
2318 }
2319 }
2320 }
2321
2322 #[test]
2323 fn truncated_detect_integrity() {
2324 let (mut dest, _sender_key, _receiver_key, files) =
2326 build_archive(false, false, false, false);
2327
2328 let expect = files[0].1.as_slice();
2330 let pos: Vec<usize> = dest
2331 .iter()
2332 .enumerate()
2333 .filter_map(|e| {
2334 if e.0 + expect.len() < dest.len() && &dest[e.0..e.0 + expect.len()] == expect {
2335 Some(e.0)
2336 } else {
2337 None
2338 }
2339 })
2340 .collect();
2341 dest.swap(pos[0], pos[0] + 1);
2342
2343 let mut mla_fsread = TruncatedArchiveReader::from_config(
2345 dest.as_slice(),
2346 TruncatedReaderConfig::without_signature_verification_without_encryption(),
2347 )
2348 .unwrap();
2349
2350 let dest_w = Vec::new();
2352 let config = ArchiveWriterConfig::without_encryption_without_signature()
2353 .unwrap()
2354 .without_compression();
2355 let mla_w = ArchiveWriter::from_config(dest_w, config).expect("Writer init failed");
2356
2357 match mla_fsread.convert_to_archive(mla_w).unwrap() {
2359 TruncatedReadError::UnfinishedEntries {
2360 names,
2361 stopping_error,
2362 } => {
2363 assert_eq!(names, vec![files[0].0.clone()]);
2365 match *stopping_error {
2366 TruncatedReadError::HashDiffers { .. } => {}
2367 _ => {
2368 panic!("Unexpected stopping_error: {stopping_error}");
2369 }
2370 }
2371 }
2372 status => {
2373 panic!("Unexpected status: {status}");
2374 }
2375 }
2376 }
2377
2378 #[test]
2379 fn get_hash() {
2380 let (dest, _sender_key, receiver_key, files) = build_archive(true, true, false, false);
2382
2383 let buf = Cursor::new(dest.as_slice());
2385 let config = ArchiveReaderConfig::without_signature_verification()
2386 .with_encryption(&[receiver_key.0.get_decryption_private_key().clone()]);
2387 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
2388
2389 for (name, content) in files {
2391 let hash = mla_read.get_hash(&name).unwrap().unwrap();
2392
2393 let mut hasher = Sha256::new();
2394 hasher.update(content);
2395 let result = hasher.finalize();
2396 assert_eq!(result.as_slice(), hash);
2397 }
2398 }
2399
2400 fn make_format_regression_files() -> HashMap<EntryName, Vec<u8>> {
2401 let mut files: HashMap<EntryName, Vec<u8>> = HashMap::new();
2403
2404 let mut simple: Vec<u8> = Vec::new();
2406 for i in 0..=255 {
2407 simple.push(i);
2408 }
2409 let pattern = simple.clone();
2410 files.insert(EntryName::from_path("simple").unwrap(), simple);
2411
2412 let big: Vec<u8> = pattern
2414 .iter()
2415 .cycle()
2416 .take(10 * 1024 * 1024)
2417 .copied()
2418 .collect();
2419 files.insert(EntryName::from_path("big").unwrap(), big);
2420
2421 for i in 0..=255 {
2423 files.insert(
2424 EntryName::from_path(format!("file_{i}")).unwrap(),
2425 std::iter::repeat_n(i, 0x1000).collect::<Vec<u8>>(),
2426 );
2427 }
2428
2429 let mut sha256sum: Vec<u8> = Vec::new();
2431 let mut info: Vec<(&EntryName, &Vec<_>)> = files.iter().collect();
2432 info.sort_by(|i1, i2| Ord::cmp(&i1.0, &i2.0));
2433 for (entry, content) in &info {
2434 let h = Sha256::digest(content);
2435 sha256sum.extend_from_slice(hex::encode(h).as_bytes());
2436 sha256sum.push(0x20);
2437 sha256sum.push(0x20);
2438 sha256sum.extend(entry.as_arbitrary_bytes());
2439 sha256sum.push(0x0a);
2440 }
2441 files.insert(EntryName::from_path("sha256sum").unwrap(), sha256sum);
2442 files
2443 }
2444
2445 #[test]
2446 fn create_archive_format_version() {
2447 let file = Vec::new();
2449
2450 let priv_bytes: &'static [u8] =
2452 include_bytes!("../../samples/test_mlakey_archive_v2_sender.mlapriv");
2453 let pub_bytes: &'static [u8] =
2454 include_bytes!("../../samples/test_mlakey_archive_v2_receiver.mlapub");
2455 let (_, priv_key) = MLAPrivateKey::deserialize_private_key(priv_bytes)
2456 .unwrap()
2457 .get_private_keys();
2458 let (pub_key, _) = MLAPublicKey::deserialize_public_key(pub_bytes)
2459 .unwrap()
2460 .get_public_keys();
2461
2462 let mut config =
2463 ArchiveWriterConfig::with_encryption_with_signature(&[pub_key], &[priv_key]).unwrap();
2464 if let Some(cfg) = config.encryption.as_mut() {
2465 cfg.rng = crate::crypto::MaybeSeededRNG::Seed([0; 32]);
2466 }
2467 if let Some(cfg) = config.signature.as_mut() {
2468 cfg.rng = crate::crypto::MaybeSeededRNG::Seed([0; 32]);
2469 }
2470 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
2471
2472 let files = make_format_regression_files();
2473 let entry_simple = EntryName::from_path("simple").unwrap();
2475 mla.add_entry(
2476 entry_simple.clone(),
2477 files.get(&entry_simple).unwrap().len() as u64,
2478 files.get(&entry_simple).unwrap().as_slice(),
2479 )
2480 .unwrap();
2481
2482 let entries: Vec<EntryName> = (0..=255)
2484 .map(|i| format!("file_{i}"))
2485 .map(|s| EntryName::from_path(&s).unwrap())
2486 .collect();
2487 let mut name2id: HashMap<_, _> = HashMap::new();
2488
2489 (0..=255)
2491 .map(|i| {
2492 let id = mla.start_entry(entries[i].clone()).unwrap();
2493 name2id.insert(&entries[i], id);
2494 })
2495 .for_each(drop);
2496
2497 (0..=255)
2499 .rev()
2500 .map(|i| {
2501 let id = name2id.get(&entries[i]).unwrap();
2502 mla.append_entry_content(*id, 32, &files.get(&entries[i]).unwrap()[..32])
2503 .unwrap();
2504 })
2505 .for_each(drop);
2506
2507 (0..=255)
2509 .map(|i| {
2510 let id = name2id.get(&entries[i]).unwrap();
2511 let data = &files.get(&entries[i]).unwrap()[32..];
2512 mla.append_entry_content(*id, data.len() as u64, data)
2513 .unwrap();
2514 })
2515 .for_each(drop);
2516
2517 (0..=255)
2519 .rev()
2520 .map(|i| {
2521 let id = name2id.get(&entries[i]).unwrap();
2522 mla.end_entry(*id).unwrap();
2523 })
2524 .for_each(drop);
2525
2526 let entry_big = EntryName::from_path("big").unwrap();
2528 mla.add_entry(
2529 entry_big.clone(),
2530 files.get(&entry_big).unwrap().len() as u64,
2531 files.get(&entry_big).unwrap().as_slice(),
2532 )
2533 .unwrap();
2534
2535 let entry_sha256sum = EntryName::from_path("sha256sum").unwrap();
2537 mla.add_entry(
2538 entry_sha256sum.clone(),
2539 files.get(&entry_sha256sum).unwrap().len() as u64,
2540 files.get(&entry_sha256sum).unwrap().as_slice(),
2541 )
2542 .unwrap();
2543 let raw_mla = mla.finalize().unwrap();
2544
2545 std::fs::File::create(std::path::Path::new(&format!(
2546 "../samples/archive_v{MLA_FORMAT_VERSION}.mla"
2547 )))
2548 .unwrap()
2549 .write_all(&raw_mla)
2550 .unwrap();
2551
2552 assert_eq!(
2554 Sha256::digest(&raw_mla).as_slice(),
2555 [
2556 252, 199, 175, 3, 17, 155, 237, 195, 81, 200, 149, 68, 209, 57, 5, 58, 245, 242,
2557 100, 13, 170, 168, 241, 27, 169, 112, 81, 182, 99, 141, 93, 248
2558 ]
2559 );
2560 }
2561
2562 #[test]
2563 fn check_archive_format_v2_content() {
2564 let privbytes: &'static [u8] =
2565 include_bytes!("../../samples/test_mlakey_archive_v2_receiver.mlapriv");
2566 let pubbytes: &'static [u8] =
2567 include_bytes!("../../samples/test_mlakey_archive_v2_sender.mlapub");
2568
2569 let mla_data: &'static [u8] = include_bytes!("../../samples/archive_v2.mla");
2570 let files = make_format_regression_files();
2571
2572 let buf = Cursor::new(mla_data);
2574 let (privkey, _) = MLAPrivateKey::deserialize_private_key(privbytes)
2575 .unwrap()
2576 .get_private_keys();
2577 let (_, pubkey) = MLAPublicKey::deserialize_public_key(pubbytes)
2578 .unwrap()
2579 .get_public_keys();
2580 let config = ArchiveReaderConfig::with_signature_verification(&[pubkey])
2581 .with_encryption(std::slice::from_ref(&privkey));
2582 let mut mla_read = ArchiveReader::from_config(buf, config).unwrap().0;
2583
2584 let config = TruncatedReaderConfig::without_signature_verification_with_encryption(
2586 &[privkey],
2587 TruncatedReaderDecryptionMode::OnlyAuthenticatedData,
2588 );
2589 let mut mla_fsread = TruncatedArchiveReader::from_config(mla_data, config).unwrap();
2590
2591 let mut dest_w = Vec::new();
2593 let config = ArchiveWriterConfig::without_encryption_without_signature()
2594 .unwrap()
2595 .without_compression();
2596 let mla_w = ArchiveWriter::from_config(&mut dest_w, config).expect("Writer init failed");
2597 if let TruncatedReadError::EndOfOriginalArchiveData =
2598 mla_fsread.convert_to_archive(mla_w).unwrap()
2599 {
2600 } else {
2602 panic!();
2603 }
2604 let buf2 = Cursor::new(dest_w);
2606 let repread_config =
2607 ArchiveReaderConfig::without_signature_verification().without_encryption();
2608 let mut mla_repread = ArchiveReader::from_config(buf2, repread_config).unwrap().0;
2609
2610 assert_eq!(files.len(), mla_read.list_entries().unwrap().count());
2611 assert_eq!(files.len(), mla_repread.list_entries().unwrap().count());
2612
2613 for (entry, content) in &files {
2615 let mut mla_file = mla_read.get_entry(entry.clone()).unwrap().unwrap();
2616 let mut mla_rep_file = mla_repread.get_entry(entry.clone()).unwrap().unwrap();
2617 assert_eq!(mla_file.name, entry.clone());
2618 assert_eq!(mla_rep_file.name, entry.clone());
2619 let mut buf = Vec::new();
2620 mla_file.data.read_to_end(&mut buf).unwrap();
2621 assert_eq!(buf.as_slice(), content.as_slice());
2622 let mut buf = Vec::new();
2623 mla_rep_file.data.read_to_end(&mut buf).unwrap();
2624 assert_eq!(buf.as_slice(), content.as_slice());
2625 }
2626 }
2627
2628 #[test]
2629 fn not_path_entry_name() {
2630 let mut file: Vec<u8> = Vec::new();
2631 let priv_bytes: &'static [u8] =
2632 include_bytes!("../../samples/test_mlakey_archive_v2_sender.mlapriv");
2633 let pub_bytes: &'static [u8] =
2634 include_bytes!("../../samples/test_mlakey_archive_v2_receiver.mlapub");
2635 let (_, priv_key) = MLAPrivateKey::deserialize_private_key(priv_bytes)
2636 .unwrap()
2637 .get_private_keys();
2638 let (pub_key, _) = MLAPublicKey::deserialize_public_key(pub_bytes)
2639 .unwrap()
2640 .get_public_keys();
2641
2642 let mut config =
2643 ArchiveWriterConfig::with_encryption_with_signature(&[pub_key], &[priv_key])
2644 .unwrap()
2645 .without_compression();
2646 if let Some(cfg) = config.encryption.as_mut() {
2647 cfg.rng = crate::crypto::MaybeSeededRNG::Seed([0; 32]);
2648 }
2649 if let Some(cfg) = config.signature.as_mut() {
2650 cfg.rng = crate::crypto::MaybeSeededRNG::Seed([0; 32]);
2651 }
2652 let mut mla = ArchiveWriter::from_config(&mut file, config).expect("Writer init failed");
2653
2654 let name = EntryName::from_arbitrary_bytes(
2655 b"c:/\0;\xe2\x80\xae\nc\rd\x1b[1;31ma<script>evil\\../\xd8\x01\xc2\x85\xe2\x88\x95",
2656 )
2657 .unwrap();
2658
2659 mla.add_entry(name.clone(), 8, b"' OR 1=1".as_slice())
2660 .expect("start_file");
2661 mla.finalize().unwrap();
2662
2663 std::fs::File::create(std::path::Path::new("../samples/archive_weird.mla"))
2664 .unwrap()
2665 .write_all(&file)
2666 .unwrap();
2667
2668 assert_eq!(
2669 Sha256::digest(&file).as_slice(),
2670 [
2671 95, 79, 59, 224, 200, 239, 240, 229, 137, 227, 3, 80, 95, 185, 106, 204, 219, 224,
2672 5, 102, 120, 246, 158, 151, 64, 151, 97, 52, 69, 134, 26, 25
2673 ]
2674 );
2675 }
2676
2677 #[test]
2678 fn empty_blocks() {
2679 let file = Vec::new();
2681 let config = ArchiveWriterConfig::without_encryption_without_signature()
2682 .unwrap()
2683 .without_compression();
2684 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
2685
2686 let entry = EntryName::from_path("my_file").unwrap();
2687 let fake_file = vec![1, 2, 3, 4, 5, 6, 7, 8];
2688
2689 let id = mla.start_entry(entry.clone()).expect("start_file");
2690 mla.append_entry_content(id, 4, &fake_file[..4])
2691 .expect("add content");
2692 mla.append_entry_content(id, 0, &fake_file[..1])
2693 .expect("add content empty");
2694 mla.append_entry_content(id, fake_file.len() as u64 - 4, &fake_file[4..])
2695 .expect("add rest");
2696 mla.end_entry(id).unwrap();
2697
2698 let mla_data = mla.finalize().unwrap();
2699
2700 let buf = Cursor::new(mla_data);
2701 let mut mla_read = ArchiveReader::from_config(
2702 buf,
2703 ArchiveReaderConfig::without_signature_verification().without_encryption(),
2704 )
2705 .expect("archive reader")
2706 .0;
2707 let mut out = Vec::new();
2708 mla_read
2709 .get_entry(entry)
2710 .unwrap()
2711 .unwrap()
2712 .data
2713 .read_to_end(&mut out)
2714 .unwrap();
2715 assert_eq!(out.as_slice(), fake_file.as_slice());
2716 }
2717
2718 #[test]
2719 #[ignore = "As it is costly, only enabled by default in the CI."]
2720 fn more_than_u32_entry() {
2721 const MORE_THAN_U32: u64 = 0x0001_0001_0000; const MAX_SIZE: u64 = 5 * 1024 * 1024 * 1024; const CHUNK_SIZE: usize = 10 * 1024 * 1024; let mut rng = ChaChaRng::seed_from_u64(0);
2727 let mut rng_data = ChaChaRng::seed_from_u64(0);
2728
2729 let (private_key, public_key) = generate_keypair_from_seed([0; 32]);
2730 let config = ArchiveWriterConfig::with_encryption_without_signature(&[public_key]).unwrap();
2731 let file = Vec::new();
2732 let mut mla = ArchiveWriter::from_config(file, config).expect("Writer init failed");
2733
2734 let id1 = mla
2736 .start_entry(EntryName::from_path("file_0").unwrap())
2737 .unwrap();
2738 let mut cur_size = 0;
2739 while cur_size < MORE_THAN_U32 {
2740 let size = std::cmp::min(u64::from(rng.next_u32()), MORE_THAN_U32 - cur_size);
2741 let data: Vec<u8> = Standard
2742 .sample_iter(&mut rng_data)
2743 .take(usize::try_from(size).expect("Failed to convert size to usize"))
2744 .collect();
2745 mla.append_entry_content(id1, size, data.as_slice())
2746 .unwrap();
2747 cur_size += size;
2748 }
2749 mla.end_entry(id1).unwrap();
2750
2751 let mut nb_file = 1;
2752
2753 while cur_size < MAX_SIZE {
2755 let id = mla
2756 .start_entry(EntryName::from_path(format!("file_{nb_file:}")).unwrap())
2757 .unwrap();
2758 let size = std::cmp::min(u64::from(rng.next_u32()), MAX_SIZE - cur_size);
2759 let data: Vec<u8> = Standard
2760 .sample_iter(&mut rng_data)
2761 .take(usize::try_from(size).expect("Failed to convert size to usize"))
2762 .collect();
2763 mla.append_entry_content(id, size, data.as_slice()).unwrap();
2764 cur_size += size;
2765 mla.end_entry(id).unwrap();
2766 nb_file += 1;
2767 }
2768 let mla_data = mla.finalize().unwrap();
2769
2770 let buf = Cursor::new(mla_data);
2773 let config =
2774 ArchiveReaderConfig::without_signature_verification().with_encryption(&[private_key]);
2775 let mut mla_read = ArchiveReader::from_config(buf, config)
2776 .expect("archive reader")
2777 .0;
2778
2779 let file_names: Vec<EntryName> = (0..nb_file)
2780 .map(|nb| EntryName::from_path(format!("file_{nb:}")).unwrap())
2781 .collect();
2782 let mut file_list = mla_read
2783 .list_entries()
2784 .unwrap()
2785 .cloned()
2786 .collect::<Vec<_>>();
2787 file_list.sort();
2788 assert_eq!(file_list, file_names);
2789
2790 let mut rng_data = ChaChaRng::seed_from_u64(0);
2794
2795 let mut chunk = vec![0u8; CHUNK_SIZE];
2796 for file_name in file_names {
2797 let mut file_stream = mla_read.get_entry(file_name).unwrap().unwrap().data;
2798 loop {
2799 let read = file_stream.read(&mut chunk).unwrap();
2800 let expect: Vec<u8> = Standard.sample_iter(&mut rng_data).take(read).collect();
2801 assert_eq!(&chunk[..read], expect.as_slice());
2802 if read == 0 {
2803 break;
2804 }
2805 }
2806 }
2807 }
2808
2809 #[test]
2810 fn signature_verification_fails_with_wrong_public_key() {
2811 let file = Vec::new();
2812
2813 let (privkey_correct, _pubkey_correct) = generate_mla_keypair_from_seed([1u8; 32]);
2815 let (_privkey_wrong, pubkey_wrong) = generate_mla_keypair_from_seed([2u8; 32]);
2817
2818 let config = ArchiveWriterConfig::without_encryption_with_signature(&[privkey_correct
2819 .get_signing_private_key()
2820 .clone()])
2821 .unwrap();
2822
2823 let mut writer = ArchiveWriter::from_config(file, config).expect("Writer init failed");
2824
2825 let data = vec![10, 20, 30, 40];
2826 writer
2827 .add_entry(
2828 EntryName::from_path("entry1").unwrap(),
2829 data.len() as u64,
2830 data.as_slice(),
2831 )
2832 .unwrap();
2833
2834 let archive = writer.finalize().unwrap();
2835 let buf = Cursor::new(archive.as_slice());
2836
2837 let config = ArchiveReaderConfig::with_signature_verification(&[pubkey_wrong
2838 .get_signature_verification_public_key()
2839 .clone()])
2840 .without_encryption();
2841
2842 let reader_result = ArchiveReader::from_config(buf, config);
2844
2845 assert!(
2846 reader_result.is_err(),
2847 "Verification should fail with wrong public key"
2848 );
2849 }
2850
2851 #[test]
2852 fn signature_verification_fails_on_tampered_archive() {
2853 let file = Vec::new();
2854
2855 let (privkey, pubkey) = generate_mla_keypair_from_seed([5u8; 32]);
2856
2857 let config = ArchiveWriterConfig::without_encryption_with_signature(&[privkey
2858 .get_signing_private_key()
2859 .clone()])
2860 .unwrap();
2861
2862 let mut writer = ArchiveWriter::from_config(file, config).expect("Writer init failed");
2863
2864 let data = vec![50, 60, 70, 80];
2865 writer
2866 .add_entry(
2867 EntryName::from_path("my_file").unwrap(),
2868 data.len() as u64,
2869 data.as_slice(),
2870 )
2871 .unwrap();
2872
2873 let mut archive = writer.finalize().unwrap();
2874
2875 archive[10] ^= 0xFF;
2877
2878 let buf = Cursor::new(archive.as_slice());
2879
2880 let config = ArchiveReaderConfig::with_signature_verification(&[pubkey
2881 .get_signature_verification_public_key()
2882 .clone()])
2883 .without_encryption();
2884
2885 let result = ArchiveReader::from_config(buf, config);
2887
2888 assert!(
2889 result.is_err(),
2890 "Signature verification should fail on tampered archive"
2891 );
2892 }
2893
2894 #[test]
2895 fn signature_verification_fails_if_ed25519_signature_corrupted() {
2896 let file = Vec::new();
2897
2898 let (privkey, pubkey) = generate_mla_keypair_from_seed([7u8; 32]);
2899
2900 let config = ArchiveWriterConfig::without_encryption_with_signature(&[privkey
2901 .get_signing_private_key()
2902 .clone()])
2903 .unwrap();
2904
2905 let mut writer = ArchiveWriter::from_config(file, config).expect("Writer init failed");
2906
2907 let data = vec![10, 20, 30, 40];
2908 writer
2909 .add_entry(
2910 EntryName::from_path("entry1").unwrap(),
2911 data.len() as u64,
2912 data.as_slice(),
2913 )
2914 .unwrap();
2915
2916 let mut archive = writer.finalize().unwrap();
2917
2918 let flip_pos = 50;
2921 archive[flip_pos] ^= 0xFF;
2922
2923 let buf = std::io::Cursor::new(archive);
2924
2925 let config = ArchiveReaderConfig::with_signature_verification(&[pubkey
2926 .get_signature_verification_public_key()
2927 .clone()])
2928 .without_encryption();
2929
2930 let reader_result = ArchiveReader::from_config(buf, config);
2931
2932 assert!(
2933 matches!(reader_result, Err(Error::NoValidSignatureFound)),
2934 "Verification should fail if Ed25519 signature is corrupted"
2935 );
2936 }
2937
2938 #[test]
2939 fn signature_verification_fails_if_mldsa87_signature_corrupted() {
2940 let file = Vec::new();
2941
2942 let (privkey, pubkey) = generate_mla_keypair_from_seed([8u8; 32]);
2943
2944 let config = ArchiveWriterConfig::without_encryption_with_signature(&[privkey
2945 .get_signing_private_key()
2946 .clone()])
2947 .unwrap();
2948
2949 let mut writer = ArchiveWriter::from_config(file, config).expect("Writer init failed");
2950
2951 let data = vec![50, 60, 70, 80];
2952 writer
2953 .add_entry(
2954 EntryName::from_path("my_file").unwrap(),
2955 data.len() as u64,
2956 data.as_slice(),
2957 )
2958 .unwrap();
2959
2960 let mut archive = writer.finalize().unwrap();
2961
2962 let flip_pos = 4000;
2965 archive[flip_pos] ^= 0xFF;
2966
2967 let buf = std::io::Cursor::new(archive);
2968
2969 let config = ArchiveReaderConfig::with_signature_verification(&[pubkey
2970 .get_signature_verification_public_key()
2971 .clone()])
2972 .without_encryption();
2973
2974 let reader_result = ArchiveReader::from_config(buf, config);
2975
2976 assert!(
2977 matches!(reader_result, Err(Error::NoValidSignatureFound)),
2978 "Verification should fail if MlDsa87 signature is corrupted"
2979 );
2980 }
2981
2982 #[test]
2983 fn test_footer_deserialization_none() {
2984 let data = vec![0u8]; let res = ArchiveFooter::deserialize_from(Cursor::new(&data)).unwrap();
2986 assert!(res.is_none());
2987 }
2988
2989 #[test]
2990 fn test_footer_deserialization_invalid_index() {
2991 let data = vec![0x42]; let res = ArchiveFooter::deserialize_from(Cursor::new(&data)).unwrap();
2993 assert!(res.is_none());
2994 }
2995
2996 #[test]
2997 fn test_footer_deserialization_valid_index() {
2998 let (_dest, _sender_key, _receiver_key, _files, entries_info, ids_info) =
2999 build_archive2(false, false, false, false);
3000
3001 let mut buf = Cursor::new(Vec::new());
3003 buf.write_all(&[1]).unwrap();
3004 ArchiveFooter::serialize_into(&mut buf, &entries_info, &ids_info).unwrap();
3005 buf.rewind().unwrap();
3006
3007 let result = ArchiveFooter::deserialize_from(buf).unwrap();
3008 assert!(result.is_some());
3009 }
3010
3011 #[test]
3012 #[cfg(feature = "send")]
3013 fn test_send() {
3014 static_assertions::assert_cfg!(feature = "send");
3015 static_assertions::assert_impl_all!(File: Send);
3016 static_assertions::assert_impl_all!(ArchiveWriter<File>: Send);
3017 static_assertions::assert_impl_all!(ArchiveReader<File>: Send);
3018 }
3019}