├── .gitattributes ├── .github └── workflows │ ├── build_and_test.yml │ └── mirror-to-codeberg.yaml ├── .gitignore ├── .gitmodules ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── SECURITY.md ├── benchmarks ├── acquisition_time.png ├── gen_benchmark_image.sh └── read_speed_dd.png └── src └── lib ├── chunk.rs ├── compression.rs ├── constants.rs ├── encryption.rs ├── error.rs ├── file ├── encoder.rs └── mod.rs ├── file_extension.rs ├── footer ├── file_footer.rs ├── main_footer.rs ├── mod.rs ├── object_footer │ ├── mod.rs │ ├── object_footer_logical.rs │ ├── object_footer_physical.rs │ └── object_footer_virtual.rs └── segment_footer.rs ├── hashing.rs ├── header ├── chunk_header.rs ├── chunk_map │ ├── chunk_deduplication.rs │ ├── chunk_header.rs │ ├── chunk_same_bytes.rs │ └── mod.rs ├── compression_header.rs ├── description_header.rs ├── encryption_header.rs ├── file_header.rs ├── hash_header.rs ├── mod.rs ├── object_header.rs ├── pbe_header.rs ├── segment_header.rs └── virtual_maps.rs ├── helper.rs ├── io ├── mod.rs ├── zffreader │ ├── mod.rs │ ├── redb_handling.rs │ └── zffobjectreader.rs └── zffwriter │ └── mod.rs ├── mod.rs ├── object ├── encoder.rs └── mod.rs ├── segment.rs ├── signatures.rs └── traits.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh linguist-vendored -------------------------------------------------------------------------------- /.github/workflows/build_and_test.yml: -------------------------------------------------------------------------------- 1 | name: Build and test workflow 2 | 3 | on: [ push, pull_request ] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | LLVM-MINGW-TOOLCHAIN-NAME: llvm-mingw-20240619-ucrt-ubuntu-20.04-x86_64 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | name: ${{ matrix.name }} 13 | strategy: 14 | matrix: 15 | os: 16 | - ubuntu-22.04 17 | - ubuntu-latest # only used for building the windows aarch64 binary 18 | - windows-latest 19 | - macos-latest 20 | include: 21 | - os: ubuntu-22.04 22 | libacl: libacl1-dev 23 | toolchain: stable 24 | target: x86_64-unknown-linux-gnu 25 | name: Linux (Ubuntu 20.04) 26 | - os: ubuntu-latest 27 | libacl: "" # no libacl on ubuntu-latest for windows aarch64 28 | toolchain: nightly 29 | target: aarch64-pc-windows-gnullvm 30 | name: Windows aarch64 (cross-compiled on Ubuntu 24.04) 31 | - os: windows-latest 32 | libacl: "" 33 | toolchain: nightly 34 | target: x86_64-pc-windows-msvc 35 | name: Windows x86_64 (MSVC) 36 | - os: macos-latest 37 | libacl: "" 38 | toolchain: stable 39 | target: aarch64-apple-darwin 40 | name: macOS (arm64) 41 | 42 | steps: 43 | - uses: actions/checkout@v2 44 | 45 | - name: Set up Rust 46 | uses: actions-rs/toolchain@v1 47 | with: 48 | toolchain: ${{ matrix.toolchain }} 49 | target: ${{ matrix.target }} 50 | components: clippy 51 | default: true 52 | 53 | - name: Install acl-deps (Linux only) 54 | if: matrix.os == 'ubuntu-20.04' 55 | run: sudo apt-get install -y ${{ matrix.libacl }} 56 | 57 | - name: Install LLVM MinGW toolchain (Windows aarch64) 58 | if: matrix.os == 'ubuntu-latest' 59 | run: | 60 | curl -L -o ${{ env.LLVM-MINGW-TOOLCHAIN-NAME }}.tar.xz https://github.com/mstorsjo/llvm-mingw/releases/download/20240619/${{ env.LLVM-MINGW-TOOLCHAIN-NAME }}.tar.xz 61 | tar -xf ${{ env.LLVM-MINGW-TOOLCHAIN-NAME }}.tar.xz 62 | echo "$GITHUB_WORKSPACE/${{ env.LLVM-MINGW-TOOLCHAIN-NAME }}/bin" >> $GITHUB_PATH 63 | 64 | - name: Build ${{ matrix.target }} 65 | run: cargo clippy --verbose --all-features --target ${{ matrix.target }} 66 | 67 | - name: Run tests 68 | if: matrix.os != 'ubuntu-latest' 69 | run: cargo test --verbose --all-features --target ${{ matrix.target }} -------------------------------------------------------------------------------- /.github/workflows/mirror-to-codeberg.yaml: -------------------------------------------------------------------------------- 1 | name: Mirror to Codeberg 2 | 3 | on: [push] 4 | 5 | jobs: 6 | mirror: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | - uses: yesolutions/mirror-action@master 14 | with: 15 | REMOTE: 'https://codeberg.org/zff-team/zff-rs.git' 16 | GIT_USERNAME: yourusername 17 | GIT_PASSWORD: ${{ secrets.CODEBERG_TOKEN }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | /target 15 | 16 | # for testing purposes only. 17 | /zff/src/bin 18 | 19 | # ignore vscode folder 20 | /.vscode -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zff-team/zff-rs/3ca2b83bf91e9718d90a8db4330a60a0eb31371d/.gitmodules -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zff" 3 | version = "3.0.0-rc.4" 4 | authors = ["ph0llux , 11 | flags: ChunkFlags, 12 | size: u64, 13 | } 14 | 15 | impl Chunk { 16 | /// Returns a new [Chunk] with the given values. 17 | pub fn new(data: Vec, flags: ChunkFlags, size: u64) -> Chunk { 18 | Self { 19 | data, 20 | flags, 21 | size 22 | } 23 | } 24 | 25 | /// Returns the underlying data. 26 | pub fn data(&self) -> &Vec { 27 | &self.data 28 | } 29 | 30 | /// Returns the flags. 31 | pub fn flags(&self) -> &ChunkFlags { 32 | &self.flags 33 | } 34 | 35 | /// Returns the size. 36 | pub fn size(&self) -> u64 { 37 | self.size 38 | } 39 | 40 | /// Checks the integrity of the chunk data by calculating the appropriate xxhash hash and comparing it with the given hash. 41 | /// 42 | /// Returns true if the xxhash hash is equal to the hash in the header, otherwise false. 43 | pub fn check_integrity(&self, original_hash: u64) -> Result { 44 | let calculated_hash = calculate_xxhash(&self.data); 45 | Ok(calculated_hash == original_hash) 46 | } 47 | } 48 | 49 | /// This struct represents a prepared [Chunk] (encrypted and compressed). 50 | #[derive(Debug, Clone, Default)] 51 | pub(crate) struct PreparedChunk { 52 | pub data: Vec, 53 | pub chunk_header: ChunkHeader, // the offset has to be set afterwards 54 | pub samebytes: Option, 55 | pub duplicated: Option, 56 | } 57 | 58 | impl PreparedChunk { 59 | /// Returns a new [PreparedChunk] with the given values. 60 | pub fn new(data: Vec, chunk_header: ChunkHeader, samebytes: Option, duplicated: Option) -> PreparedChunk { 61 | Self { 62 | data, 63 | chunk_header, 64 | samebytes, 65 | duplicated 66 | } 67 | } 68 | } 69 | 70 | #[derive(Debug, Clone)] 71 | /// The data of the chunk. 72 | pub(crate) enum ChunkContent { 73 | /// The unencrypted and uncompressed original data of the chunk. 74 | Raw(Vec), 75 | /// The appropriate byte, if the same byte flag is set. 76 | SameBytes(u8), 77 | /// The appropriate chunk, if this chunk is a duplication. 78 | Duplicate(u64), 79 | } 80 | -------------------------------------------------------------------------------- /src/lib/compression.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::Read; 3 | use std::borrow::Borrow; 4 | use std::fmt; 5 | 6 | 7 | // - internal 8 | use crate::Result; 9 | 10 | // - external 11 | #[cfg(feature = "serde")] 12 | use serde::{ 13 | Deserialize, 14 | Serialize, 15 | }; 16 | 17 | /// Defines all compression algorithms, which are implemented in zff. 18 | #[repr(u8)] 19 | #[non_exhaustive] 20 | #[derive(Debug,Clone,Eq,PartialEq)] 21 | #[cfg_attr(feature = "serde", derive(Deserialize))] 22 | #[cfg_attr(feature = "serde", derive(Serialize))] 23 | pub enum CompressionAlgorithm { 24 | /// No compression - encoded as 0 in the header. 25 | None = 0, 26 | /// Zstd compression (default) - encoded as 1 in the header. 27 | Zstd = 1, 28 | /// LZ4 compression - encoded as 2 in the header. LZ4 frame format is used (not the LZ4 block format) for compression. 29 | Lz4 = 2, 30 | } 31 | 32 | impl From<&str> for CompressionAlgorithm { 33 | fn from(algorithm: &str) -> CompressionAlgorithm { 34 | let algorithm = algorithm.to_lowercase(); 35 | match algorithm.as_str() { 36 | "zstd" => CompressionAlgorithm::Zstd, 37 | "lz4" => CompressionAlgorithm::Lz4, 38 | _ => CompressionAlgorithm::None, 39 | } 40 | } 41 | } 42 | 43 | impl fmt::Display for CompressionAlgorithm { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | let value = match self { 46 | CompressionAlgorithm::Zstd => "Zstd", 47 | CompressionAlgorithm::Lz4 => "Lz4", 48 | CompressionAlgorithm::None => "None", 49 | }; 50 | write!(f, "{value}") 51 | } 52 | } 53 | 54 | /// Decompresses a buffer with the given [CompressionAlgorithm]. 55 | pub fn decompress_buffer(buffer: &[u8], compression_algorithm: C) -> Result> 56 | where 57 | C: Borrow, 58 | { 59 | match compression_algorithm.borrow() { 60 | CompressionAlgorithm::None => Ok(buffer.to_vec()), 61 | CompressionAlgorithm::Zstd => { 62 | let mut decompressed_buffer = Vec::new(); 63 | let mut decoder = zstd::stream::read::Decoder::new(buffer)?; 64 | decoder.read_to_end(&mut decompressed_buffer)?; 65 | Ok(decompressed_buffer) 66 | }, 67 | CompressionAlgorithm::Lz4 => { 68 | let mut decompressed_buffer = Vec::new(); 69 | let mut decompressor = lz4_flex::frame::FrameDecoder::new(buffer); 70 | decompressor.read_to_end(&mut decompressed_buffer)?; 71 | Ok(decompressed_buffer) 72 | } 73 | } 74 | } 75 | 76 | /// Decompresses a reader with the given [CompressionAlgorithm]. 77 | pub fn decompress_reader(input: &mut R, compression_algorithm: C) -> Result> 78 | where 79 | C: Borrow, 80 | R: Read + std::marker::Send + 'static, 81 | { 82 | match compression_algorithm.borrow() { 83 | CompressionAlgorithm::None => Ok(Box::new(input)), 84 | CompressionAlgorithm::Zstd => { 85 | let decoder = zstd::stream::read::Decoder::new(input)?; 86 | Ok(Box::new(decoder)) 87 | }, 88 | CompressionAlgorithm::Lz4 => { 89 | let decompressor = lz4_flex::frame::FrameDecoder::new(input); 90 | Ok(Box::new(decompressor)) 91 | }, 92 | } 93 | } -------------------------------------------------------------------------------- /src/lib/file/encoder.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::{Read, Cursor, Seek}; 3 | use std::path::PathBuf; 4 | use std::rc::Rc; 5 | use std::cell::RefCell; 6 | 7 | use std::time::SystemTime; 8 | 9 | use crate::header::{ChunkFlags, ChunkHeader}; 10 | use crate::io::BufferedChunk; 11 | use crate::PreparedChunk; 12 | // - internal 13 | use crate::{ 14 | header::{FileHeader, HashHeader, HashValue, EncryptionInformation, ObjectHeader, DeduplicationMetadata}, 15 | footer::FileFooter, 16 | }; 17 | use crate::{ 18 | Result, 19 | EncodingState, 20 | io::buffer_chunk, 21 | HeaderCoding, 22 | ValueEncoder, 23 | EncodingThreadPoolManager, 24 | Signature, 25 | chunking, 26 | }; 27 | 28 | #[cfg(feature = "log")] 29 | use crate::hashes_to_log; 30 | 31 | // - external 32 | use time::OffsetDateTime; 33 | use ed25519_dalek::SigningKey; 34 | 35 | /// This enum contains the information, which are needed to encode the different file types. 36 | #[derive(Debug)] 37 | pub enum FileTypeEncodingInformation { 38 | /// A regular file. 39 | File, 40 | /// A directory with the given children. 41 | Directory(Vec), // directory children, 42 | /// A symlink with the given real path. 43 | Symlink(PathBuf), // symlink real path 44 | /// A hardlink with the given twin filenumber. 45 | Hardlink(u64), // hardlink filenumber 46 | /// A special file with the given special file information. 47 | #[cfg(target_family = "unix")] 48 | SpecialFile(SpecialFileEncodingInformation), // special file information (rdev, type_flag) 49 | } 50 | 51 | /// This enum contains the information, which are needed to encode the different special file types. 52 | #[cfg(target_family = "unix")] 53 | #[derive(Debug)] 54 | pub enum SpecialFileEncodingInformation { 55 | /// A fifo file with the given rdev-id. 56 | Fifo(u64), // fifo(rdev), 57 | /// A char file with the given rdev-id. 58 | Char(u64), // char(rdev), 59 | /// A block file with the given rdev-id. 60 | Block(u64), // block(rdev), 61 | /// A socket file with the given rdev-id. 62 | Socket(u64), // socket(rdev), 63 | } 64 | 65 | /// The [FileEncoder] can be used to encode a [crate::file::File]. 66 | pub struct FileEncoder { 67 | /// The appropriate [FileHeader]. 68 | file_header: FileHeader, 69 | /// The appropriate [ObjectHeader]. 70 | object_header: ObjectHeader, 71 | /// The underlying [File](std::fs::File) object to read from. 72 | underlying_file: Box, 73 | /// The optional signing key, to sign the hashes. 74 | signing_key: Option, 75 | /// optional encryption information, to encrypt the data with the given key and algorithm 76 | encryption_information: Option, 77 | /// A reference counter to the encoding thread pool manager of the parent logical object encoder. 78 | encoding_thread_pool_manager: Rc>, 79 | /// The first chunk number for this file. 80 | initial_chunk_number: u64, 81 | /// The current chunk number 82 | current_chunk_number: u64, 83 | /// The number of bytes, which were read from the underlying file. 84 | read_bytes_underlying_data: u64, 85 | acquisition_start: u64, 86 | acquisition_end: u64, 87 | filetype_encoding_information: FileTypeEncodingInformation, 88 | } 89 | 90 | impl FileEncoder { 91 | /// creates a new [FileEncoder] with the given values. 92 | #[allow(clippy::too_many_arguments)] 93 | pub fn new( 94 | file_header: FileHeader, 95 | object_header: ObjectHeader, 96 | file: Box, 97 | encoding_thread_pool_manager: Rc>, 98 | signing_key: Option, 99 | encryption_information: Option, 100 | current_chunk_number: u64, 101 | filetype_encoding_information: FileTypeEncodingInformation) -> Result { 102 | 103 | Ok(Self { 104 | file_header, 105 | object_header, 106 | underlying_file: Box::new(file), 107 | encoding_thread_pool_manager, 108 | signing_key, 109 | encryption_information, 110 | initial_chunk_number: current_chunk_number, 111 | current_chunk_number, 112 | read_bytes_underlying_data: 0, 113 | acquisition_start: 0, 114 | acquisition_end: 0, 115 | filetype_encoding_information, 116 | }) 117 | } 118 | 119 | /// Returns a reference of the appropriate file header 120 | pub fn file_header_ref(&self) -> &FileHeader { 121 | &self.file_header 122 | } 123 | 124 | /// returns the underlying encoded header 125 | pub fn get_encoded_header(&mut self) -> Vec { 126 | if self.acquisition_start == 0 { 127 | self.acquisition_start = OffsetDateTime::from(SystemTime::now()).unix_timestamp() as u64; 128 | } 129 | if let Some(enc_info) = &self.encryption_information { 130 | //unwrap should be safe here, because we have already testet this before. 131 | self.file_header.encode_encrypted_header_directly(enc_info).unwrap() 132 | } else { 133 | self.file_header.encode_directly() 134 | } 135 | } 136 | 137 | /// returns the encoded chunk - this method will increment the self.current_chunk_number automatically. 138 | pub(crate) fn get_next_chunk( 139 | &mut self, 140 | deduplication_metadata: Option<&mut DeduplicationMetadata>, 141 | ) -> Result { 142 | let chunk_size = self.object_header.chunk_size as usize; 143 | let mut eof = false; 144 | 145 | let buffered_chunk = match &self.filetype_encoding_information { 146 | FileTypeEncodingInformation::Directory(directory_children) => { 147 | let encoded_directory_children = if directory_children.is_empty() { 148 | Vec::::new().encode_directly() 149 | } else { 150 | directory_children.encode_directly() 151 | }; 152 | let mut cursor = Cursor::new(&encoded_directory_children); 153 | cursor.set_position(self.read_bytes_underlying_data); 154 | let buffered_chunk = buffer_chunk(&mut cursor, chunk_size)?; 155 | self.read_bytes_underlying_data += buffered_chunk.bytes_read; 156 | buffered_chunk 157 | }, 158 | FileTypeEncodingInformation::Symlink(symlink_real_path) => { 159 | let encoded_symlink_real_path = symlink_real_path.to_string_lossy().encode_directly(); 160 | let mut cursor = Cursor::new(&encoded_symlink_real_path); 161 | cursor.set_position(self.read_bytes_underlying_data); 162 | let buffered_chunk = buffer_chunk(&mut cursor, chunk_size)?; 163 | self.read_bytes_underlying_data += buffered_chunk.bytes_read; 164 | if buffered_chunk.bytes_read > 0 { 165 | buffered_chunk 166 | } else { 167 | eof = true; 168 | BufferedChunk::default() 169 | } 170 | }, 171 | FileTypeEncodingInformation::Hardlink(hardlink_filenumber) => { 172 | let encoded_hardlink_filenumber = hardlink_filenumber.encode_directly(); 173 | let mut cursor = Cursor::new(&encoded_hardlink_filenumber); 174 | cursor.set_position(self.read_bytes_underlying_data); 175 | let buffered_chunk = buffer_chunk(&mut cursor, chunk_size)?; 176 | self.read_bytes_underlying_data += buffered_chunk.bytes_read; 177 | if buffered_chunk.bytes_read > 0 { 178 | buffered_chunk 179 | } else { 180 | eof = true; 181 | BufferedChunk::default() 182 | } 183 | }, 184 | FileTypeEncodingInformation::File => { 185 | let buffered_chunk = buffer_chunk(&mut self.underlying_file, chunk_size)?; 186 | self.read_bytes_underlying_data += buffered_chunk.bytes_read; 187 | buffered_chunk 188 | }, 189 | // contains the rdev-id and a flag for the type of the special file 190 | // (0 if fifo-, 1 if char-, 2 if block-, and 3 if it is a socket-file). 191 | #[cfg(target_family = "unix")] 192 | FileTypeEncodingInformation::SpecialFile(specialfile_encoding_information) => { 193 | let (rdev_id, type_flag) = match specialfile_encoding_information { 194 | SpecialFileEncodingInformation::Fifo(rdev_id) => (rdev_id, 0_u8), 195 | SpecialFileEncodingInformation::Char(rdev_id) => (rdev_id, 1), 196 | SpecialFileEncodingInformation::Block(rdev_id) => (rdev_id, 2), 197 | SpecialFileEncodingInformation::Socket(rdev_id) => (rdev_id, 3), 198 | }; 199 | let mut encoded_data = rdev_id.encode_directly(); 200 | encoded_data.append(&mut type_flag.encode_directly()); 201 | let mut cursor = Cursor::new(&encoded_data); 202 | cursor.set_position(self.read_bytes_underlying_data); 203 | let buffered_chunk = buffer_chunk(&mut cursor, chunk_size)?; 204 | self.read_bytes_underlying_data += buffered_chunk.bytes_read; 205 | if buffered_chunk.bytes_read > 0 { 206 | buffered_chunk 207 | } else { 208 | eof = true; 209 | BufferedChunk::default() 210 | } 211 | } 212 | }; 213 | 214 | if buffered_chunk.buffer.is_empty() && self.read_bytes_underlying_data != 0 || eof { 215 | //this case is the normal "file reader reached EOF". 216 | return Ok(EncodingState::ReadEOF); 217 | } else if buffered_chunk.buffer.is_empty() && self.read_bytes_underlying_data == 0 { 218 | //this case is the "file is empty". 219 | let mut flags = ChunkFlags::default(); 220 | flags.empty_file = true; 221 | let mut chunk_header = ChunkHeader::default(); 222 | chunk_header.flags = flags; 223 | let prepared_chunk = PreparedChunk::new(Vec::new(), chunk_header, None, None); 224 | return Ok(EncodingState::PreparedChunk(prepared_chunk)) 225 | }; 226 | 227 | // Needed for the same byte check 228 | let buf_len = buffered_chunk.buffer.len() as u64; 229 | 230 | let mut encoding_thread_pool_manager = self.encoding_thread_pool_manager.borrow_mut(); 231 | 232 | encoding_thread_pool_manager.update(buffered_chunk.buffer); 233 | 234 | let encryption_algorithm = self.encryption_information.as_ref().map(|encryption_information| &encryption_information.algorithm); 235 | let encryption_key = self.encryption_information.as_ref().map(|encryption_information| &encryption_information.encryption_key); 236 | 237 | let chunk = chunking( 238 | &mut encoding_thread_pool_manager, 239 | self.current_chunk_number, 240 | buf_len, 241 | chunk_size as u64, 242 | deduplication_metadata, 243 | encryption_key, 244 | encryption_algorithm, 245 | )?; 246 | 247 | self.current_chunk_number += 1; 248 | Ok(EncodingState::PreparedChunk(chunk)) 249 | } 250 | 251 | /// returns the appropriate encoded [FileFooter]. 252 | /// A call of this method finalizes the underlying hashers. You should be care. 253 | pub fn get_encoded_footer(&mut self) -> Result> { 254 | let mut encoding_thread_pool_manager = self.encoding_thread_pool_manager.borrow_mut(); 255 | 256 | self.acquisition_end = OffsetDateTime::from(SystemTime::now()).unix_timestamp() as u64; 257 | let mut hash_values = Vec::new(); 258 | for (hash_type, hash) in encoding_thread_pool_manager.finalize_all_hashing_threads() { 259 | let mut hash_value = HashValue::new_empty(hash_type.clone()); 260 | hash_value.set_hash(hash.to_vec()); 261 | if let Some(signing_key) = &self.signing_key { 262 | let signature = Signature::sign(signing_key, &hash); 263 | hash_value.set_ed25519_signature(signature); 264 | } 265 | hash_values.push(hash_value); 266 | } 267 | 268 | #[cfg(feature = "log")] 269 | hashes_to_log(self.object_header.object_number, Some(self.file_header.file_number), &hash_values); 270 | 271 | let hash_header = HashHeader::new(hash_values); 272 | let footer = FileFooter::new( 273 | self.file_header.file_number, 274 | self.acquisition_start, 275 | self.acquisition_end, 276 | hash_header, 277 | self.initial_chunk_number, 278 | self.current_chunk_number - self.initial_chunk_number, 279 | self.read_bytes_underlying_data, 280 | ); 281 | if let Some(enc_info) = &self.encryption_information { 282 | footer.encode_encrypted_header_directly(enc_info) 283 | } else { 284 | Ok(footer.encode_directly()) 285 | } 286 | } 287 | } -------------------------------------------------------------------------------- /src/lib/file/mod.rs: -------------------------------------------------------------------------------- 1 | // - modules 2 | mod encoder; 3 | 4 | // - re-exports 5 | pub use encoder::*; 6 | 7 | // - internal 8 | use crate::{ 9 | header::{FileHeader, FileType}, 10 | footer::FileFooter, 11 | }; 12 | 13 | /// The [File] contains the appropriate [FileHeader] and [FileFooter] of a dumped [File]. 14 | /// Also this struct contains a position value for a [Reader](std::io::Read). 15 | #[derive(Debug, Clone, Eq, PartialEq)] 16 | pub struct File { 17 | header: FileHeader, 18 | footer: FileFooter, 19 | position: u64, 20 | } 21 | 22 | impl File { 23 | /// creates a new [File] instance for the given [FileHeader] and [FileFooter]. 24 | pub fn new(header: FileHeader, footer: FileFooter) -> File { 25 | Self { 26 | header, 27 | footer, 28 | position: 0 29 | } 30 | } 31 | 32 | /// returns a reference of the underlying [FileHeader]. 33 | pub fn header(&self) -> &FileHeader { 34 | &self.header 35 | } 36 | 37 | /// returns a reference of the underlying [FileFooter]. 38 | pub fn footer(&self) -> &FileFooter { 39 | &self.footer 40 | } 41 | 42 | /// returns the parent file number 43 | pub fn parent(&self) -> u64 { 44 | self.header.parent_file_number 45 | } 46 | 47 | /// returns the [FileType]. 48 | pub fn filetype(&self) -> FileType { 49 | self.header.file_type.clone() 50 | } 51 | 52 | /// returns the position of the [Reader](std::io::Read) used for this [File]. 53 | pub fn position(&self) -> u64 { 54 | self.position 55 | } 56 | 57 | /// sets the position of the [Reader](std::io::Read). 58 | pub fn set_position(&mut self, position: u64) { 59 | self.position = position 60 | } 61 | 62 | /// returns the length of the data, read from the underlying [FileFooter]. 63 | pub fn length_of_data(&self) -> u64 { 64 | self.footer.length_of_data 65 | } 66 | } -------------------------------------------------------------------------------- /src/lib/file_extension.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Result, 3 | ZffError, 4 | ZffErrorKind, 5 | FILE_EXTENSION_START, 6 | FILE_EXTENSION_START_PRE9, 7 | FILE_EXTENSION_PARSER_ERROR, 8 | }; 9 | 10 | /// Returns the next file extension value. 11 | /// # Example 12 | /// ``` 13 | /// use zff::*; 14 | /// 15 | /// let file_extension = "z01"; 16 | /// assert_eq!(file_extension_next_value(file_extension).unwrap(), "z02"); 17 | /// ``` 18 | /// # Error 19 | /// fails if the file-extension is in an unsuitable format. 20 | pub fn file_extension_next_value>(value: V) -> Result { 21 | let value = value.into(); 22 | 23 | let mut chars = value.chars(); 24 | match chars.next() { 25 | Some(FILE_EXTENSION_START) => (), 26 | _ => return Err(ZffError::new( 27 | ZffErrorKind::ParsingError, 28 | format!("{FILE_EXTENSION_PARSER_ERROR} \"{value}\""))), 29 | }; 30 | let mut next_value: u64 = match chars.as_str().parse() { 31 | Ok(val) => val, 32 | Err(e) => return Err(ZffError::new(ZffErrorKind::ParsingError, e.to_string())), 33 | }; 34 | next_value += 1; 35 | if next_value <= 9 { 36 | Ok(String::from(FILE_EXTENSION_START_PRE9) + &next_value.to_string()) 37 | } else { 38 | Ok(String::from(FILE_EXTENSION_START) + &next_value.to_string()) 39 | } 40 | } 41 | 42 | /// Returns the previous file extension value. 43 | /// # Example 44 | /// ``` 45 | /// use zff::*; 46 | /// 47 | /// let file_extension = "z05"; 48 | /// assert_eq!(file_extension_previous_value(file_extension).unwrap(), "z04"); 49 | /// ``` 50 | /// # Error 51 | /// fails if the file-extension is in an unsuitable format or if the previous value is < 0. 52 | pub fn file_extension_previous_value>(value: V) -> Result { 53 | let value = value.into(); 54 | 55 | let mut chars = value.chars(); 56 | match chars.next() { 57 | Some(FILE_EXTENSION_START) => (), 58 | _ => return Err(ZffError::new( 59 | ZffErrorKind::ParsingError, 60 | FILE_EXTENSION_PARSER_ERROR)), 61 | }; 62 | let mut previous_value: u64 = match chars.as_str().parse() { 63 | Ok(val) => val, 64 | Err(e) => return Err(ZffError::new( 65 | ZffErrorKind::ParsingError, 66 | e.to_string())), 67 | }; 68 | previous_value -= 1; 69 | if previous_value <= 9 { 70 | Ok(String::from(FILE_EXTENSION_START_PRE9) + &previous_value.to_string()) 71 | } else { 72 | Ok(String::from(FILE_EXTENSION_START) + &previous_value.to_string()) 73 | } 74 | } -------------------------------------------------------------------------------- /src/lib/footer/file_footer.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use core::borrow::Borrow; 3 | use std::io::{Cursor, Read}; 4 | use std::fmt; 5 | 6 | // - internal 7 | use crate::{ 8 | Result, 9 | HeaderCoding, 10 | ValueDecoder, 11 | ValueEncoder, 12 | Encryption, 13 | ZffError, 14 | ZffErrorKind, 15 | encryption::EncryptionAlgorithm, 16 | FOOTER_IDENTIFIER_FILE_FOOTER, 17 | DEFAULT_LENGTH_HEADER_IDENTIFIER, 18 | DEFAULT_LENGTH_VALUE_HEADER_LENGTH, 19 | DEFAULT_FOOTER_VERSION_FILE_FOOTER, 20 | ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER, 21 | }; 22 | use crate::header::{ 23 | HashHeader, 24 | EncryptionInformation, 25 | }; 26 | 27 | // - external 28 | #[cfg(feature = "serde")] 29 | use serde::Serialize; 30 | 31 | 32 | /// The file footer is written at the end of each acquired file. 33 | /// 34 | /// The file footer contains several metadata about the acquisition process itself: e.g. the acquisition start/end time of the appropriate file, 35 | /// hash values, or size information. 36 | /// The general structure of the file footer is the same for all file types. 37 | #[derive(Debug,Clone,Eq,PartialEq)] 38 | #[cfg_attr(feature = "serde", derive(Serialize))] 39 | pub struct FileFooter { 40 | /// the appropriate file number. 41 | pub file_number: u64, 42 | /// the acquisition start time for this file. 43 | pub acquisition_start: u64, 44 | /// the acquisition end/finish time for this file. 45 | pub acquisition_end: u64, 46 | /// The appropriate hash header for this file. 47 | pub hash_header: HashHeader, 48 | /// the first chunk number which was used for this file. 49 | pub first_chunk_number: u64, 50 | /// The full number of chunks for this file. 51 | pub number_of_chunks: u64, 52 | /// the original (uncompressed & unencrypted) length of the file. 53 | /// - If the file is a regular file, this method returns the original (uncompressed, unencrypted) size 54 | /// of the file (without "filesystem-"metadata - just the size of the file content). 55 | /// - If the file is a hardlink, this method returns the size of the inner value (just the size of the appropriate filenumber: 8). 56 | /// - If the file is a directory, this method returns the size of the underlying vector of children. 57 | /// - If the file is a symlink, this method returns the length of the linked path. 58 | pub length_of_data: u64, 59 | } 60 | 61 | impl FileFooter { 62 | /// creates a new FileFooter by given values/hashes. 63 | pub fn new( 64 | file_number: u64, 65 | acquisition_start: u64, 66 | acquisition_end: u64, 67 | hash_header: HashHeader, 68 | first_chunk_number: u64, 69 | number_of_chunks: u64, 70 | length_of_data: u64) -> FileFooter { 71 | Self { 72 | file_number, 73 | acquisition_start, 74 | acquisition_end, 75 | hash_header, 76 | first_chunk_number, 77 | number_of_chunks, 78 | length_of_data, 79 | } 80 | } 81 | 82 | fn encode_content(&self) -> Vec { 83 | let mut vec = Vec::new(); 84 | vec.append(&mut self.acquisition_start.encode_directly()); 85 | vec.append(&mut self.acquisition_end.encode_directly()); 86 | vec.append(&mut self.hash_header.encode_directly()); 87 | vec.append(&mut self.first_chunk_number.encode_directly()); 88 | vec.append(&mut self.number_of_chunks.encode_directly()); 89 | vec.append(&mut self.length_of_data.encode_directly()); 90 | vec 91 | } 92 | 93 | /// encrypts the file footer by the given encryption information and returns the encrypted file footer. 94 | /// # Error 95 | /// The method returns an error, if the encryption fails. 96 | pub fn encode_encrypted_header_directly(&self, encryption_information: E) -> Result> 97 | where 98 | E: Borrow 99 | { 100 | let mut vec = Vec::new(); 101 | let encryption_information = encryption_information.borrow(); 102 | let mut encoded_footer = self.encode_encrypted_footer(&encryption_information.encryption_key, &encryption_information.algorithm)?; 103 | let identifier = Self::identifier(); 104 | let encoded_header_length = 4 + 8 + (encoded_footer.len() as u64); //4 bytes identifier + 8 bytes for length + length itself 105 | vec.append(&mut identifier.to_be_bytes().to_vec()); 106 | vec.append(&mut encoded_header_length.to_le_bytes().to_vec()); 107 | vec.append(&mut encoded_footer); 108 | 109 | Ok(vec) 110 | } 111 | 112 | fn encode_encrypted_footer(&self, key: K, algorithm: A) -> Result> 113 | where 114 | K: AsRef<[u8]>, 115 | A: Borrow, 116 | { 117 | let mut vec = Vec::new(); 118 | vec.append(&mut Self::version().encode_directly()); 119 | vec.append(&mut self.file_number.encode_directly()); 120 | 121 | let mut data_to_encrypt = Vec::new(); 122 | data_to_encrypt.append(&mut self.encode_content()); 123 | 124 | let encrypted_data = FileFooter::encrypt( 125 | key, data_to_encrypt, 126 | self.file_number, 127 | algorithm 128 | )?; 129 | vec.append(&mut encrypted_data.encode_directly()); 130 | Ok(vec) 131 | } 132 | 133 | /// decodes the encrypted header with the given key and [crate::header::EncryptionHeader]. 134 | /// The appropriate [crate::header::EncryptionHeader] has to be stored in the appropriate [crate::header::ObjectHeader]. 135 | pub fn decode_encrypted_footer_with_key(data: &mut R, encryption_information: E) -> Result 136 | where 137 | R: Read, 138 | E: Borrow 139 | { 140 | if !Self::check_identifier(data) { 141 | return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER)); 142 | }; 143 | let header_length = Self::decode_header_length(data)? as usize; 144 | let mut header_content = vec![0u8; header_length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH]; 145 | data.read_exact(&mut header_content)?; 146 | let mut cursor = Cursor::new(header_content); 147 | Self::check_version(&mut cursor)?; 148 | let file_number = u64::decode_directly(&mut cursor)?; 149 | let encrypted_data = Vec::::decode_directly(&mut cursor)?; 150 | let algorithm = &encryption_information.borrow().algorithm; 151 | let decrypted_data = FileFooter::decrypt( 152 | &encryption_information.borrow().encryption_key, 153 | encrypted_data, 154 | file_number, 155 | algorithm)?; 156 | let mut cursor = Cursor::new(decrypted_data); 157 | let (acquisition_start, acquisition_end, hash_header, first_chunk_number, number_of_chunks, length_of_data) = Self::decode_inner_content(&mut cursor)?; 158 | Ok(FileFooter::new(file_number, acquisition_start, acquisition_end, hash_header, first_chunk_number, number_of_chunks, length_of_data)) 159 | } 160 | 161 | #[allow(clippy::type_complexity)] 162 | fn decode_inner_content(inner_content: &mut R) -> Result<( 163 | u64, //acquisition_start 164 | u64, //acquisition_end 165 | HashHeader, //HashHeader 166 | u64, //first_chunk_number 167 | u64, // number_of_chunks, 168 | u64, // length_of_data 169 | )> { 170 | let acquisition_start = u64::decode_directly(inner_content)?; 171 | let acquisition_end = u64::decode_directly(inner_content)?; 172 | let hash_header = HashHeader::decode_directly(inner_content)?; 173 | let first_chunk_number = u64::decode_directly(inner_content)?; 174 | let number_of_chunks = u64::decode_directly(inner_content)?; 175 | let length_of_data = u64::decode_directly(inner_content)?; 176 | 177 | let inner_content = ( 178 | acquisition_start, 179 | acquisition_end, 180 | hash_header, 181 | first_chunk_number, 182 | number_of_chunks, 183 | length_of_data); 184 | Ok(inner_content) 185 | } 186 | } 187 | 188 | impl HeaderCoding for FileFooter { 189 | type Item = FileFooter; 190 | fn version() -> u8 { 191 | DEFAULT_FOOTER_VERSION_FILE_FOOTER 192 | } 193 | fn identifier() -> u32 { 194 | FOOTER_IDENTIFIER_FILE_FOOTER 195 | } 196 | fn encode_header(&self) -> Vec { 197 | let mut vec = vec![Self::version()]; 198 | vec.append(&mut self.file_number.encode_directly()); 199 | vec.append(&mut self.encode_content()); 200 | vec 201 | } 202 | fn decode_content(data: Vec) -> Result { 203 | let mut cursor = Cursor::new(data); 204 | Self::check_version(&mut cursor)?; 205 | let file_number = u64::decode_directly(&mut cursor)?; 206 | let (acquisition_start, acquisition_end, hash_header, first_chunk_number, number_of_chunks, length_of_data) = Self::decode_inner_content(&mut cursor)?; 207 | Ok(FileFooter::new(file_number, acquisition_start, acquisition_end, hash_header, first_chunk_number, number_of_chunks, length_of_data)) 208 | } 209 | 210 | fn struct_name() -> &'static str { 211 | "FileFooter" 212 | } 213 | } 214 | 215 | // - implement fmt::Display 216 | impl fmt::Display for FileFooter { 217 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 218 | write!(f, "{}", Self::struct_name()) 219 | } 220 | } 221 | 222 | impl Encryption for FileFooter { 223 | fn crypto_nonce_padding() -> u8 { 224 | 0b00001000 //TODO: move all crypto paddings to constants (#codeCleanup) 225 | } 226 | } -------------------------------------------------------------------------------- /src/lib/footer/main_footer.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::Cursor; 3 | use std::collections::BTreeMap; 4 | use std::fmt; 5 | 6 | // - internal 7 | use crate::{ 8 | HeaderCoding, 9 | Result, 10 | ValueDecoder, 11 | ValueEncoder, 12 | ZffErrorKind, 13 | ENCODING_KEY_DESCRIPTION_NOTES, 14 | FOOTER_IDENTIFIER_MAIN_FOOTER, 15 | DEFAULT_FOOTER_VERSION_MAIN_FOOTER 16 | }; 17 | 18 | // - external 19 | #[cfg(feature = "serde")] 20 | use serde::{ 21 | Deserialize, 22 | Serialize, 23 | }; 24 | 25 | 26 | /// The main footer is the last thing, which is written at the end of the last segment.\ 27 | /// This footer contains a lot of variable information about the zff container (e.g. number of segments, ...). 28 | #[derive(Debug,Clone, Eq, PartialEq, Default)] 29 | #[cfg_attr(feature = "serde", derive(Deserialize))] 30 | #[cfg_attr(feature = "serde", derive(Serialize))] 31 | pub struct MainFooter { 32 | /// the total number of segments for this container 33 | pub number_of_segments: u64, 34 | /// the segment numbers where the appropriate object header can be found. 35 | pub object_header: BTreeMap, // 36 | /// the segment numbers where the appropriate object footer can be found. 37 | pub object_footer: BTreeMap, // 38 | /// the segment numbers where the appropriate chunkmap can be found. 39 | pub chunk_header_maps: BTreeMap, // 40 | /// The segment numbers where the appropriate chunkmap can be found. 41 | pub chunk_samebytes_maps: BTreeMap, // 42 | /// The segment numbers where the appropriate chunkmap can be found. 43 | pub chunk_dedup_maps: BTreeMap, // 44 | /// some optional (globally) description notes for the container. 45 | pub description_notes: Option, 46 | /// offset in the current segment, where the footer starts. 47 | pub footer_offset: u64, 48 | } 49 | 50 | impl MainFooter { 51 | /// creates a new MainFooter with a given values. 52 | pub fn new( 53 | number_of_segments: u64, 54 | object_header: BTreeMap, 55 | object_footer: BTreeMap, 56 | chunk_header_maps: BTreeMap, 57 | chunk_samebytes_maps: BTreeMap, 58 | chunk_dedup_maps: BTreeMap, 59 | description_notes: Option, 60 | footer_offset: u64) -> MainFooter { 61 | Self { 62 | number_of_segments, 63 | object_header, 64 | object_footer, 65 | chunk_header_maps, 66 | chunk_samebytes_maps, 67 | chunk_dedup_maps, 68 | description_notes, 69 | footer_offset, 70 | } 71 | } 72 | 73 | /// sets the number of segments of the appropriate zff container. 74 | pub fn set_number_of_segments(&mut self, number: u64) { 75 | self.number_of_segments = number 76 | } 77 | 78 | /// returns the number of segments of the appropriate zff container. 79 | pub fn number_of_segments(&self) -> u64 { 80 | self.number_of_segments 81 | } 82 | 83 | /// adds a new combination to the inner object-header hashmap. 84 | pub fn add_object_header(&mut self, object_number: u64, segment_no: u64) { 85 | self.object_header.insert(object_number, segment_no); 86 | } 87 | 88 | /// returns the inner hashmap of object-header. 89 | pub fn object_header(&self) -> &BTreeMap { 90 | &self.object_header 91 | } 92 | 93 | /// adds a new combination to the inner object-footer hashmap. 94 | pub fn add_object_footer(&mut self, object_number: u64, segment_no: u64) { 95 | self.object_footer.insert(object_number, segment_no); 96 | } 97 | 98 | /// returns the inner hashmap of object-footer. 99 | pub fn object_footer(&self) -> &BTreeMap { 100 | &self.object_footer 101 | } 102 | 103 | /// sets the start offset of this main footer. 104 | pub fn set_footer_offset(&mut self, offset: u64) { 105 | self.footer_offset = offset 106 | } 107 | 108 | /// returns the start offset of this main footer. 109 | pub fn footer_offset(&self) -> u64 { 110 | self.footer_offset 111 | } 112 | 113 | /// returns the description notes of the zff container (Not to be mixed up with the "notes" which can be created in the description header of each object!)). 114 | pub fn description_notes(&self) -> Option<&str> { 115 | Some(self.description_notes.as_ref()?) 116 | } 117 | 118 | /// Returns a reference of the global chunk samebytes table. 119 | pub fn chunk_samebytes_maps(&self) -> &BTreeMap { 120 | &self.chunk_samebytes_maps 121 | } 122 | 123 | /// Returns a reference of the global chunk deduplication table. 124 | pub fn chunk_dedup_maps(&self) -> &BTreeMap { 125 | &self.chunk_dedup_maps 126 | } 127 | } 128 | 129 | impl HeaderCoding for MainFooter { 130 | type Item = MainFooter; 131 | 132 | fn identifier() -> u32 { 133 | FOOTER_IDENTIFIER_MAIN_FOOTER 134 | } 135 | 136 | fn version() -> u8 { 137 | DEFAULT_FOOTER_VERSION_MAIN_FOOTER 138 | } 139 | 140 | fn encode_header(&self) -> Vec { 141 | let mut vec = Vec::new(); 142 | vec.append(&mut Self::version().encode_directly()); 143 | vec.append(&mut self.number_of_segments.encode_directly()); 144 | vec.append(&mut self.object_header.encode_directly()); 145 | vec.append(&mut self.object_footer.encode_directly()); 146 | vec.append(&mut self.chunk_header_maps.encode_directly()); 147 | vec.append(&mut self.chunk_samebytes_maps.encode_directly()); 148 | vec.append(&mut self.chunk_dedup_maps.encode_directly()); 149 | if let Some(description_notes) = &self.description_notes { 150 | vec.append(&mut description_notes.encode_for_key(ENCODING_KEY_DESCRIPTION_NOTES)); 151 | }; 152 | vec.append(&mut self.footer_offset.encode_directly()); 153 | vec 154 | } 155 | 156 | fn decode_content(data: Vec) -> Result { 157 | let mut cursor = Cursor::new(data); 158 | Self::check_version(&mut cursor)?; 159 | let number_of_segments = u64::decode_directly(&mut cursor)?; 160 | let object_header = BTreeMap::::decode_directly(&mut cursor)?; 161 | let object_footer = BTreeMap::::decode_directly(&mut cursor)?; 162 | let chunk_header_maps = BTreeMap::::decode_directly(&mut cursor)?; 163 | let chunk_samebytes_maps = BTreeMap::::decode_directly(&mut cursor)?; 164 | let chunk_dedup_maps = BTreeMap::::decode_directly(&mut cursor)?; 165 | let position = cursor.position(); 166 | let description_notes = match String::decode_for_key(&mut cursor, ENCODING_KEY_DESCRIPTION_NOTES) { 167 | Ok(value) => Some(value), 168 | Err(e) => match e.kind_ref() { 169 | ZffErrorKind::KeyNotOnPosition => { 170 | cursor.set_position(position); 171 | None 172 | }, 173 | _ => return Err(e) 174 | }, 175 | }; 176 | let footer_offset = u64::decode_directly(&mut cursor)?; 177 | Ok(MainFooter::new( 178 | number_of_segments, 179 | object_header, 180 | object_footer, 181 | chunk_header_maps, 182 | chunk_samebytes_maps, 183 | chunk_dedup_maps, 184 | description_notes, 185 | footer_offset)) 186 | } 187 | 188 | fn struct_name() -> &'static str { 189 | "MainFooter" 190 | } 191 | } 192 | 193 | // - implement fmt::Display 194 | impl fmt::Display for MainFooter { 195 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 196 | write!(f, "{}", Self::struct_name()) 197 | } 198 | } -------------------------------------------------------------------------------- /src/lib/footer/mod.rs: -------------------------------------------------------------------------------- 1 | // - modules 2 | mod main_footer; 3 | mod segment_footer; 4 | mod file_footer; 5 | mod object_footer; 6 | 7 | // - re-exports -- this section contains the footer of the current zff version. 8 | pub use main_footer::*; 9 | pub use segment_footer::*; 10 | pub use file_footer::*; 11 | pub use object_footer::*; -------------------------------------------------------------------------------- /src/lib/footer/object_footer/mod.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use core::borrow::Borrow; 3 | use std::io::{Cursor, Read}; 4 | use std::collections::HashMap; 5 | use std::fmt; 6 | 7 | // - internal 8 | use crate::{ 9 | Result, 10 | HeaderCoding, 11 | ValueDecoder, 12 | ValueEncoder, 13 | ZffError, 14 | ZffErrorKind, 15 | Encryption, 16 | EncryptionAlgorithm, 17 | constants::*, 18 | }; 19 | use crate::header::{ 20 | HashHeader, 21 | EncryptionInformation, 22 | }; 23 | 24 | // - modules 25 | mod object_footer_physical; 26 | mod object_footer_logical; 27 | mod object_footer_virtual; 28 | 29 | // - re-exports 30 | pub use object_footer_physical::*; 31 | pub use object_footer_logical::*; 32 | pub use object_footer_virtual::*; 33 | 34 | // - external 35 | use byteorder::{LittleEndian, BigEndian, ReadBytesExt}; 36 | #[cfg(feature = "serde")] 37 | use serde::{ 38 | Deserialize, 39 | Serialize, 40 | }; 41 | 42 | 43 | /// Each object contains its own object footer. 44 | #[derive(Debug, Clone, PartialEq, Eq)] 45 | #[cfg_attr(feature = "serde", derive(Serialize))] 46 | pub enum ObjectFooter { 47 | /// A physical object contains a [ObjectFooterPhysical]. 48 | Physical(ObjectFooterPhysical), 49 | /// A logical object contains a [ObjectFooterLogical]. 50 | Logical(ObjectFooterLogical), 51 | /// Footer of a virtual object. 52 | Virtual(ObjectFooterVirtual), 53 | } 54 | 55 | impl ObjectFooter { 56 | /// returns the version of the object footer. 57 | pub fn version(&self) -> u8 { 58 | match self { 59 | ObjectFooter::Physical(_) => ObjectFooterPhysical::version(), 60 | ObjectFooter::Logical(_) => ObjectFooterLogical::version(), 61 | ObjectFooter::Virtual(_) => ObjectFooterVirtual::version(), 62 | } 63 | } 64 | 65 | /// checks if the identifier matches to an physical or logical object footer. 66 | /// Returns 1 for a physical object footer, 2 for a logical object footer and 0 if neither applies. 67 | fn check_identifier(data: &mut R) -> u8 { 68 | let identifier = match data.read_u32::() { 69 | Ok(val) => val, 70 | Err(_) => return 0, 71 | }; 72 | if identifier == ObjectFooterPhysical::identifier() { 73 | 1 74 | } else if identifier == ObjectFooterLogical::identifier() { 75 | 2 76 | } else { 77 | 0 78 | } 79 | } 80 | 81 | /// decodes the length of the header. 82 | fn decode_header_length(data: &mut R) -> Result { 83 | match data.read_u64::() { 84 | Ok(value) => Ok(value), 85 | Err(_) => Err(ZffError::new(ZffErrorKind::EncodingError, ERROR_HEADER_DECODER_HEADER_LENGTH)), 86 | } 87 | } 88 | 89 | /// Reads the data from the given [Reader](std::io::Read) and returns a decoded object footer. 90 | /// Returns an error, if the decoding process fails. 91 | pub fn decode_directly(data: &mut R) -> Result { 92 | match Self::check_identifier(data) { 93 | 1 => { 94 | let length = Self::decode_header_length(data)? as usize; 95 | let mut content_buffer = vec![0u8; length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH]; 96 | data.read_exact(&mut content_buffer)?; 97 | Ok(ObjectFooter::Physical(ObjectFooterPhysical::decode_content(content_buffer)?)) 98 | }, 99 | 2 => { 100 | let length = Self::decode_header_length(data)? as usize; 101 | let mut content_buffer = vec![0u8; length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH]; 102 | data.read_exact(&mut content_buffer)?; 103 | Ok(ObjectFooter::Logical(ObjectFooterLogical::decode_content(content_buffer)?)) 104 | }, 105 | _ => Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER)), 106 | } 107 | } 108 | 109 | /// Returns the appropriate object number. 110 | pub fn object_number(&self) -> u64 { 111 | match self { 112 | ObjectFooter::Physical(footer) => footer.object_number, 113 | ObjectFooter::Logical(footer) => footer.object_number, 114 | ObjectFooter::Virtual(footer) => footer.object_number, 115 | } 116 | } 117 | 118 | /// Returns the appropriate acquisition start timestamp. 119 | pub fn acquisition_start(&self) -> u64 { 120 | match self { 121 | ObjectFooter::Physical(footer) => footer.acquisition_start, 122 | ObjectFooter::Logical(footer) => footer.acquisition_start, 123 | ObjectFooter::Virtual(footer) => footer.creation_timestamp, 124 | } 125 | } 126 | 127 | /// Returns the appropriate acquisition end timestamp. 128 | pub fn acquisition_end(&self) -> u64 { 129 | match self { 130 | ObjectFooter::Physical(footer) => footer.acquisition_end, 131 | ObjectFooter::Logical(footer) => footer.acquisition_end, 132 | ObjectFooter::Virtual(footer) => footer.creation_timestamp, 133 | } 134 | } 135 | } 136 | 137 | impl From for ObjectFooter { 138 | fn from(footer: ObjectFooterPhysical) -> Self { 139 | ObjectFooter::Physical(footer) 140 | } 141 | } 142 | 143 | impl From for ObjectFooter { 144 | fn from(footer: ObjectFooterLogical) -> Self { 145 | ObjectFooter::Logical(footer) 146 | } 147 | } 148 | 149 | // - implement fmt::Display 150 | impl fmt::Display for ObjectFooter { 151 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 152 | write!(f, "{}", Self::struct_name()) 153 | } 154 | } 155 | 156 | // - this is a necassary helper method for fmt::Display and serde::ser::SerializeStruct. 157 | impl ObjectFooter { 158 | fn struct_name() -> &'static str { 159 | "ObjectFooter" 160 | } 161 | } 162 | 163 | impl Encryption for ObjectFooter { 164 | fn crypto_nonce_padding() -> u8 { 165 | 0b00100000 166 | } 167 | } 168 | 169 | 170 | /// Each object contains its own object footer (and this is the encrypted variant). 171 | #[derive(Debug, Clone, PartialEq, Eq)] 172 | #[cfg_attr(feature = "serde", derive(Serialize))] 173 | #[cfg_attr(feature = "serde", derive(Deserialize))] 174 | pub enum EncryptedObjectFooter { 175 | /// A physical object contains a [EncryptedObjectFooterPhysical]. 176 | Physical(EncryptedObjectFooterPhysical), 177 | /// A logical object contains a [EncryptedObjectFooterLogical]. 178 | Logical(EncryptedObjectFooterLogical), 179 | } 180 | 181 | impl EncryptedObjectFooter { 182 | /// returns the version of the object footer. 183 | pub fn version(&self) -> u8 { 184 | match self { 185 | EncryptedObjectFooter::Physical(_) => ObjectFooterPhysical::version(), 186 | EncryptedObjectFooter::Logical(_) => ObjectFooterLogical::version(), 187 | } 188 | } 189 | 190 | /// checks if the identifier matches to an physical or logical object footer. Returns 1 for a physical object footer, 2 for a logical object footer and 0 if neither applies. 191 | fn check_identifier(data: &mut R) -> u8 { 192 | let identifier = match data.read_u32::() { 193 | Ok(val) => val, 194 | Err(_) => return 0, 195 | }; 196 | if identifier == EncryptedObjectFooterPhysical::identifier() { 197 | 1 198 | } else if identifier == EncryptedObjectFooterLogical::identifier() { 199 | 2 200 | } else { 201 | 0 202 | } 203 | } 204 | 205 | /// decodes the length of the header. 206 | fn decode_header_length(data: &mut R) -> Result { 207 | match data.read_u64::() { 208 | Ok(value) => Ok(value), 209 | Err(_) => Err(ZffError::new(ZffErrorKind::EncodingError, ERROR_HEADER_DECODER_HEADER_LENGTH)), 210 | } 211 | } 212 | 213 | /// Reads the data from the given [Reader](std::io::Read) and returns a decoded object footer. 214 | /// Returns an error, if the decoding process fails. 215 | pub fn decode_directly(data: &mut R) -> Result { 216 | match Self::check_identifier(data) { 217 | 1 => { 218 | let length = Self::decode_header_length(data)? as usize; 219 | let mut content_buffer = vec![0u8; length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH]; 220 | data.read_exact(&mut content_buffer)?; 221 | Ok(EncryptedObjectFooter::Physical(EncryptedObjectFooterPhysical::decode_content(content_buffer)?)) 222 | }, 223 | 2 => { 224 | let length = Self::decode_header_length(data)? as usize; 225 | let mut content_buffer = vec![0u8; length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH]; 226 | data.read_exact(&mut content_buffer)?; 227 | Ok(EncryptedObjectFooter::Logical(EncryptedObjectFooterLogical::decode_content(content_buffer)?)) 228 | }, 229 | _ => Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER)), 230 | } 231 | } 232 | 233 | /// tries to decrypt the ObjectFooter. If an error occures, the EncryptedObjectFooter is still available. 234 | pub fn decrypt(&self, key: K, algorithm: A) -> Result 235 | where 236 | A: Borrow, 237 | K: AsRef<[u8]>, 238 | { 239 | match self { 240 | EncryptedObjectFooter::Physical(encrypted_inner_footer) => { 241 | let decrypted_footer = encrypted_inner_footer.decrypt(key, algorithm)?; 242 | Ok(ObjectFooter::from(decrypted_footer)) 243 | }, 244 | EncryptedObjectFooter::Logical(encrypted_inner_footer) => { 245 | let decrypted_footer = encrypted_inner_footer.decrypt(key, algorithm)?; 246 | Ok(ObjectFooter::from(decrypted_footer)) 247 | } 248 | } 249 | } 250 | 251 | /// tries to decrypt the ObjectFooter. Consumes the EncryptedObjectFooter, regardless of whether an error occurs or not. 252 | pub fn decrypt_and_consume(self, key: K, algorithm: A) -> Result 253 | where 254 | A: Borrow, 255 | K: AsRef<[u8]>, 256 | { 257 | self.decrypt(key, algorithm) 258 | } 259 | } -------------------------------------------------------------------------------- /src/lib/footer/object_footer/object_footer_physical.rs: -------------------------------------------------------------------------------- 1 | // - internal 2 | use super::*; 3 | 4 | /// An [ObjectFooterPhysical] is written at the end of each physical object. 5 | /// This footer contains various information about the acquisition process: 6 | /// - the acquisition start time 7 | /// - the acquisition start time 8 | /// - the size of the (uncompressed and unencrypted) underlying data 9 | /// - the first chunk number, which is used for this physical dump 10 | /// - the total number of chunks, used for this physical dump 11 | /// - a hash header with the appropriate hash values of the underlying physical dump 12 | #[derive(Debug,Clone, PartialEq, Eq)] 13 | #[cfg_attr(feature = "serde", derive(Serialize))] 14 | pub struct ObjectFooterPhysical { 15 | /// The object number of the footer. 16 | pub object_number: u64, 17 | /// The acquisition start timestamp of the footer. 18 | pub acquisition_start: u64, 19 | /// The acquisition end timestamp of the footer. 20 | pub acquisition_end: u64, 21 | /// The original length of the data. 22 | pub length_of_data: u64, 23 | /// The first used chunk number in this object. 24 | pub first_chunk_number: u64, 25 | /// The total number of chunks used in this object. 26 | pub number_of_chunks: u64, 27 | /// The appropriate [crate::header::HashHeader]. 28 | pub hash_header: HashHeader, 29 | } 30 | 31 | impl ObjectFooterPhysical { 32 | /// creates a new [ObjectFooterPhysical] with the given values. 33 | pub fn new( 34 | object_number: u64, 35 | acquisition_start: u64, 36 | acquisition_end: u64, 37 | length_of_data: u64, 38 | first_chunk_number: u64, 39 | number_of_chunks: u64, 40 | hash_header: HashHeader) -> ObjectFooterPhysical { 41 | Self { 42 | object_number, 43 | acquisition_start, 44 | acquisition_end, 45 | length_of_data, 46 | first_chunk_number, 47 | number_of_chunks, 48 | hash_header, 49 | } 50 | } 51 | 52 | fn encode_content(&self) -> Vec { 53 | let mut vec = Vec::new(); 54 | vec.append(&mut self.acquisition_start.encode_directly()); 55 | vec.append(&mut self.acquisition_end.encode_directly()); 56 | vec.append(&mut self.length_of_data.encode_directly()); 57 | vec.append(&mut self.first_chunk_number.encode_directly()); 58 | vec.append(&mut self.number_of_chunks.encode_directly()); 59 | vec.append(&mut self.hash_header.encode_directly()); 60 | vec 61 | } 62 | 63 | /// encrypts the object footer by the given encryption information and returns the encrypted object footer. 64 | pub fn encrypt_directly(&self, encryption_information: E) -> Result> 65 | where 66 | E: Borrow 67 | { 68 | let mut vec = Vec::new(); 69 | let encrypted_content = ObjectFooter::encrypt( 70 | &encryption_information.borrow().encryption_key, 71 | self.encode_content(), 72 | self.object_number, 73 | &encryption_information.borrow().algorithm)?; 74 | let identifier = Self::identifier(); 75 | let encoded_header_length = ( 76 | DEFAULT_LENGTH_HEADER_IDENTIFIER + 77 | DEFAULT_LENGTH_VALUE_HEADER_LENGTH + 78 | Self::version().encode_directly().len() + 79 | self.object_number.encode_directly().len() + 80 | true.encode_directly().len() + 81 | encrypted_content.encode_directly().len()) as u64; //4 bytes identifier + 8 bytes for length + length itself 82 | vec.append(&mut identifier.to_be_bytes().to_vec()); 83 | vec.append(&mut encoded_header_length.encode_directly()); 84 | vec.append(&mut Self::version().encode_directly()); 85 | vec.append(&mut self.object_number.encode_directly()); 86 | vec.append(&mut true.encode_directly()); // encryption flag 87 | vec.append(&mut encrypted_content.encode_directly()); 88 | 89 | Ok(vec) 90 | } 91 | 92 | pub(crate) fn decode_inner_content(data: &mut R) -> Result<( 93 | u64, //acquisition_start 94 | u64, //acquisition_end 95 | u64, //length_of_data 96 | u64, //first_chunk_number 97 | u64, //number_of_chunks 98 | HashHeader // hash_header 99 | )> { 100 | let acquisition_start = u64::decode_directly(data)?; 101 | let acquisition_end = u64::decode_directly(data)?; 102 | let length_of_data = u64::decode_directly(data)?; 103 | let first_chunk_number = u64::decode_directly(data)?; 104 | let number_of_chunks = u64::decode_directly(data)?; 105 | let hash_header = HashHeader::decode_directly(data)?; 106 | Ok(( 107 | acquisition_start, 108 | acquisition_end, 109 | length_of_data, 110 | first_chunk_number, 111 | number_of_chunks, 112 | hash_header 113 | )) 114 | } 115 | } 116 | 117 | // - implement fmt::Display 118 | impl fmt::Display for ObjectFooterPhysical { 119 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 120 | write!(f, "{}", Self::struct_name()) 121 | } 122 | } 123 | 124 | impl HeaderCoding for ObjectFooterPhysical { 125 | type Item = ObjectFooterPhysical; 126 | fn version() -> u8 { 127 | DEFAULT_FOOTER_VERSION_OBJECT_FOOTER_PHYSICAL 128 | } 129 | fn identifier() -> u32 { 130 | FOOTER_IDENTIFIER_OBJECT_FOOTER_PHYSICAL 131 | } 132 | fn encode_header(&self) -> Vec { 133 | let mut vec = vec![Self::version()]; 134 | vec.append(&mut self.object_number.encode_directly()); 135 | vec.append(&mut false.encode_directly()); // encryption flag 136 | vec.append(&mut self.encode_content()); 137 | vec 138 | } 139 | fn decode_content(data: Vec) -> Result { 140 | let mut cursor = Cursor::new(data); 141 | Self::check_version(&mut cursor)?; 142 | let object_number = u64::decode_directly(&mut cursor)?; 143 | let encryption_flag = bool::decode_directly(&mut cursor)?; 144 | if encryption_flag { 145 | return Err(ZffError::new(ZffErrorKind::Missing, ERROR_MISSING_ENCRYPTION_HEADER_KEY)); 146 | } 147 | let (acquisition_start, 148 | acquisition_end, 149 | length_of_data, 150 | first_chunk_number, 151 | number_of_chunks, 152 | hash_header) = Self::decode_inner_content(&mut cursor)?; 153 | Ok(ObjectFooterPhysical::new( 154 | object_number, 155 | acquisition_start, 156 | acquisition_end, 157 | length_of_data, 158 | first_chunk_number, 159 | number_of_chunks, 160 | hash_header)) 161 | } 162 | 163 | fn struct_name() -> &'static str { 164 | "ObjectFooterPhysical" 165 | } 166 | } 167 | 168 | /// Represents an encrypted object footer of a physical object. 169 | #[derive(Debug, Clone, PartialEq, Eq)] 170 | #[cfg_attr(feature = "serde", derive(Serialize))] 171 | #[cfg_attr(feature = "serde", derive(Deserialize))] 172 | pub struct EncryptedObjectFooterPhysical { 173 | /// The appropriate object number. 174 | pub object_number: u64, 175 | /// The underlying data in encrypted form. 176 | pub encrypted_data: Vec, 177 | } 178 | 179 | impl EncryptedObjectFooterPhysical { 180 | /// Creates a new [EncryptedObjectFooterPhysical] by the given values. 181 | pub fn new(object_number: u64, encrypted_data: Vec) -> Self { 182 | Self { 183 | object_number, 184 | encrypted_data, 185 | } 186 | } 187 | 188 | /// Tries to decrypt the ObjectFooter. If an error occures, the EncryptedObjectFooterPhysical is still available. 189 | pub fn decrypt(&self, key: K, algorithm: A) -> Result 190 | where 191 | A: Borrow, 192 | K: AsRef<[u8]>, 193 | { 194 | let content = ObjectFooter::decrypt(key, &self.encrypted_data, self.object_number, algorithm.borrow())?; 195 | let mut cursor = Cursor::new(content); 196 | let (acquisition_start, 197 | acquisition_end, 198 | length_of_data, 199 | first_chunk_number, 200 | number_of_chunks, 201 | hash_header) = ObjectFooterPhysical::decode_inner_content(&mut cursor)?; 202 | Ok(ObjectFooterPhysical::new( 203 | self.object_number, 204 | acquisition_start, 205 | acquisition_end, 206 | length_of_data, 207 | first_chunk_number, 208 | number_of_chunks, 209 | hash_header)) 210 | } 211 | 212 | /// Tries to decrypt the ObjectFooter. Consumes the EncryptedObjectFooterPhysical, regardless of whether an error occurs or not. 213 | pub fn decrypt_and_consume(self, key: K, algorithm: A) -> Result 214 | where 215 | A: Borrow, 216 | K: AsRef<[u8]>, 217 | { 218 | self.decrypt(key, algorithm) 219 | } 220 | } 221 | 222 | // - implement fmt::Display 223 | impl fmt::Display for EncryptedObjectFooterPhysical { 224 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 225 | write!(f, "{}", Self::struct_name()) 226 | } 227 | } 228 | 229 | impl HeaderCoding for EncryptedObjectFooterPhysical { 230 | type Item = EncryptedObjectFooterPhysical; 231 | fn version() -> u8 { 232 | DEFAULT_FOOTER_VERSION_OBJECT_FOOTER_PHYSICAL 233 | } 234 | fn identifier() -> u32 { 235 | FOOTER_IDENTIFIER_OBJECT_FOOTER_PHYSICAL 236 | } 237 | fn encode_header(&self) -> Vec { 238 | let mut vec = vec![Self::version()]; 239 | vec.append(&mut self.object_number.encode_directly()); 240 | vec.append(&mut true.encode_directly()); // encryption flag 241 | vec.append(&mut self.encrypted_data.encode_directly()); 242 | vec 243 | } 244 | fn decode_content(data: Vec) -> Result { 245 | let mut cursor = Cursor::new(data); 246 | Self::check_version(&mut cursor)?; 247 | let object_number = u64::decode_directly(&mut cursor)?; 248 | let encryption_flag = bool::decode_directly(&mut cursor)?; 249 | if !encryption_flag { 250 | return Err(ZffError::new( 251 | ZffErrorKind::EncryptionError, 252 | ERROR_DECODE_UNENCRYPTED_OBJECT_WITH_DECRYPTION_FN)); 253 | } 254 | let encrypted_data = Vec::::decode_directly(&mut cursor)?; 255 | Ok(Self::new( 256 | object_number, 257 | encrypted_data)) 258 | } 259 | 260 | fn struct_name() -> &'static str { 261 | "EncryptedObjectFooterPhysical" 262 | } 263 | } -------------------------------------------------------------------------------- /src/lib/footer/object_footer/object_footer_virtual.rs: -------------------------------------------------------------------------------- 1 | // - internal 2 | use super::*; 3 | 4 | /// An [ObjectFooterVirtual] is written at the end of an virtual object. 5 | /// This footer contains various information about the acquisition process: 6 | /// - the acquisition start time 7 | /// - the acquisition start time 8 | /// - the size of the (uncompressed and unencrypted) underlying data 9 | /// - the first chunk number, which is used for this physical dump 10 | /// - the total number of chunks, used for this physical dump 11 | /// - a hash header with the appropriate hash values of the underlying physical dump 12 | #[derive(Debug,Clone, PartialEq, Eq)] 13 | #[cfg_attr(feature = "serde", derive(Serialize))] 14 | pub struct ObjectFooterVirtual { 15 | /// The object number of the footer. 16 | pub object_number: u64, 17 | /// UNIX timestamp when the creation of this objects has started. 18 | pub creation_timestamp: u64, 19 | /// A list of all objects which are affected by this virtual object. 20 | pub passive_objects: Vec, 21 | /// The length of the original data in bytes. 22 | pub length_of_data: u64, 23 | /// The map of to the highest layer. 24 | pub virtual_object_map_offset: u64, 25 | /// The map of the segments to the appropriate next layer 26 | pub virtual_object_map_segment_no: u64, 27 | } 28 | 29 | impl ObjectFooterVirtual { 30 | /// creates a new [ObjectFooterVirtual] with the given values. 31 | pub fn with_data(object_number: u64, 32 | creation_timestamp: u64, 33 | passive_objects: Vec, 34 | length_of_data: u64, 35 | virtual_object_map_offset: u64, 36 | virtual_object_map_segment_no: u64) -> Self { 37 | Self { 38 | object_number, 39 | creation_timestamp, 40 | passive_objects, 41 | length_of_data, 42 | virtual_object_map_offset, 43 | virtual_object_map_segment_no, 44 | } 45 | } 46 | 47 | fn encode_content(&self) -> Vec { 48 | let mut vec = Vec::new(); 49 | vec.append(&mut self.creation_timestamp.encode_directly()); 50 | vec.append(&mut self.passive_objects.encode_directly()); 51 | vec.append(&mut self.length_of_data.encode_directly()); 52 | vec.append(&mut self.virtual_object_map_offset.encode_directly()); 53 | vec.append(&mut self.virtual_object_map_segment_no.encode_directly()); 54 | vec 55 | } 56 | 57 | /// encrypts the object footer by the given encryption information and returns the encrypted object footer. 58 | pub fn encrypt_directly(&self, encryption_information: E) -> Result> 59 | where 60 | E: Borrow 61 | { 62 | let mut vec = Vec::new(); 63 | let encrypted_content = ObjectFooter::encrypt( 64 | &encryption_information.borrow().encryption_key, 65 | self.encode_content(), 66 | self.object_number, 67 | &encryption_information.borrow().algorithm)?; 68 | let identifier = Self::identifier(); 69 | let encoded_header_length = ( 70 | DEFAULT_LENGTH_HEADER_IDENTIFIER + 71 | DEFAULT_LENGTH_VALUE_HEADER_LENGTH + 72 | Self::version().encode_directly().len() + 73 | self.object_number.encode_directly().len() + 74 | true.encode_directly().len() + 75 | encrypted_content.encode_directly().len()) as u64; //4 bytes identifier + 8 bytes for length + length itself 76 | vec.append(&mut identifier.to_be_bytes().to_vec()); 77 | vec.append(&mut encoded_header_length.encode_directly()); 78 | vec.append(&mut Self::version().encode_directly()); 79 | vec.append(&mut self.object_number.encode_directly()); 80 | vec.append(&mut true.encode_directly()); // encryption flag 81 | vec.append(&mut encrypted_content.encode_directly()); 82 | 83 | Ok(vec) 84 | } 85 | 86 | #[allow(clippy::type_complexity)] 87 | fn decode_inner_content(data: &mut R) -> Result<( 88 | u64, //creation_timestamp 89 | Vec, //passive_objects 90 | u64, //length_of_data 91 | u64, //virtual_object_map_offset 92 | u64, //virtual_object_map_segment_no, 93 | )> { 94 | let creation_timestamp = u64::decode_directly(data)?; 95 | let passive_objects = Vec::::decode_directly(data)?; 96 | let length_of_data = u64::decode_directly(data)?; 97 | let virtual_object_map_offset = u64::decode_directly(data)?; 98 | let virtual_object_map_segment_no = u64::decode_directly(data)?; 99 | Ok(( 100 | creation_timestamp, 101 | passive_objects, 102 | length_of_data, 103 | virtual_object_map_offset, 104 | virtual_object_map_segment_no, 105 | )) 106 | } 107 | } 108 | 109 | // - implement fmt::Display 110 | impl fmt::Display for ObjectFooterVirtual { 111 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 112 | write!(f, "{}", Self::struct_name()) 113 | } 114 | } 115 | 116 | impl HeaderCoding for ObjectFooterVirtual { 117 | type Item = ObjectFooterVirtual; 118 | fn version() -> u8 { 119 | DEFAULT_FOOTER_VERSION_OBJECT_FOOTER_VIRTUAL 120 | } 121 | fn identifier() -> u32 { 122 | FOOTER_IDENTIFIER_OBJECT_FOOTER_VIRTUAL 123 | } 124 | fn encode_header(&self) -> Vec { 125 | let mut vec = vec![Self::version()]; 126 | vec.append(&mut self.object_number.encode_directly()); 127 | vec.append(&mut false.encode_directly()); // encryption flag 128 | vec.append(&mut self.encode_content()); 129 | vec 130 | } 131 | fn decode_content(data: Vec) -> Result { 132 | let mut cursor = Cursor::new(data); 133 | Self::check_version(&mut cursor)?; 134 | let object_number = u64::decode_directly(&mut cursor)?; 135 | let encryption_flag = bool::decode_directly(&mut cursor)?; 136 | if encryption_flag { 137 | return Err(ZffError::new(ZffErrorKind::EncodingError, ERROR_MISSING_ENCRYPTION_HEADER_KEY)); 138 | } 139 | let (creation_timestamp, 140 | passive_objects, 141 | length_of_data, 142 | virtual_object_map_offset, 143 | virtual_object_map_segment_no) = Self::decode_inner_content(&mut cursor)?; 144 | Ok(Self::with_data( 145 | object_number, 146 | creation_timestamp, 147 | passive_objects, 148 | length_of_data, 149 | virtual_object_map_offset, 150 | virtual_object_map_segment_no)) 151 | } 152 | 153 | fn struct_name() -> &'static str { 154 | "ObjectFooterVirtual" 155 | } 156 | } 157 | 158 | /// Represents an encrypted object footer of a virtual object. 159 | #[derive(Debug, Clone, PartialEq, Eq)] 160 | #[cfg_attr(feature = "serde", derive(Serialize))] 161 | #[cfg_attr(feature = "serde", derive(Deserialize))] 162 | pub struct EncryptedObjectFooterVirtual { 163 | /// The appropriate object number. 164 | pub object_number: u64, 165 | /// The underlying data in encrypted form. 166 | pub encrypted_data: Vec, 167 | } 168 | 169 | impl EncryptedObjectFooterVirtual { 170 | /// Creates a new [EncryptedObjectFooterPhysical] by the given values. 171 | pub fn with_data(object_number: u64, encrypted_data: Vec) -> Self { 172 | Self { 173 | object_number, 174 | encrypted_data, 175 | } 176 | } 177 | 178 | /// Tries to decrypt the ObjectFooter. If an error occures, the EncryptedObjectFooterVirtual is still available. 179 | pub fn decrypt(&self, key: K, algorithm: A) -> Result 180 | where 181 | A: Borrow, 182 | K: AsRef<[u8]>, 183 | { 184 | self.inner_decrypt(key, algorithm) 185 | } 186 | 187 | /// Tries to decrypt the ObjectFooter. Consumes the EncryptedObjectFooterPhysical, regardless of whether an error occurs or not. 188 | pub fn decrypt_and_consume(self, key: K, algorithm: A) -> Result 189 | where 190 | A: Borrow, 191 | K: AsRef<[u8]>, 192 | { 193 | self.inner_decrypt(key, algorithm) 194 | } 195 | 196 | fn inner_decrypt(&self, key: K, algorithm: A) -> Result 197 | where 198 | A: Borrow, 199 | K: AsRef<[u8]>, 200 | { 201 | let content = ObjectFooter::decrypt( 202 | key, &self.encrypted_data, self.object_number, algorithm.borrow())?; 203 | let mut cursor = Cursor::new(content); 204 | let (creation_timestamp, 205 | passive_objects, 206 | length_of_data, 207 | virtual_object_map_offset, 208 | virtual_object_map_segment_no) = ObjectFooterVirtual::decode_inner_content(&mut cursor)?; 209 | Ok(ObjectFooterVirtual::with_data( 210 | self.object_number, 211 | creation_timestamp, 212 | passive_objects, 213 | length_of_data, 214 | virtual_object_map_offset, 215 | virtual_object_map_segment_no)) 216 | } 217 | } 218 | 219 | // - implement fmt::Display 220 | impl fmt::Display for EncryptedObjectFooterVirtual { 221 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 222 | write!(f, "{}", Self::struct_name()) 223 | } 224 | } 225 | 226 | impl HeaderCoding for EncryptedObjectFooterVirtual { 227 | type Item = EncryptedObjectFooterVirtual; 228 | fn version() -> u8 { 229 | DEFAULT_FOOTER_VERSION_OBJECT_FOOTER_VIRTUAL 230 | } 231 | fn identifier() -> u32 { 232 | FOOTER_IDENTIFIER_OBJECT_FOOTER_VIRTUAL 233 | } 234 | fn encode_header(&self) -> Vec { 235 | let mut vec = vec![Self::version()]; 236 | vec.append(&mut self.object_number.encode_directly()); 237 | vec.append(&mut true.encode_directly()); // encryption flag 238 | vec.append(&mut self.encrypted_data.encode_directly()); 239 | vec 240 | } 241 | fn decode_content(data: Vec) -> Result { 242 | let mut cursor = Cursor::new(data); 243 | let version = u8::decode_directly(&mut cursor)?; 244 | if version != DEFAULT_FOOTER_VERSION_OBJECT_FOOTER_VIRTUAL { 245 | return Err(ZffError::new(ZffErrorKind::Unsupported, format!("{ERROR_UNSUPPORTED_VERSION}{version}"))) 246 | }; 247 | let object_number = u64::decode_directly(&mut cursor)?; 248 | let encryption_flag = bool::decode_directly(&mut cursor)?; 249 | if !encryption_flag { 250 | return Err(ZffError::new( 251 | ZffErrorKind::EncryptionError, 252 | ERROR_DECODE_UNENCRYPTED_OBJECT_WITH_DECRYPTION_FN)); 253 | } 254 | let encrypted_data = Vec::::decode_directly(&mut cursor)?; 255 | Ok(Self::with_data( 256 | object_number, 257 | encrypted_data)) 258 | } 259 | 260 | fn struct_name() -> &'static str { 261 | "EncryptedObjectFooterVirtual" 262 | } 263 | } -------------------------------------------------------------------------------- /src/lib/footer/segment_footer.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::collections::{HashMap, BTreeMap}; 3 | use std::io::Cursor; 4 | use std::fmt; 5 | 6 | // - internal 7 | use crate::{ 8 | HeaderCoding, Result, ValueDecoder, ValueEncoder, DEFAULT_FOOTER_VERSION_SEGMENT_FOOTER, FOOTER_IDENTIFIER_SEGMENT_FOOTER, INITIAL_CHUNK_NUMBER 9 | }; 10 | 11 | // - external 12 | #[cfg(feature = "serde")] 13 | use serde::{ 14 | Deserialize, 15 | Serialize, 16 | }; 17 | 18 | 19 | 20 | /// The SegmentFooter is a footer which is be written at the end of each segment. 21 | /// 22 | /// The footer contains a table on the chunks, present in the appropriate segment. 23 | /// The offset table is internally managed as a ```HashMap```. 24 | #[derive(Debug,Clone, PartialEq, Eq)] 25 | #[cfg_attr(feature = "serde", derive(Deserialize))] 26 | #[cfg_attr(feature = "serde", derive(Serialize))] 27 | pub struct SegmentFooter { 28 | /// The total length of the segment. 29 | pub length_of_segment: u64, 30 | /// A [HashMap] containing the object number and the appropriate offset of the [crate::header::ObjectHeader]. 31 | pub object_header_offsets: HashMap, //, 32 | /// A [HashMap] containing the object number and the appropriate offset of the [crate::footer::ObjectFooter]. 33 | pub object_footer_offsets: HashMap, //, 34 | /// [BTreeMap] containing the chunk number and the appropriate offset of the chunkmaps. 35 | pub chunk_header_map_table: BTreeMap, // 36 | /// [BTreeMap] containing the chunk number and the appropriate offset of the chunkmaps. 37 | pub chunk_samebytes_map_table: BTreeMap, // 38 | /// [BTreeMap] containing the chunk number and the appropriate offset of the chunkmaps. 39 | pub chunk_dedup_map_table: BTreeMap, // 40 | /// The first chunk number which was used in this segment. 41 | pub first_chunk_number: u64, 42 | /// The offset where the footer starts. 43 | pub footer_offset: u64, 44 | 45 | } 46 | 47 | impl Default for SegmentFooter { 48 | fn default() -> Self { 49 | SegmentFooter::new_empty() 50 | } 51 | } 52 | 53 | impl SegmentFooter { 54 | /// creates a new empty SegmentFooter. 55 | pub fn new_empty() -> SegmentFooter { 56 | Self { 57 | length_of_segment: 0, 58 | object_header_offsets: HashMap::new(), 59 | object_footer_offsets: HashMap::new(), 60 | chunk_header_map_table: BTreeMap::new(), 61 | chunk_samebytes_map_table: BTreeMap::new(), 62 | chunk_dedup_map_table: BTreeMap::new(), 63 | first_chunk_number: INITIAL_CHUNK_NUMBER, 64 | footer_offset: 0, 65 | } 66 | } 67 | 68 | /// creates a new SegmentFooter. 69 | pub fn new( 70 | length_of_segment: u64, 71 | object_header_offsets: HashMap, 72 | object_footer_offsets: HashMap, 73 | chunk_header_map_table: BTreeMap, 74 | chunk_samebytes_map_table: BTreeMap, 75 | chunk_dedup_map_table: BTreeMap, 76 | first_chunk_number: u64, 77 | footer_offset: u64) -> SegmentFooter { 78 | Self { 79 | length_of_segment, 80 | object_header_offsets, 81 | object_footer_offsets, 82 | chunk_header_map_table, 83 | chunk_samebytes_map_table, 84 | chunk_dedup_map_table, 85 | first_chunk_number, 86 | footer_offset, 87 | } 88 | } 89 | 90 | /// overwrites the length value in the footer with the given value. This can be useful, if you create an 'empty' 91 | /// footer (with length=0) and want to set the length value after reading the data from source to buffer. 92 | pub fn set_length_of_segment(&mut self, value: u64) { 93 | self.length_of_segment = value 94 | } 95 | 96 | /// adds an offset to the object header offset table of the SegmentFooter. 97 | pub fn add_object_header_offset(&mut self, object_number: u64, offset: u64) { 98 | self.object_header_offsets.insert(object_number, offset); 99 | } 100 | 101 | /// returns a reference of the object header offset table 102 | pub fn object_header_offsets(&self) -> &HashMap { 103 | &self.object_header_offsets 104 | } 105 | 106 | /// adds an offset to the object footer offset table of the SegmentFooter. 107 | pub fn add_object_footer_offset(&mut self, object_number: u64, offset: u64) { 108 | self.object_footer_offsets.insert(object_number, offset); 109 | } 110 | 111 | /// returns a reference of the object footer offset table 112 | pub fn object_footer_offsets(&self) -> &HashMap { 113 | &self.object_footer_offsets 114 | } 115 | 116 | /// sets the offset of this footer 117 | pub fn set_footer_offset(&mut self, offset: u64) { 118 | self.footer_offset = offset; 119 | } 120 | } 121 | 122 | impl HeaderCoding for SegmentFooter { 123 | type Item = SegmentFooter; 124 | 125 | fn identifier() -> u32 { 126 | FOOTER_IDENTIFIER_SEGMENT_FOOTER 127 | } 128 | 129 | fn version() -> u8 { 130 | DEFAULT_FOOTER_VERSION_SEGMENT_FOOTER 131 | } 132 | 133 | fn encode_header(&self) -> Vec { 134 | let mut vec = Vec::new(); 135 | vec.append(&mut Self::version().encode_directly()); 136 | vec.append(&mut self.length_of_segment.encode_directly()); 137 | vec.append(&mut self.object_header_offsets.encode_directly()); 138 | vec.append(&mut self.object_footer_offsets.encode_directly()); 139 | vec.append(&mut self.chunk_header_map_table.encode_directly()); 140 | vec.append(&mut self.chunk_samebytes_map_table.encode_directly()); 141 | vec.append(&mut self.chunk_dedup_map_table.encode_directly()); 142 | vec.append(&mut self.first_chunk_number.encode_directly()); 143 | vec.append(&mut self.footer_offset.encode_directly()); 144 | vec 145 | } 146 | 147 | fn decode_content(data: Vec) -> Result { 148 | let mut cursor = Cursor::new(data); 149 | Self::check_version(&mut cursor)?; 150 | let length_of_segment = u64::decode_directly(&mut cursor)?; 151 | let object_header_offsets = HashMap::::decode_directly(&mut cursor)?; 152 | let object_footer_offsets = HashMap::::decode_directly(&mut cursor)?; 153 | let chunk_header_map_table = BTreeMap::::decode_directly(&mut cursor)?; 154 | let chunk_samebytes_map_table = BTreeMap::::decode_directly(&mut cursor)?; 155 | let chunk_dedup_map_table = BTreeMap::::decode_directly(&mut cursor)?; 156 | let first_chunk_number = u64::decode_directly(&mut cursor)?; 157 | let footer_offset = u64::decode_directly(&mut cursor)?; 158 | Ok(SegmentFooter::new( 159 | length_of_segment, 160 | object_header_offsets, 161 | object_footer_offsets, 162 | chunk_header_map_table, 163 | chunk_samebytes_map_table, 164 | chunk_dedup_map_table, 165 | first_chunk_number, 166 | footer_offset)) 167 | } 168 | 169 | fn struct_name() -> &'static str { 170 | "SegmentFooter" 171 | } 172 | } 173 | 174 | // - implement fmt::Display 175 | impl fmt::Display for SegmentFooter { 176 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 177 | write!(f, "{}", Self::struct_name()) 178 | } 179 | } -------------------------------------------------------------------------------- /src/lib/hashing.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::fmt; 3 | 4 | // - external 5 | use blake3::Hasher as Blake3; 6 | use blake2::Blake2b512; 7 | use sha2::{Sha256, Sha512}; 8 | use sha3::Sha3_256; 9 | use digest::{DynDigest, Digest}; 10 | #[cfg(feature = "serde")] 11 | use serde::{ 12 | Deserialize, 13 | Serialize, 14 | }; 15 | #[cfg(feature = "log")] 16 | use log::debug; 17 | 18 | /// Defines all hashing algorithms, which are implemented in zff. 19 | #[repr(u8)] 20 | #[non_exhaustive] 21 | #[derive(Debug,Clone,Eq,PartialEq,Hash)] 22 | #[cfg_attr(feature = "serde", derive(Deserialize))] 23 | #[cfg_attr(feature = "serde", derive(Serialize))] 24 | pub enum HashType { 25 | /// The Blake2b-512 algorithm with the encoding value 0. 26 | Blake2b512 = 0, 27 | /// The SHA256 algorithm with the encoding value 1. 28 | SHA256 = 1, 29 | /// The SHA512 algorithm with the encoding value 2. 30 | SHA512 = 2, 31 | /// The SHA3-256 (keccak) algorithm with the encoding value 3. 32 | SHA3_256 = 3, 33 | /// The blake3 algorithm with the encoding value 4. 34 | Blake3 = 4, 35 | } 36 | 37 | impl HashType { 38 | /// returns the default length of the appropriate hash (as bits). 39 | pub fn default_len(&self) -> usize { 40 | match self { 41 | HashType::Blake2b512 => 512, 42 | HashType::SHA256 => 256, 43 | HashType::SHA512 => 512, 44 | HashType::SHA3_256 => 256, 45 | HashType::Blake3 => 256, 46 | } 47 | } 48 | } 49 | 50 | impl fmt::Display for HashType { 51 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 52 | let msg = match self { 53 | HashType::Blake2b512 => "Blake2b512", 54 | HashType::SHA256 => "SHA256", 55 | HashType::SHA512 => "SHA512", 56 | HashType::SHA3_256 => "Sha3_256", 57 | HashType::Blake3 => "Blake3" 58 | }; 59 | write!(f, "{}", msg) 60 | } 61 | } 62 | 63 | /// structure contains serveral methods to handle hashing 64 | #[derive(Debug,Clone)] 65 | pub struct Hash; 66 | 67 | impl Hash { 68 | /// returns a new Hasher which implements [DynDigest](https://docs.rs/digest/0.9.0/digest/trait.DynDigest.html). 69 | pub fn new_hasher(hash_type: &HashType) -> Box { 70 | match hash_type { 71 | HashType::Blake2b512 => Box::new(Blake2b512::new()), 72 | HashType::SHA256 => Box::new(Sha256::new()), 73 | HashType::SHA512 => Box::new(Sha512::new()), 74 | HashType::SHA3_256 => Box::new(Sha3_256::new()), 75 | HashType::Blake3 => Box::new(Blake3::new()), 76 | } 77 | } 78 | 79 | /// returns the default hashtype of zff. 80 | pub fn default_hashtype() -> HashType { 81 | HashType::Blake3 82 | } 83 | } 84 | 85 | #[cfg(feature = "log")] 86 | pub(crate) fn hashes_to_log(object_no: u64, file_no: Option, values: &Vec) { 87 | for value in values { 88 | if let Some(file_no) = file_no { 89 | debug!("{} hash for object {object_no} / file {file_no} finalized: {}", value.hash_type(), hex::encode(value.hash())); 90 | } else { 91 | debug!("{} hash for object {object_no} finalized: {}", value.hash_type(), hex::encode(value.hash())); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/lib/header/chunk_header.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::{Cursor, Read}; 3 | use std::fmt; 4 | 5 | // - internal 6 | use crate::{ 7 | Result, 8 | HeaderCoding, 9 | ValueEncoder, 10 | ValueDecoder, 11 | }; 12 | 13 | use crate::{ 14 | HEADER_IDENTIFIER_CHUNK_HEADER, 15 | DEFAULT_HEADER_VERSION_CHUNK_HEADER, 16 | ERROR_FLAG_VALUE, 17 | COMPRESSION_FLAG_VALUE, 18 | SAME_BYTES_FLAG_VALUE, 19 | DUPLICATION_FLAG_VALUE, 20 | ENCRYPTION_FLAG_VALUE, 21 | EMPTY_FILE_FLAG_VALUE, 22 | VIRTUAL_FLAG_VALUE, 23 | METADATA_EXT_TYPE_IDENTIFIER_U8, 24 | METADATA_EXT_TYPE_IDENTIFIER_CHUNK_HEADER, 25 | }; 26 | 27 | // - external 28 | #[cfg(feature = "serde")] 29 | use serde::{ 30 | Deserialize, 31 | Serialize, 32 | }; 33 | 34 | /// Header for the chunked data (typically stored in a chunk header map). 35 | /// The Header doesn't contain a chunk number, because it is stored in a map with the chunk number as key. 36 | #[derive(Debug,Clone, PartialEq, Eq, Hash, Default)] 37 | #[cfg_attr(feature = "serde", derive(Deserialize))] 38 | #[cfg_attr(feature = "serde", derive(Serialize))] 39 | pub struct ChunkHeader { 40 | /// The offset, where the chunked data can be found (inside the appriopriate segment file). 41 | pub offset: u64, 42 | /// The length of the chunked data. 43 | pub size: u64, 44 | /// The flags of the chunked data. 45 | pub flags: ChunkFlags, 46 | /// The integrity hash (currently xxhash) of the chunked data. 47 | pub integrity_hash: u64, 48 | } 49 | 50 | impl ChunkHeader { 51 | /// returns a new compression header with the given values. 52 | pub fn new(offset: u64, size: u64, flags: ChunkFlags, integrity_hash: u64) -> Self { 53 | Self { 54 | offset, 55 | size, 56 | flags, 57 | integrity_hash, 58 | } 59 | } 60 | } 61 | 62 | impl HeaderCoding for ChunkHeader { 63 | type Item = Self; 64 | 65 | fn identifier() -> u32 { 66 | HEADER_IDENTIFIER_CHUNK_HEADER 67 | } 68 | 69 | fn version() -> u8 { 70 | DEFAULT_HEADER_VERSION_CHUNK_HEADER 71 | } 72 | 73 | fn encode_header(&self) -> Vec { 74 | let mut vec = vec![Self::version()]; 75 | vec.append(&mut self.offset.encode_directly()); 76 | vec.append(&mut self.size.encode_directly()); 77 | vec.append(&mut self.flags.encode_directly()); 78 | vec.append(&mut self.integrity_hash.encode_directly()); 79 | vec 80 | } 81 | 82 | fn decode_content(data: Vec) -> Result { 83 | let mut cursor = Cursor::new(data); 84 | Self::check_version(&mut cursor)?; 85 | let offset = u64::decode_directly(&mut cursor)?; 86 | let size = u64::decode_directly(&mut cursor)?; 87 | let flags = ChunkFlags::decode_directly(&mut cursor)?; 88 | let integrity_hash = u64::decode_directly(&mut cursor)?; 89 | Ok(ChunkHeader::new(offset, size, flags, integrity_hash)) 90 | } 91 | 92 | fn struct_name() -> &'static str { 93 | "ChunkHeader" 94 | } 95 | } 96 | 97 | // - implement fmt::Display 98 | impl fmt::Display for ChunkHeader { 99 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 100 | write!(f, "{}", Self::struct_name()) 101 | } 102 | } 103 | 104 | impl ValueEncoder for ChunkHeader { 105 | fn identifier(&self) -> u8 { 106 | METADATA_EXT_TYPE_IDENTIFIER_CHUNK_HEADER 107 | } 108 | 109 | fn encode_directly(&self) -> Vec { 110 | HeaderCoding::encode_directly(self) 111 | } 112 | } 113 | 114 | impl ValueDecoder for ChunkHeader { 115 | type Item = ChunkHeader; 116 | 117 | fn decode_directly(data: &mut R) -> Result { 118 | ::decode_directly(data) 119 | } 120 | } 121 | 122 | /// The appropriate flags for each chunk. 123 | #[derive(Debug,Clone,Default, PartialEq, Eq, Hash)] 124 | #[cfg_attr(feature = "serde", derive(Deserialize))] 125 | #[cfg_attr(feature = "serde", derive(Serialize))] 126 | pub struct ChunkFlags { 127 | /// is set, if an read error is occured and the data in this chunk could be corrupted. 128 | pub error: bool, 129 | /// is set, if the data in the chunk are compressed. 130 | pub compression: bool, 131 | /// is set, if the chunk contains the same bytes. 132 | pub same_bytes: bool, 133 | /// is set, if this chunk is a duplicate of an other chunk. 134 | pub duplicate: bool, 135 | /// is set, if the chunk data is encrypted. 136 | pub encryption: bool, 137 | /// is set, if this is a placeholder chunk of an empty file. 138 | pub empty_file: bool, 139 | /// is set, if the chunk is a virtual chunk. 140 | pub virtual_chunk: bool, 141 | } 142 | 143 | impl From for ChunkFlags { 144 | fn from(flag_values: u8) -> Self { 145 | Self { 146 | error: flag_values & ERROR_FLAG_VALUE != 0, 147 | compression: flag_values & COMPRESSION_FLAG_VALUE != 0, 148 | same_bytes: flag_values & SAME_BYTES_FLAG_VALUE != 0, 149 | duplicate: flag_values & DUPLICATION_FLAG_VALUE != 0, 150 | encryption: flag_values & ENCRYPTION_FLAG_VALUE != 0, 151 | empty_file: flag_values & EMPTY_FILE_FLAG_VALUE != 0, 152 | virtual_chunk: flag_values & VIRTUAL_FLAG_VALUE != 0, 153 | } 154 | } 155 | } 156 | 157 | // - implement fmt::Display 158 | impl fmt::Display for ChunkFlags { 159 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 160 | write!(f, "{}", Self::struct_name()) 161 | } 162 | } 163 | 164 | // - this is a necassary helper method for fmt::Display and serde::ser::SerializeStruct. 165 | impl ChunkFlags { 166 | /// Creates an emtpy [ChunkFlags] struct with all flags set to false. 167 | pub fn new() -> Self { 168 | Self::default() 169 | } 170 | 171 | /// Returns the byte representation of the flags. 172 | pub fn as_bytes(&self) -> u8 { 173 | let mut flag_value: u8 = 0; 174 | if self.error { flag_value |= ERROR_FLAG_VALUE; } 175 | if self.compression { flag_value |= COMPRESSION_FLAG_VALUE; } 176 | if self.same_bytes { flag_value |= SAME_BYTES_FLAG_VALUE; } 177 | if self.duplicate { flag_value |= DUPLICATION_FLAG_VALUE; } 178 | if self.encryption { flag_value |= ENCRYPTION_FLAG_VALUE; } 179 | if self.empty_file { flag_value |= EMPTY_FILE_FLAG_VALUE; } 180 | if self.virtual_chunk { flag_value |= VIRTUAL_FLAG_VALUE; } 181 | flag_value 182 | } 183 | 184 | fn struct_name() -> &'static str { 185 | "ChunkHeaderFlags" 186 | } 187 | } 188 | 189 | impl ValueEncoder for ChunkFlags { 190 | fn encode_directly(&self) -> Vec { 191 | let mut flag_value: u8 = 0; 192 | if self.error { flag_value |= ERROR_FLAG_VALUE; } 193 | if self.compression { flag_value |= COMPRESSION_FLAG_VALUE; } 194 | if self.same_bytes { flag_value |= SAME_BYTES_FLAG_VALUE; } 195 | if self.duplicate { flag_value |= DUPLICATION_FLAG_VALUE; } 196 | if self.encryption { flag_value |= ENCRYPTION_FLAG_VALUE; } 197 | if self.empty_file { flag_value |= EMPTY_FILE_FLAG_VALUE; } 198 | if self.virtual_chunk { flag_value |= VIRTUAL_FLAG_VALUE; } 199 | flag_value.encode_directly() 200 | } 201 | 202 | fn identifier(&self) -> u8 { 203 | METADATA_EXT_TYPE_IDENTIFIER_U8 204 | } 205 | } 206 | 207 | impl ValueDecoder for ChunkFlags { 208 | type Item = Self; 209 | 210 | fn decode_directly(data: &mut R) -> Result { 211 | let flag_value = u8::decode_directly(data)?; 212 | Ok(ChunkFlags::from(flag_value)) 213 | } 214 | } -------------------------------------------------------------------------------- /src/lib/header/chunk_map/chunk_deduplication.rs: -------------------------------------------------------------------------------- 1 | // - parent 2 | use super::*; 3 | 4 | // - internal 5 | use crate::{ 6 | constants::*, io::zffreader::ZffReader 7 | }; 8 | 9 | // - external 10 | use redb::Database; 11 | use blake3::Hash as Blake3Hash; 12 | 13 | /// The [ChunkDeduplicationMap] stores the chunk size of the appropriate chunk. 14 | #[derive(Debug,Clone,PartialEq,Eq, Default)] 15 | #[cfg_attr(feature = "serde", derive(Deserialize))] 16 | pub struct ChunkDeduplicationMap { 17 | chunkmap: BTreeMap, // 18 | object_number: u64, 19 | target_size: usize, 20 | } 21 | 22 | impl ChunkMap for ChunkDeduplicationMap { 23 | type Value = u64; 24 | 25 | /// returns a new [ChunkDeduplicationMap] with the given values. 26 | fn new(object_number: u64, chunkmap: BTreeMap) -> Self { 27 | Self { 28 | chunkmap, 29 | object_number, 30 | target_size: 0, 31 | } 32 | } 33 | 34 | /// returns a new, empty [ChunkDeduplicationMap] with the given values. 35 | fn new_empty(object_number: u64) -> Self { 36 | Self { 37 | chunkmap: BTreeMap::new(), 38 | object_number, 39 | target_size: 0, 40 | } 41 | } 42 | 43 | fn flush(&mut self) -> BTreeMap { 44 | std::mem::take(&mut self.chunkmap) 45 | } 46 | 47 | fn current_size(&self) -> usize { 48 | match self.chunkmap.first_key_value() { 49 | Some(_) => self.chunkmap.len() * (8 + 8) + 8, //8 -> 8bytes for the chunk no, 8 bytes for the deduplication chunk no 50 | None => return 0, 51 | } 52 | } 53 | 54 | fn chunkmap(&self) -> &BTreeMap { 55 | &self.chunkmap 56 | } 57 | 58 | fn set_target_size(&mut self, target_size: usize) { 59 | self.target_size = target_size 60 | } 61 | 62 | fn set_object_number(&mut self, object_number: u64) { 63 | self.object_number = object_number 64 | } 65 | 66 | fn add_chunk_entry(&mut self, chunk_no: u64, value: Self::Value) -> bool { 67 | if self.is_full() { 68 | false 69 | } else { 70 | self.chunkmap.entry(chunk_no).or_insert(value); 71 | true 72 | } 73 | } 74 | 75 | fn is_full(&self) -> bool { 76 | if self.target_size < self.current_size() + 24 { //24 -> 8bytes for next chunk_no, 8 bytes for deduplication chunk_no, 8 bytes for the size of the encoded BTreeMap 77 | true 78 | } else { 79 | false 80 | } 81 | } 82 | 83 | fn decrypt_and_decode(key: K, encryption_algorithm: A, data: &mut D, chunk_no: u64) -> Result 84 | where 85 | K: AsRef<[u8]>, 86 | A: Borrow, 87 | D: Read, 88 | Self: Sized { 89 | let inner_structure_data = Self::inner_structure_data(data)?; 90 | let enc_buffer = Self::decrypt(key, inner_structure_data.structure_data, chunk_no, encryption_algorithm.borrow())?; 91 | let mut reader = Cursor::new(enc_buffer); 92 | let map = BTreeMap::decode_directly(&mut reader)?; 93 | Ok(Self::new(inner_structure_data.object_number, map)) 94 | } 95 | 96 | fn encode_map(&self) -> Vec { 97 | self.chunkmap.encode_directly() 98 | } 99 | 100 | fn encrypt_encoded_map(&self, key: K, encryption_algorithm: A, chunk_no: u64) -> Result> 101 | where 102 | K: AsRef<[u8]>, 103 | A: Borrow, 104 | Self: HeaderCoding, { 105 | let mut vec = Vec::new(); 106 | let encoded_map = Self::encode_map(self); 107 | let mut encrypted_map = Self::encrypt(key, encoded_map, chunk_no, encryption_algorithm.borrow())?; 108 | let mut encoded_version = Self::version().encode_directly(); 109 | let mut encoded_object_number = self.object_number.encode_directly(); 110 | let identifier = Self::identifier(); 111 | let encoded_header_length = ( 112 | DEFAULT_LENGTH_HEADER_IDENTIFIER + 113 | DEFAULT_LENGTH_VALUE_HEADER_LENGTH + 114 | encoded_object_number.len() + 115 | encrypted_map.len() + 116 | encoded_version.len()) as u64; 117 | vec.append(&mut identifier.to_be_bytes().to_vec()); 118 | vec.append(&mut encoded_header_length.to_le_bytes().to_vec()); 119 | vec.append(&mut encoded_version); 120 | vec.append(&mut encoded_object_number); 121 | vec.append(&mut encrypted_map); 122 | Ok(vec) 123 | } 124 | } 125 | 126 | impl HeaderCoding for ChunkDeduplicationMap { 127 | type Item = ChunkDeduplicationMap; 128 | 129 | fn identifier() -> u32 { 130 | HEADER_IDENTIFIER_CHUNK_DEDUPLICATION_MAP 131 | } 132 | 133 | fn version() -> u8 { 134 | DEFAULT_HEADER_VERSION_CHUNK_DEDUPLICATION_MAP 135 | } 136 | 137 | fn encode_header(&self) -> Vec { 138 | let mut vec = Vec::new(); 139 | vec.append(&mut Self::version().encode_directly()); 140 | vec.append(&mut self.object_number.encode_directly()); 141 | vec.append(&mut self.chunkmap.encode_directly()); 142 | vec 143 | } 144 | 145 | fn decode_content(data: Vec) -> Result { 146 | let mut cursor = Cursor::new(data); 147 | Self::check_version(&mut cursor)?; 148 | let object_number = u64::decode_directly(&mut cursor)?; 149 | let chunkmap = BTreeMap::::decode_directly(&mut cursor)?; 150 | Ok(Self::new(object_number, chunkmap)) 151 | } 152 | 153 | fn struct_name() -> &'static str { 154 | "ChunkDeduplicationMap" 155 | } 156 | } 157 | 158 | // - implement fmt::Display 159 | impl fmt::Display for ChunkDeduplicationMap { 160 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 161 | write!(f, "{}", Self::struct_name()) 162 | } 163 | } 164 | 165 | impl Encryption for ChunkDeduplicationMap { 166 | fn crypto_nonce_padding() -> u8 { 167 | 0b00111111 168 | } 169 | } 170 | 171 | #[cfg(feature = "serde")] 172 | impl Serialize for ChunkDeduplicationMap { 173 | fn serialize(&self, serializer: S) -> std::result::Result 174 | where 175 | S: Serializer, 176 | { 177 | let mut state = serializer.serialize_struct(Self::struct_name(), 2)?; 178 | for (key, value) in &self.chunkmap { 179 | state.serialize_field(string_to_str(key.to_string()), &value)?; 180 | } 181 | state.end() 182 | } 183 | } 184 | 185 | /// A structure to handle chunk deduplication. 186 | #[derive(Debug, Default)] 187 | pub struct DeduplicationMetadata { 188 | /// A map which can be used to handle the chunk deduplication. 189 | pub deduplication_map: DeduplicationChunkMap, 190 | /// Optional original zffreader - in case of an extension of an existing zff container, this is necessary. 191 | pub original_zffreader: Option>, 192 | } 193 | 194 | /// A map which can be used to handle the chunk deduplication. 195 | #[derive(Debug)] 196 | pub enum DeduplicationChunkMap { 197 | /// Use a in-memory deduplication map at cost of memory. 198 | InMemory(InMemoryDedupMap), // 199 | /// Use a Redb based deduplication map at cost of I/O. 200 | Redb(Database), 201 | } 202 | 203 | impl Default for DeduplicationChunkMap { 204 | fn default() -> Self { 205 | DeduplicationChunkMap::InMemory(InMemoryDedupMap::default()) 206 | } 207 | } 208 | 209 | /// The InMemory deduplication map structure 210 | #[derive(Debug, Default)] 211 | pub struct InMemoryDedupMap { 212 | xxhash_map: HashMap>, // 213 | verification_hash_map: HashMap // Chunk-No, Verification-Hash 214 | } 215 | 216 | impl DeduplicationChunkMap { 217 | /// Creates a new [DeduplicationChunkMap] with a Redb by given path. 218 | /// May fail if the Redb can not be created at the given Path. 219 | pub fn new_from_path>(path: P) -> Result { 220 | let db = Database::create(path.as_ref())?; 221 | Ok(Self::Redb(db)) 222 | } 223 | 224 | /// Creates a new [DeduplicationChunkMap] with the given [Redb-Database](Database). 225 | pub fn new_from_db(database: Database) -> Self { 226 | Self::Redb(database) 227 | } 228 | 229 | /// Creates a new in-memory [DeduplicationChunkMap]. 230 | pub fn new_in_memory_map() -> Self { 231 | DeduplicationChunkMap::InMemory(InMemoryDedupMap::default()) 232 | } 233 | 234 | /// Adds an entry to the deduplication map. 235 | pub fn append_entry(&mut self, xxhash: u64, chunk_no: u64) -> Result<()> { 236 | match self { 237 | DeduplicationChunkMap::InMemory(map) => { 238 | //TODO: Better implementation in future with get_or_insert 239 | // see: https://github.com/rust-lang/rust/issues/60896 240 | match map.xxhash_map.get_mut(&xxhash) { 241 | Some(set) => { set.insert(chunk_no); }, 242 | None => { 243 | let mut set = HashSet::new(); 244 | set.insert(chunk_no); 245 | map.xxhash_map.insert(xxhash, set); 246 | } 247 | } 248 | Ok(()) 249 | }, 250 | DeduplicationChunkMap::Redb(db) => { 251 | let read_txn = db.begin_read()?; 252 | let table = read_txn.open_table(CHUNK_MAP_TABLE)?; 253 | let mut inner_vec = table.get(xxhash)?.ok_or( 254 | ZffError::new(ZffErrorKind::NotFound, ERROR_NOT_IN_MAP))?.value(); 255 | if !inner_vec.contains(&chunk_no) { 256 | inner_vec.push(chunk_no); 257 | }; 258 | let write_txn = db.begin_write()?; 259 | { 260 | let mut table = write_txn.open_table(CHUNK_MAP_TABLE)?; 261 | table.insert(xxhash, inner_vec)?; 262 | } 263 | write_txn.commit()?; 264 | Ok(()) 265 | } 266 | } 267 | } 268 | 269 | /// Adds a blake3 hash to an existing chunk / xxhash combination 270 | pub fn append_verification_hash>(&mut self, chunk_no: u64, verification_hash: B) -> Result<()> { 271 | match self { 272 | DeduplicationChunkMap::InMemory(map) => { 273 | map.verification_hash_map.insert(chunk_no, *verification_hash.borrow()); 274 | Ok(()) 275 | }, 276 | DeduplicationChunkMap::Redb(db) => { 277 | let write_txn = db.begin_write()?; 278 | { 279 | let mut table = write_txn.open_table(CHUNK_MAP_B3_TABLE)?; 280 | table.insert(chunk_no, verification_hash.borrow().as_bytes())?; 281 | } 282 | write_txn.commit()?; 283 | Ok(()) 284 | } 285 | } 286 | } 287 | 288 | /// Returns all chunk numbers to the given xxhash. 289 | pub fn get_chunk_number(&mut self, xxhash: u64) -> Result> { 290 | match self { 291 | DeduplicationChunkMap::InMemory(map) => { 292 | map.xxhash_map.get(&xxhash).ok_or(ZffError::new( 293 | ZffErrorKind::NotFound, ERROR_NOT_IN_MAP)).map(|map| map.clone()) 294 | }, 295 | DeduplicationChunkMap::Redb(db) => { 296 | let read_txn = db.begin_read()?; 297 | let table = read_txn.open_table(CHUNK_MAP_TABLE)?; 298 | let inner_vec = table.get(xxhash)?.ok_or( 299 | ZffError::new(ZffErrorKind::NotFound, ERROR_NOT_IN_MAP))?.value(); 300 | Ok(inner_vec.into_iter().collect()) 301 | } 302 | } 303 | } 304 | 305 | /// Returns the appropriate verification Hash, if exists 306 | pub fn get_verification_hash(&mut self, chunk_no: u64) -> Result> { 307 | match self { 308 | DeduplicationChunkMap::InMemory(map) => { 309 | Ok(map.verification_hash_map.get(&chunk_no).map(|hash| hash.clone())) 310 | }, 311 | DeduplicationChunkMap::Redb(db) => { 312 | let read_txn = db.begin_read()?; 313 | let table = read_txn.open_table(CHUNK_MAP_B3_TABLE)?; 314 | Ok(table.get(chunk_no)?.map(|access_guard| Blake3Hash::from_bytes(*access_guard.value()))) 315 | } 316 | } 317 | } 318 | } 319 | 320 | // - implement fmt::Display 321 | impl fmt::Display for DeduplicationChunkMap { 322 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 323 | write!(f, "{}", Self::struct_name()) 324 | } 325 | } 326 | 327 | // - this is a necassary helper method for fmt::Display and serde::ser::SerializeStruct. 328 | impl DeduplicationChunkMap { 329 | fn struct_name() -> &'static str { 330 | "DeduplicationChunkMap" 331 | } 332 | } -------------------------------------------------------------------------------- /src/lib/header/chunk_map/chunk_header.rs: -------------------------------------------------------------------------------- 1 | // - parent 2 | use super::*; 3 | 4 | // - internal 5 | use crate::{ 6 | HEADER_IDENTIFIER_CHUNK_OFFSET_MAP, 7 | DEFAULT_HEADER_VERSION_CHUNK_OFFSET_MAP, 8 | }; 9 | 10 | /// The Chunkmap stores the information where the each appropriate chunk could be found. 11 | #[derive(Debug,Clone,PartialEq,Eq, Default)] 12 | #[cfg_attr(feature = "serde", derive(Deserialize))] 13 | pub struct ChunkHeaderMap { 14 | chunkmap: BTreeMap, // 15 | object_number: u64, 16 | target_size: usize, 17 | } 18 | 19 | impl ChunkMap for ChunkHeaderMap { 20 | type Value = ChunkHeader; 21 | 22 | /// returns a new [ChunkHeaderMap] with the given values. 23 | fn new(object_number: u64, chunkmap: BTreeMap) -> Self { 24 | Self { 25 | chunkmap, 26 | object_number, 27 | target_size: 0, 28 | } 29 | } 30 | 31 | /// returns a new, empty [ChunkHeaderMap] with the given values. 32 | fn new_empty(object_number: u64) -> Self { 33 | Self { 34 | chunkmap: BTreeMap::new(), 35 | object_number, 36 | target_size: 0, 37 | } 38 | } 39 | 40 | fn flush(&mut self) -> BTreeMap { 41 | std::mem::take(&mut self.chunkmap) 42 | } 43 | 44 | fn current_size(&self) -> usize { 45 | match self.chunkmap.first_key_value() { 46 | Some(_) => self.chunkmap.len() * (8 + 38) + 8, //8 -> 8bytes for the chunk no, 8 bytes for the encoded chunk header. 47 | None => return 0, 48 | } 49 | } 50 | 51 | fn chunkmap(&self) -> &BTreeMap { 52 | &self.chunkmap 53 | } 54 | 55 | fn set_target_size(&mut self, target_size: usize) { 56 | self.target_size = target_size 57 | } 58 | 59 | fn set_object_number(&mut self, object_number: u64) { 60 | self.object_number = object_number 61 | } 62 | 63 | fn add_chunk_entry(&mut self, chunk_no: u64, value: Self::Value) -> bool { 64 | if self.is_full() { 65 | false 66 | } else { 67 | self.chunkmap.entry(chunk_no).or_insert(value); 68 | true 69 | } 70 | } 71 | 72 | fn is_full(&self) -> bool { 73 | if self.target_size < self.current_size() + 54 { //54 -> 8bytes for next chunk_no, 38 bytes for the encoded chunk header, 8 bytes for the size of the encoded BTreeMap 74 | true 75 | } else { 76 | false 77 | } 78 | } 79 | 80 | fn decrypt_and_decode(key: K, encryption_algorithm: A, data: &mut D, chunk_no: u64) -> Result 81 | where 82 | K: AsRef<[u8]>, 83 | A: Borrow, 84 | D: Read, 85 | Self: Sized { 86 | let inner_structure_data = Self::inner_structure_data(data)?; 87 | let enc_buffer = Self::decrypt(key, inner_structure_data.structure_data, chunk_no, encryption_algorithm.borrow())?; 88 | let mut reader = Cursor::new(enc_buffer); 89 | let map = BTreeMap::decode_directly(&mut reader)?; 90 | Ok(Self::new(inner_structure_data.object_number, map)) 91 | } 92 | 93 | fn encode_map(&self) -> Vec { 94 | self.chunkmap.encode_directly() 95 | } 96 | 97 | fn encrypt_encoded_map(&self, key: K, encryption_algorithm: A, chunk_no: u64) -> Result> 98 | where 99 | K: AsRef<[u8]>, 100 | A: Borrow, 101 | Self: HeaderCoding, { 102 | let mut vec = Vec::new(); 103 | let encoded_map = Self::encode_map(self); 104 | let mut encrypted_map = Self::encrypt(key, encoded_map, chunk_no, encryption_algorithm.borrow())?; 105 | let mut encoded_version = Self::version().encode_directly(); 106 | let mut encoded_object_number = self.object_number.encode_directly(); 107 | let identifier = Self::identifier(); 108 | let encoded_header_length = ( 109 | DEFAULT_LENGTH_HEADER_IDENTIFIER + 110 | DEFAULT_LENGTH_VALUE_HEADER_LENGTH + 111 | encoded_object_number.len() + 112 | encrypted_map.len() + 113 | encoded_version.len()) as u64; 114 | vec.append(&mut identifier.to_be_bytes().to_vec()); 115 | vec.append(&mut encoded_header_length.to_le_bytes().to_vec()); 116 | vec.append(&mut encoded_version); 117 | vec.append(&mut encoded_object_number); 118 | vec.append(&mut encrypted_map); 119 | Ok(vec) 120 | } 121 | } 122 | 123 | impl HeaderCoding for ChunkHeaderMap { 124 | type Item = ChunkHeaderMap; 125 | 126 | fn identifier() -> u32 { 127 | HEADER_IDENTIFIER_CHUNK_OFFSET_MAP 128 | } 129 | 130 | fn version() -> u8 { 131 | DEFAULT_HEADER_VERSION_CHUNK_OFFSET_MAP 132 | } 133 | 134 | fn encode_header(&self) -> Vec { 135 | let mut vec = Vec::new(); 136 | vec.append(&mut Self::version().encode_directly()); 137 | vec.append(&mut self.object_number.encode_directly()); 138 | vec.append(&mut self.chunkmap.encode_directly()); 139 | vec 140 | } 141 | 142 | fn decode_content(data: Vec) -> Result { 143 | let mut cursor = Cursor::new(data); 144 | Self::check_version(&mut cursor)?; 145 | let object_number = u64::decode_directly(&mut cursor)?; 146 | let chunkmap = BTreeMap::::decode_directly(&mut cursor)?; 147 | Ok(Self::new(object_number, chunkmap)) 148 | } 149 | 150 | fn struct_name() -> &'static str { 151 | "ChunkHeaderMap" 152 | } 153 | } 154 | 155 | // - implement fmt::Display 156 | impl fmt::Display for ChunkHeaderMap { 157 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 158 | write!(f, "{}", Self::struct_name()) 159 | } 160 | } 161 | 162 | impl Encryption for ChunkHeaderMap { 163 | fn crypto_nonce_padding() -> u8 { 164 | 0b00000001 165 | } 166 | } 167 | 168 | #[cfg(feature = "serde")] 169 | impl Serialize for ChunkHeaderMap { 170 | fn serialize(&self, serializer: S) -> std::result::Result 171 | where 172 | S: Serializer, 173 | { 174 | let mut state = serializer.serialize_struct(Self::struct_name(), 2)?; 175 | for (key, value) in &self.chunkmap { 176 | state.serialize_field(string_to_str(key.to_string()), &value)?; 177 | } 178 | state.end() 179 | } 180 | } -------------------------------------------------------------------------------- /src/lib/header/chunk_map/chunk_same_bytes.rs: -------------------------------------------------------------------------------- 1 | // - parent 2 | use super::*; 3 | 4 | // - internal 5 | use crate::{ 6 | HEADER_IDENTIFIER_CHUNK_SAMEBYTES_MAP, 7 | DEFAULT_HEADER_VERSION_CHUNK_SAMEBYTES_MAP, 8 | }; 9 | 10 | /// The [ChunkSamebytesMap] stores the chunk size of the appropriate chunk. 11 | #[derive(Debug,Clone,PartialEq,Eq, Default)] 12 | #[cfg_attr(feature = "serde", derive(Deserialize))] 13 | pub struct ChunkSamebytesMap { 14 | chunkmap: BTreeMap, // 15 | object_number: u64, 16 | target_size: usize, 17 | } 18 | 19 | impl ChunkMap for ChunkSamebytesMap { 20 | type Value = u8; 21 | 22 | /// returns a new [ChunkOffsetMap] with the given values. 23 | fn new(object_number: u64, chunkmap: BTreeMap) -> Self { 24 | Self { 25 | chunkmap, 26 | object_number, 27 | target_size: 0, 28 | } 29 | } 30 | 31 | /// returns a new, empty [ChunkOffsetMap] with the given values. 32 | fn new_empty(object_number: u64) -> Self { 33 | Self { 34 | chunkmap: BTreeMap::new(), 35 | object_number, 36 | target_size: 0, 37 | } 38 | } 39 | 40 | fn flush(&mut self) -> BTreeMap { 41 | std::mem::take(&mut self.chunkmap) 42 | } 43 | 44 | fn current_size(&self) -> usize { 45 | match self.chunkmap.first_key_value() { 46 | Some(_) => self.chunkmap.len() * (8 + 1) + 8, //8 -> 8bytes for the chunk no, 1 byte for samebyte 47 | None => return 0, 48 | } 49 | } 50 | 51 | fn chunkmap(&self) -> &BTreeMap { 52 | &self.chunkmap 53 | } 54 | 55 | fn set_target_size(&mut self, target_size: usize) { 56 | self.target_size = target_size 57 | } 58 | 59 | fn set_object_number(&mut self, object_number: u64) { 60 | self.object_number = object_number 61 | } 62 | 63 | fn add_chunk_entry(&mut self, chunk_no: u64, value: Self::Value) -> bool { 64 | if self.is_full() { 65 | false 66 | } else { 67 | self.chunkmap.entry(chunk_no).or_insert(*value.borrow()); 68 | true 69 | } 70 | } 71 | 72 | fn is_full(&self) -> bool { 73 | if self.target_size < self.current_size() + 17 { 74 | true 75 | } else { 76 | false 77 | } 78 | } 79 | 80 | fn decrypt_and_decode(key: K, encryption_algorithm: A, data: &mut D, chunk_no: u64) -> Result 81 | where 82 | K: AsRef<[u8]>, 83 | A: Borrow, 84 | D: Read, 85 | Self: Sized { 86 | let inner_structure_data = Self::inner_structure_data(data)?; 87 | let enc_buffer = Self::decrypt(key, inner_structure_data.structure_data, chunk_no, encryption_algorithm.borrow())?; 88 | let mut reader = Cursor::new(enc_buffer); 89 | let map = BTreeMap::decode_directly(&mut reader)?; 90 | Ok(Self::new(inner_structure_data.object_number, map)) 91 | } 92 | 93 | fn encode_map(&self) -> Vec { 94 | self.chunkmap.encode_directly() 95 | } 96 | 97 | fn encrypt_encoded_map(&self, key: K, encryption_algorithm: A, chunk_no: u64) -> Result> 98 | where 99 | K: AsRef<[u8]>, 100 | A: Borrow, 101 | Self: HeaderCoding, { 102 | let mut vec = Vec::new(); 103 | let encoded_map = Self::encode_map(self); 104 | let mut encrypted_map = Self::encrypt(key, encoded_map, chunk_no, encryption_algorithm.borrow())?; 105 | let mut encoded_version = Self::version().encode_directly(); 106 | let mut encoded_object_number = self.object_number.encode_directly(); 107 | let identifier = Self::identifier(); 108 | let encoded_header_length = ( 109 | DEFAULT_LENGTH_HEADER_IDENTIFIER + 110 | DEFAULT_LENGTH_VALUE_HEADER_LENGTH + 111 | encoded_object_number.len() + 112 | encrypted_map.len() + 113 | encoded_version.len()) as u64; 114 | vec.append(&mut identifier.to_be_bytes().to_vec()); 115 | vec.append(&mut encoded_header_length.to_le_bytes().to_vec()); 116 | vec.append(&mut encoded_version); 117 | vec.append(&mut encoded_object_number); 118 | vec.append(&mut encrypted_map); 119 | Ok(vec) 120 | } 121 | } 122 | 123 | impl HeaderCoding for ChunkSamebytesMap { 124 | type Item = ChunkSamebytesMap; 125 | 126 | fn identifier() -> u32 { 127 | HEADER_IDENTIFIER_CHUNK_SAMEBYTES_MAP 128 | } 129 | 130 | fn version() -> u8 { 131 | DEFAULT_HEADER_VERSION_CHUNK_SAMEBYTES_MAP 132 | } 133 | 134 | fn encode_header(&self) -> Vec { 135 | let mut vec = Vec::new(); 136 | vec.append(&mut Self::version().encode_directly()); 137 | vec.append(&mut self.object_number.encode_directly()); 138 | vec.append(&mut self.chunkmap.encode_directly()); 139 | vec 140 | } 141 | 142 | fn decode_content(data: Vec) -> Result { 143 | let mut cursor = Cursor::new(data); 144 | Self::check_version(&mut cursor)?; 145 | let object_number = u64::decode_directly(&mut cursor)?; 146 | let chunkmap = BTreeMap::::decode_directly(&mut cursor)?; 147 | Ok(Self::new(object_number, chunkmap)) 148 | } 149 | 150 | fn struct_name() -> &'static str { 151 | "ChunkSamebytesMap" 152 | } 153 | } 154 | 155 | // - implement fmt::Display 156 | impl fmt::Display for ChunkSamebytesMap { 157 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 158 | write!(f, "{}", Self::struct_name()) 159 | } 160 | } 161 | 162 | impl Encryption for ChunkSamebytesMap { 163 | fn crypto_nonce_padding() -> u8 { 164 | 0b00011111 165 | } 166 | } 167 | 168 | #[cfg(feature = "serde")] 169 | impl Serialize for ChunkSamebytesMap { 170 | fn serialize(&self, serializer: S) -> std::result::Result 171 | where 172 | S: Serializer, 173 | { 174 | let mut state = serializer.serialize_struct(Self::struct_name(), 2)?; 175 | for (key, value) in &self.chunkmap { 176 | state.serialize_field(string_to_str(key.to_string()), &value)?; 177 | } 178 | state.end() 179 | } 180 | } -------------------------------------------------------------------------------- /src/lib/header/chunk_map/mod.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use core::borrow::Borrow; 3 | use std::path::Path; 4 | use std::cmp::PartialEq; 5 | use std::collections::{HashMap, BTreeMap, HashSet}; 6 | use std::io::{Cursor, Read, Seek}; 7 | use std::fmt; 8 | 9 | // - modules 10 | mod chunk_header; 11 | mod chunk_same_bytes; 12 | mod chunk_deduplication; 13 | 14 | // - use 15 | pub use chunk_header::*; 16 | pub use chunk_same_bytes::*; 17 | pub use chunk_deduplication::*; 18 | 19 | // - internal 20 | use crate::{ 21 | header::ChunkHeader, 22 | Result, 23 | HeaderCoding, 24 | ValueEncoder, 25 | ValueDecoder, 26 | ZffError, 27 | ZffErrorKind, 28 | EncryptionAlgorithm, 29 | Encryption, 30 | constants::*, 31 | }; 32 | 33 | #[cfg(feature = "serde")] 34 | use crate::helper::string_to_str; 35 | 36 | // - external 37 | #[cfg(feature = "serde")] 38 | use serde::{ 39 | Deserialize, 40 | Serialize, 41 | ser::{Serializer, SerializeStruct}, 42 | }; 43 | 44 | #[repr(C)] 45 | #[derive(Debug)] 46 | /// The appropriate Chunkmap type. 47 | pub enum ChunkMapType { 48 | /// The header map. 49 | HeaderMap = 0, 50 | /// The sambebytes map. 51 | SamebytesMap = 1, 52 | /// The deduplication map. 53 | DeduplicationMap = 2, 54 | } 55 | 56 | impl fmt::Display for ChunkMapType { 57 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 58 | let value = match self { 59 | ChunkMapType::HeaderMap => "HeaderMap", 60 | ChunkMapType::SamebytesMap => "SamebytesMap", 61 | ChunkMapType::DeduplicationMap => "DeduplicationMap", 62 | }; 63 | write!(f, "{value}") 64 | } 65 | } 66 | 67 | /// The ChunkMaps struct contains all chunk maps. 68 | #[derive(Debug,Clone,PartialEq,Eq, Default)] 69 | #[cfg_attr(feature = "serde", derive(Deserialize))] 70 | pub struct ChunkMaps { 71 | /// The offset map. 72 | pub header_map: ChunkHeaderMap, 73 | /// The same bytes map. 74 | pub same_bytes_map: ChunkSamebytesMap, 75 | /// The deduplication map. 76 | pub duplicate_chunks: ChunkDeduplicationMap, 77 | } 78 | 79 | impl ChunkMaps { 80 | /// checks if all maps are empty. 81 | pub fn is_empty(&self) -> bool { 82 | self.header_map.chunkmap().is_empty() && 83 | self.same_bytes_map.chunkmap().is_empty() && 84 | self.duplicate_chunks.chunkmap().is_empty() 85 | } 86 | 87 | /// sets the appropriate object number 88 | pub fn set_object_number(&mut self, object_number: u64) { 89 | self.header_map.set_object_number(object_number); 90 | self.same_bytes_map.set_object_number(object_number); 91 | self.duplicate_chunks.set_object_number(object_number); 92 | } 93 | 94 | } 95 | 96 | #[cfg(feature = "serde")] 97 | impl Serialize for ChunkMaps { 98 | fn serialize(&self, serializer: S) -> std::result::Result 99 | where 100 | S: Serializer, 101 | { 102 | let mut state = serializer.serialize_struct("ChunkMaps", 6)?; 103 | state.serialize_field(string_to_str(self.header_map.to_string()), &self.header_map)?; 104 | state.serialize_field(string_to_str(self.same_bytes_map.to_string()), &self.same_bytes_map)?; 105 | state.serialize_field(string_to_str(self.duplicate_chunks.to_string()), &self.duplicate_chunks)?; 106 | state.end() 107 | } 108 | } 109 | 110 | /// The ChunkMap trait specifies common interface for various types of chunk maps. 111 | /// 112 | /// The chunk maps are used to store information about chunks in the file. 113 | /// The information is stored in the form of a map, where the key is the chunk offset 114 | /// and the value is the chunk information. 115 | /// 116 | /// The chunk maps are used to optimize the file reading. 117 | pub trait ChunkMap { 118 | /// The type of the value stored in the map. 119 | type Value; 120 | 121 | /// returns a new chunk map with the given values. 122 | fn new(object_number: u64, chunkmap: BTreeMap) -> Self; 123 | 124 | /// returns a new, empty chunk map with the given values. 125 | fn new_empty(object_number: u64) -> Self; 126 | 127 | /// Returns the inner map and replaces it with an empty map. 128 | fn flush(&mut self) -> BTreeMap; 129 | 130 | /// Tries to add a chunk entry. 131 | /// Returns true, if the chunk no / value pair was added to the map. 132 | /// Returns false, if the map is full (in this case, the pair was **not** added to the map). 133 | fn add_chunk_entry(&mut self, chunk_no: u64, value: Self::Value) -> bool; 134 | 135 | /// Checks if the map is full (returns true if, returns false if not). 136 | fn is_full(&self) -> bool; 137 | 138 | /// The encoded size of this map. 139 | fn current_size(&self) -> usize; 140 | 141 | /// Reset the target size to the given value. 142 | fn set_target_size(&mut self, target_size: usize); 143 | 144 | /// Returns a reference to the inner map 145 | fn chunkmap(&self) -> &BTreeMap; 146 | 147 | /// Sets the appropriate object number 148 | fn set_object_number(&mut self, object_number: u64); 149 | 150 | /// Returns the apprpropriate (encrypted) structure data 151 | fn inner_structure_data(data: &mut D) -> Result 152 | where 153 | Self: HeaderCoding, 154 | { 155 | if !Self::check_identifier(data) { 156 | return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER)); 157 | } 158 | let header_length = Self::decode_header_length(data)? as usize; 159 | let version = u8::decode_directly(data)?; 160 | if version != Self::version() { 161 | return Err(ZffError::new(ZffErrorKind::Unsupported, format!("{ERROR_UNSUPPORTED_VERSION}{version}"))); 162 | } 163 | let object_number = u64::decode_directly(data)?; 164 | let mut structure_content = vec![0u8; header_length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH-1]; 165 | data.read_exact(&mut structure_content)?; 166 | Ok(ChunkMapInnerStructureData::new(object_number, structure_content)) 167 | } 168 | 169 | /// Decrypts and decodes the chunk map by using the given key. 170 | fn decrypt_and_decode(key: K, encryption_algorithm: A, data: &mut D, chunk_no: u64) -> Result 171 | where 172 | K: AsRef<[u8]>, 173 | A: Borrow, 174 | D: Read, 175 | Self: Sized + HeaderCoding; 176 | 177 | /// Encodes the inner map 178 | fn encode_map(&self) -> Vec; 179 | 180 | /// Encrypts the inner encoded map 181 | fn encrypt_encoded_map(&self, key: K, encryption_algorithm: A, chunk_no: u64) -> Result> 182 | where 183 | K: AsRef<[u8]>, 184 | A: Borrow, 185 | Self: HeaderCoding; 186 | 187 | /// Encodes and encrypts the appropriate chunk map 188 | fn encode_and_encrypt(&self, key: K, encryption_algorithm: A, chunk_no: u64) -> Result> 189 | where 190 | K: AsRef<[u8]>, 191 | A: Borrow, 192 | Self: HeaderCoding, 193 | { 194 | let mut vec = Vec::new(); 195 | let mut encrypted_map = self.encrypt_encoded_map(key, encryption_algorithm, chunk_no)?; 196 | let identifier = Self::identifier(); 197 | let encoded_header_length = (DEFAULT_LENGTH_HEADER_IDENTIFIER + DEFAULT_LENGTH_VALUE_HEADER_LENGTH + encrypted_map.len()) as u64; //4 bytes identifier + 8 bytes for length + length itself 198 | vec.append(&mut identifier.to_be_bytes().to_vec()); 199 | vec.append(&mut encoded_header_length.to_le_bytes().to_vec()); 200 | vec.append(&mut encrypted_map); 201 | 202 | Ok(vec) 203 | } 204 | } 205 | 206 | 207 | /// Structure data for chunk map inner structure. 208 | /// This structure is used to store the structure data for chunk map inner structure. 209 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 210 | pub struct ChunkMapInnerStructureData { 211 | /// Object number for chunk map inner structure. 212 | pub object_number: u64, 213 | /// Structure data for chunk map inner structure. 214 | pub structure_data: Vec, 215 | } 216 | 217 | impl ChunkMapInnerStructureData { 218 | /// Creates a new chunk map inner structure data. 219 | pub fn new(object_number: u64, structure_data: Vec) -> Self { 220 | Self { 221 | object_number, 222 | structure_data, 223 | } 224 | } 225 | } -------------------------------------------------------------------------------- /src/lib/header/compression_header.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::Cursor; 3 | use std::fmt; 4 | 5 | use crate::constants::DEFAULT_HEADER_VERSION_COMPRESSION_HEADER; 6 | // - internal 7 | use crate::{ 8 | Result, 9 | ZffError, 10 | ZffErrorKind, 11 | HeaderCoding, 12 | ValueEncoder, 13 | ValueDecoder, 14 | CompressionAlgorithm, 15 | }; 16 | 17 | use crate::{ 18 | HEADER_IDENTIFIER_COMPRESSION_HEADER, 19 | ERROR_HEADER_DECODER_COMPRESSION_ALGORITHM, 20 | }; 21 | 22 | // - external 23 | #[cfg(feature = "serde")] 24 | use serde::{ 25 | Deserialize, 26 | Serialize, 27 | }; 28 | 29 | /// Header for the data compression parameters.\ 30 | /// This header is part of the main header. 31 | #[derive(Debug,Clone, PartialEq)] 32 | #[cfg_attr(feature = "serde", derive(Deserialize))] 33 | #[cfg_attr(feature = "serde", derive(Serialize))] 34 | pub struct CompressionHeader { 35 | /// The compression algorithm. The appropriate algorithms/values 36 | /// could be found at [CompressionAlgorithm](enum.CompressionAlgorithm.html). 37 | pub algorithm: CompressionAlgorithm, 38 | /// The compression level. 39 | pub level: u8, 40 | /// The compression threshold. 41 | pub threshold: f32, 42 | } 43 | 44 | impl CompressionHeader { 45 | /// returns a new compression header with the given values. 46 | pub fn new(compression_algo: CompressionAlgorithm, level: u8, threshold: f32) -> CompressionHeader { 47 | Self { 48 | algorithm: compression_algo, 49 | level, 50 | threshold, 51 | } 52 | } 53 | } 54 | 55 | impl HeaderCoding for CompressionHeader { 56 | type Item = CompressionHeader; 57 | 58 | fn identifier() -> u32 { 59 | HEADER_IDENTIFIER_COMPRESSION_HEADER 60 | } 61 | 62 | fn version() -> u8 { 63 | DEFAULT_HEADER_VERSION_COMPRESSION_HEADER 64 | } 65 | 66 | fn encode_header(&self) -> Vec { 67 | let mut vec = vec![Self::version(), self.algorithm.clone() as u8, self.level]; 68 | vec.append(&mut self.threshold.encode_directly()); 69 | 70 | vec 71 | } 72 | 73 | fn decode_content(data: Vec) -> Result { 74 | let mut cursor = Cursor::new(data); 75 | Self::check_version(&mut cursor)?; 76 | let algorithm = match u8::decode_directly(&mut cursor) { 77 | Ok(0) => CompressionAlgorithm::None, 78 | Ok(1) => CompressionAlgorithm::Zstd, 79 | Ok(2) => CompressionAlgorithm::Lz4, 80 | _ => return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_COMPRESSION_ALGORITHM)) 81 | }; 82 | let level = u8::decode_directly(&mut cursor)?; 83 | let threshold = f32::decode_directly(&mut cursor)?; 84 | Ok(CompressionHeader::new(algorithm, level, threshold)) 85 | } 86 | 87 | fn struct_name() -> &'static str { 88 | "CompressionHeader" 89 | } 90 | } 91 | 92 | // - implement fmt::Display 93 | impl fmt::Display for CompressionHeader { 94 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 95 | write!(f, "{}", Self::struct_name()) 96 | } 97 | } -------------------------------------------------------------------------------- /src/lib/header/description_header.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::Cursor; 3 | use std::io::Read; 4 | use std::collections::HashMap; 5 | use std::fmt; 6 | 7 | // - internal 8 | use crate::{ 9 | Result, 10 | HeaderCoding, 11 | ValueEncoder, 12 | ValueDecoder, 13 | ZffError, 14 | ZffErrorKind, 15 | }; 16 | 17 | #[cfg(feature = "serde")] 18 | use crate::helper::string_to_str; 19 | 20 | use crate::constants::*; 21 | 22 | // - external 23 | #[cfg(feature = "serde")] 24 | use serde::{ 25 | ser::{ 26 | Serialize, 27 | Serializer, 28 | SerializeStruct, 29 | }, 30 | Deserialize, 31 | }; 32 | 33 | /// The description header contains all data, which describes the dumped data in den appropriate object (e.g. case number, examiner name or acquisition date). 34 | /// 35 | /// The description information are stored in a HashMap (e.g. like ["acquisition tool", "zffacquire"]). 36 | /// Some fields are predefined, to be able to ensure a certain degree of compatibility between different tools. 37 | /// The following fields are predefined: 38 | /// - case number (for the appropriate HashMap key, see [ENCODING_KEY_CASE_NUMBER](crate::constants::ENCODING_KEY_CASE_NUMBER)) 39 | /// - evidence number (for the appropriate HashMap key, see [ENCODING_KEY_EVIDENCE_NUMBER](crate::constants::ENCODING_KEY_EVIDENCE_NUMBER)) 40 | /// - examiner name (for the appropriate HashMap key, see [ENCODING_KEY_EXAMINER_NAME](crate::constants::ENCODING_KEY_EXAMINER_NAME)) 41 | /// - notes ((for the appropriate HashMap key, see [ENCODING_KEY_NOTES](crate::constants::ENCODING_KEY_NOTES)) 42 | /// 43 | /// But you are free to define custom additional key-value pairs. 44 | /// 45 | /// # Example 46 | /// ``` 47 | /// use zff::header::DescriptionHeader; 48 | /// 49 | /// let header_version = 2; 50 | /// let mut description_header = DescriptionHeader::new_empty(); 51 | /// 52 | /// description_header.set_examiner_name("ph0llux"); 53 | /// assert_eq!(Some("ph0llux"), description_header.examiner_name()); 54 | /// ``` 55 | #[derive(Debug,Clone, PartialEq, Eq)] 56 | #[cfg_attr(feature = "serde", derive(Deserialize))] 57 | pub struct DescriptionHeader { 58 | /// The inner identifier map. 59 | pub identifier_map: HashMap 60 | } 61 | 62 | #[cfg(feature = "serde")] 63 | impl Serialize for DescriptionHeader { 64 | fn serialize(&self, serializer: S) -> std::result::Result 65 | where 66 | S: Serializer, 67 | { 68 | let mut state = serializer.serialize_struct(Self::struct_name(), self.identifier_map.keys().len()+1)?; 69 | state.serialize_field("version", &Self::version())?; 70 | for (key, value) in &self.identifier_map { 71 | //work around lifetime checking (there is maybe a more elegant solution...) 72 | state.serialize_field(string_to_str(key.to_string()), string_to_str(value.to_string()))?; 73 | } 74 | state.end() 75 | } 76 | } 77 | 78 | impl DescriptionHeader { 79 | /// creates a new, empty header, which can be filled by the set_*-methods. 80 | /// All fields will be initialized with ```None``` or ```0```. 81 | pub fn new_empty() -> DescriptionHeader { 82 | Self { 83 | identifier_map: HashMap::new(), 84 | } 85 | } 86 | 87 | /// Creates a new [DescriptionHeader] with the given identifier map. 88 | pub fn new(identifier_map: HashMap) -> DescriptionHeader { 89 | Self { 90 | identifier_map, 91 | } 92 | } 93 | 94 | /// sets the case number as ```String```. 95 | pub fn set_case_number>(&mut self, value: V) { 96 | self.identifier_map.insert(String::from(ENCODING_KEY_CASE_NUMBER), value.into()); 97 | } 98 | 99 | /// sets the evidence number as ```String```. 100 | pub fn set_evidence_number>(&mut self, value: V) { 101 | self.identifier_map.insert(String::from(ENCODING_KEY_EVIDENCE_NUMBER), value.into()); 102 | } 103 | 104 | /// sets the examiner name as ```String```. 105 | pub fn set_examiner_name>(&mut self, value: V) { 106 | self.identifier_map.insert(String::from(ENCODING_KEY_EXAMINER_NAME), value.into()); 107 | } 108 | 109 | /// sets some notes as ```String```. 110 | pub fn set_notes>(&mut self, value: V) { 111 | self.identifier_map.insert(String::from(ENCODING_KEY_NOTES), value.into()); 112 | } 113 | 114 | /// returns the case number, if available. 115 | pub fn case_number(&self) -> Option<&str> { 116 | match &self.identifier_map.get(ENCODING_KEY_CASE_NUMBER) { 117 | Some(x) => Some(x), 118 | None => None 119 | } 120 | } 121 | 122 | /// returns the evidence number, if available. 123 | pub fn evidence_number(&self) -> Option<&str> { 124 | match &self.identifier_map.get(ENCODING_KEY_EVIDENCE_NUMBER) { 125 | Some(x) => Some(x), 126 | None => None 127 | } 128 | } 129 | 130 | /// returns the examiner name, if available. 131 | pub fn examiner_name(&self) -> Option<&str> { 132 | match &self.identifier_map.get(ENCODING_KEY_EXAMINER_NAME) { 133 | Some(x) => Some(x), 134 | None => None 135 | } 136 | } 137 | 138 | /// returns the notes, if some available. 139 | pub fn notes(&self) -> Option<&str> { 140 | match &self.identifier_map.get(ENCODING_KEY_NOTES) { 141 | Some(x) => Some(x), 142 | None => None 143 | } 144 | } 145 | 146 | /// inserts a custom key-value pair 147 | pub fn custom_identifier_value, V: Into>(&mut self, key: K, value: V) { 148 | self.identifier_map.insert(key.into(), value.into()); 149 | } 150 | 151 | /// returns all key-value pairs of this header. 152 | pub fn identifier_map(&self) -> &HashMap { 153 | &self.identifier_map 154 | } 155 | } 156 | 157 | impl HeaderCoding for DescriptionHeader { 158 | type Item = DescriptionHeader; 159 | 160 | fn identifier() -> u32 { 161 | HEADER_IDENTIFIER_DESCRIPTION_HEADER 162 | } 163 | 164 | fn version() -> u8 { 165 | DEFAULT_HEADER_VERSION_DESCRIPTION_HEADER 166 | } 167 | 168 | fn encode_header(&self) -> Vec { 169 | let mut vec = Vec::new(); 170 | vec.append(&mut Self::version().encode_directly()); 171 | vec.append(&mut self.identifier_map.encode_directly()); 172 | vec 173 | } 174 | 175 | /// decodes the header directly. 176 | fn decode_directly(data: &mut R) -> Result { 177 | if !Self::check_identifier(data) { 178 | return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER)); 179 | } 180 | let header_length = Self::decode_header_length(data)? as usize; 181 | let mut header_content = vec![0u8; header_length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH]; 182 | data.read_exact(&mut header_content)?; 183 | Self::decode_content(header_content) 184 | } 185 | 186 | fn decode_content(data: Vec) -> Result { 187 | let mut cursor = Cursor::new(data); 188 | Self::check_version(&mut cursor)?; 189 | let identifier_map = HashMap::::decode_directly(&mut cursor)?; 190 | let description_header = DescriptionHeader::new(identifier_map); 191 | 192 | Ok(description_header) 193 | } 194 | 195 | fn struct_name() -> &'static str { 196 | "DescriptionHeader" 197 | } 198 | } 199 | 200 | // - implement fmt::Display 201 | impl fmt::Display for DescriptionHeader { 202 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 203 | write!(f, "{}", Self::struct_name()) 204 | } 205 | } -------------------------------------------------------------------------------- /src/lib/header/encryption_header.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::{Cursor, Read}; 3 | use std::fmt; 4 | 5 | // - internal 6 | use crate::{ 7 | Result, 8 | EncryptionAlgorithm, 9 | HeaderCoding, 10 | ValueEncoder, 11 | ValueDecoder, 12 | header::{PBEHeader, KDFParameters, ObjectHeader}, 13 | ZffError, 14 | ZffErrorKind, 15 | KDFScheme, 16 | PBEScheme, 17 | encryption::*, 18 | constants::*, 19 | }; 20 | // - external 21 | #[cfg(feature = "serde")] 22 | use serde::{ 23 | Deserialize, 24 | Serialize, 25 | }; 26 | 27 | /// This struct could be used to manage the encryption information while creating a zff container 28 | #[derive(Debug, Clone, Eq, PartialEq)] 29 | #[cfg_attr(feature = "serde", derive(Deserialize))] 30 | #[cfg_attr(feature = "serde", derive(Serialize))] 31 | pub struct EncryptionInformation { 32 | /// The encryption key in **unencrypted** form. 33 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::helper::buffer_to_hex", deserialize_with = "crate::helper::hex_to_buffer"))] 34 | pub encryption_key: Vec, 35 | /// The used [crate::encryption::EncryptionAlgorithm]. 36 | pub algorithm: EncryptionAlgorithm, 37 | } 38 | 39 | impl EncryptionInformation { 40 | /// Creates a new [EncryptionInformation] by the given values. 41 | pub fn new(key: Vec, algorithm: EncryptionAlgorithm) -> Self { 42 | Self { 43 | encryption_key: key, 44 | algorithm 45 | } 46 | } 47 | } 48 | 49 | impl TryFrom for EncryptionInformation { 50 | type Error = ZffError; 51 | fn try_from(obj_header: ObjectHeader) -> Result { 52 | match obj_header.encryption_header { 53 | None => Err(ZffError::new(ZffErrorKind::EncryptionError, ERROR_MISSING_ENCRYPTION_HEADER_KEY)), 54 | Some(enc_header) => { 55 | match enc_header.get_encryption_key() { 56 | None => Err(ZffError::new(ZffErrorKind::EncryptionError, ERROR_MISSING_ENCRYPTION_HEADER_KEY)), 57 | Some(key) => Ok(EncryptionInformation { 58 | encryption_key: key, 59 | algorithm: enc_header.algorithm 60 | }), 61 | } 62 | } 63 | } 64 | } 65 | } 66 | 67 | impl TryFrom<&ObjectHeader> for EncryptionInformation { 68 | type Error = ZffError; 69 | fn try_from(obj_header: &ObjectHeader) -> Result { 70 | match obj_header.encryption_header { 71 | None => Err(ZffError::new(ZffErrorKind::EncryptionError, ERROR_MISSING_ENCRYPTION_HEADER_KEY)), 72 | Some(ref enc_header) => { 73 | match enc_header.get_encryption_key() { 74 | None => Err(ZffError::new(ZffErrorKind::EncryptionError, ERROR_MISSING_ENCRYPTION_HEADER_KEY)), 75 | Some(key) => Ok(EncryptionInformation { 76 | encryption_key: key, 77 | algorithm: enc_header.algorithm.clone() 78 | }), 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | // - implement fmt::Display 86 | impl fmt::Display for EncryptionInformation { 87 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 88 | write!(f, "{}", Self::struct_name()) 89 | } 90 | } 91 | 92 | // - this is a necassary helper method for fmt::Display and serde::ser::SerializeStruct. 93 | impl EncryptionInformation { 94 | fn struct_name() -> &'static str { 95 | "EncryptionInformation" 96 | } 97 | } 98 | 99 | /// The encryption header contains all informations (and the **encrypted** key) for the data and header encryption. 100 | /// 101 | /// The encryption header is the only optional header part of the main header 102 | /// (With the exception of the [PBEHeader], which is, however, part of the [EncryptionHeader]). 103 | /// The encryption header contains an encrypted key (encrypted encryption key). This key is encrypted with a password based encryption method, 104 | /// described by the containing [PBEHeader]. 105 | /// This key (decrypted with the appropriate password) is used to decrypt the encrypted data or the optionally encrypted header. 106 | #[derive(Debug,Clone,Eq,PartialEq)] 107 | #[cfg_attr(feature = "serde", derive(Deserialize))] 108 | #[cfg_attr(feature = "serde", derive(Serialize))] 109 | pub struct EncryptionHeader { 110 | /// The password based encryption header. 111 | pub pbe_header: PBEHeader, 112 | /// The used encryption algorithm. 113 | pub algorithm: EncryptionAlgorithm, 114 | /// The encrypted encryption key. 115 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::helper::buffer_to_base64", deserialize_with = "crate::helper::base64_to_buffer"))] 116 | pub encrypted_encryption_key: Vec, 117 | /// The decrypted encryption key. 118 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::helper::option_buffer_to_base64"))] 119 | pub decrypted_encryption_key: Option> 120 | } 121 | 122 | impl EncryptionHeader { 123 | /// creates a new encryption header by the given values. 124 | pub fn new( 125 | pbe_header: PBEHeader, 126 | algorithm: EncryptionAlgorithm, 127 | encrypted_encryption_key: Vec, //encrypted with set password 128 | ) -> EncryptionHeader { 129 | Self { 130 | pbe_header, 131 | algorithm, 132 | encrypted_encryption_key, 133 | decrypted_encryption_key: None, 134 | } 135 | } 136 | 137 | /// returns the decrypted encryption key. If the Key is already encrypted, you will get an None and should use the decrypt_encryption_key() method. 138 | pub fn get_encryption_key(&self) -> Option> { 139 | self.decrypted_encryption_key.clone() 140 | } 141 | 142 | /// returns the decrypted encryption key. If the Key is already encrypted, you will get an None and should use the decrypt_encryption_key() method. 143 | pub fn get_encryption_key_ref(&self) -> Option<&Vec> { 144 | self.decrypted_encryption_key.as_ref() 145 | } 146 | 147 | /// tries to decrypt the encryption key. 148 | pub fn decrypt_encryption_key>(&mut self, password: P) -> Result> { 149 | if let Some(decrypted_encryption_key) = &self.decrypted_encryption_key { 150 | return Ok(decrypted_encryption_key.clone()) 151 | } 152 | let decryption_key = match self.pbe_header.kdf_scheme { 153 | KDFScheme::PBKDF2SHA256 => match &self.pbe_header.kdf_parameters { 154 | KDFParameters::PBKDF2SHA256Parameters(parameters) => { 155 | let iterations = parameters.iterations; 156 | let salt = parameters.salt; 157 | match self.pbe_header.encryption_scheme { 158 | PBEScheme::AES128CBC => decrypt_pbkdf2sha256_aes128cbc( 159 | iterations, 160 | &salt, 161 | &self.pbe_header.pbencryption_nonce, 162 | &password, 163 | &self.encrypted_encryption_key 164 | ), 165 | PBEScheme::AES256CBC => decrypt_pbkdf2sha256_aes256cbc( 166 | iterations, 167 | &salt, 168 | &self.pbe_header.pbencryption_nonce, 169 | &password, 170 | &self.encrypted_encryption_key 171 | ), 172 | } 173 | } 174 | _ => Err(ZffError::new(ZffErrorKind::EncodingError, ERROR_HEADER_DECODER_UNKNOWN_KDF_SCHEME)) 175 | }, 176 | KDFScheme::Scrypt => match &self.pbe_header.kdf_parameters { 177 | KDFParameters::ScryptParameters(parameters) => { 178 | let logn = parameters.logn; 179 | let p = parameters.p; 180 | let r = parameters.r; 181 | let salt = parameters.salt; 182 | match self.pbe_header.encryption_scheme { 183 | PBEScheme::AES128CBC => decrypt_scrypt_aes128cbc( 184 | logn, 185 | p, 186 | r, 187 | &salt, 188 | &self.pbe_header.pbencryption_nonce, 189 | &password, 190 | &self.encrypted_encryption_key 191 | ), 192 | PBEScheme::AES256CBC => decrypt_scrypt_aes256cbc( 193 | logn, 194 | p, 195 | r, 196 | &salt, 197 | &self.pbe_header.pbencryption_nonce, 198 | &password, 199 | &self.encrypted_encryption_key 200 | ), 201 | } 202 | }, 203 | _ => Err(ZffError::new(ZffErrorKind::EncodingError, ERROR_HEADER_DECODER_UNKNOWN_KDF_SCHEME)), 204 | }, 205 | KDFScheme::Argon2id => match &self.pbe_header.kdf_parameters { 206 | KDFParameters::Argon2idParameters(parameters) => { 207 | let mem_cost = parameters.mem_cost; 208 | let lanes = parameters.lanes; 209 | let iterations = parameters.iterations; 210 | let salt = parameters.salt; 211 | match self.pbe_header.encryption_scheme { 212 | PBEScheme::AES128CBC => decrypt_argon2_aes128cbc( 213 | mem_cost, 214 | lanes, 215 | iterations, 216 | &salt, 217 | &self.pbe_header.pbencryption_nonce, 218 | &password, 219 | &self.encrypted_encryption_key 220 | ), 221 | PBEScheme::AES256CBC => decrypt_argon2_aes256cbc( 222 | mem_cost, 223 | lanes, 224 | iterations, 225 | &salt, 226 | &self.pbe_header.pbencryption_nonce, 227 | &password, 228 | &self.encrypted_encryption_key 229 | ), 230 | } 231 | }, 232 | _ => Err(ZffError::new(ZffErrorKind::EncodingError, ERROR_HEADER_DECODER_UNKNOWN_KDF_SCHEME)), 233 | }, 234 | }?; 235 | self.decrypted_encryption_key = Some(decryption_key.clone()); 236 | Ok(decryption_key) 237 | } 238 | } 239 | 240 | impl HeaderCoding for EncryptionHeader { 241 | type Item = EncryptionHeader; 242 | 243 | fn identifier() -> u32 { 244 | HEADER_IDENTIFIER_ENCRYPTION_HEADER 245 | } 246 | 247 | fn version() -> u8 { 248 | DEFAULT_HEADER_VERSION_ENCRYPTION_HEADER 249 | } 250 | 251 | fn encode_header(&self) -> Vec { 252 | let mut vec = vec![Self::version()]; 253 | vec.append(&mut self.pbe_header.encode_directly()); 254 | vec.push(self.algorithm.clone() as u8); 255 | vec.append(&mut self.encrypted_encryption_key.encode_directly()); 256 | vec 257 | } 258 | 259 | fn decode_content(data: Vec) -> Result { 260 | let mut cursor = Cursor::new(data); 261 | Self::check_version(&mut cursor)?; 262 | let pbe_header = PBEHeader::decode_directly(&mut cursor)?; 263 | let encryption_algorithm = match u8::decode_directly(&mut cursor)? { 264 | 0 => EncryptionAlgorithm::AES128GCM, 265 | 1 => EncryptionAlgorithm::AES256GCM, 266 | 2 => EncryptionAlgorithm::CHACHA20POLY1305, 267 | _ => return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_UNKNOWN_ENCRYPTION_ALGORITHM)), 268 | }; 269 | let key_length = u64::decode_directly(&mut cursor)? as usize; 270 | let mut encryption_key = vec![0u8; key_length]; 271 | cursor.read_exact(&mut encryption_key)?; 272 | Ok(EncryptionHeader::new(pbe_header, encryption_algorithm, encryption_key)) 273 | } 274 | 275 | fn struct_name() -> &'static str { 276 | "EncryptionHeader" 277 | } 278 | } 279 | 280 | // - implement fmt::Display 281 | impl fmt::Display for EncryptionHeader { 282 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 283 | write!(f, "{}", Self::struct_name()) 284 | } 285 | } -------------------------------------------------------------------------------- /src/lib/header/hash_header.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::{Cursor, Read}; 3 | use std::fmt; 4 | 5 | #[cfg(feature = "serde")] 6 | use std::collections::HashMap; 7 | 8 | use crate::constants::{DEFAULT_HEADER_VERSION_HASH_HEADER, DEFAULT_HEADER_VERSION_HASH_VALUE_HEADER}; 9 | // - internal 10 | use crate::{ 11 | Result, 12 | ValueEncoder, 13 | ValueDecoder, 14 | HeaderCoding, 15 | HashType, 16 | ZffError, 17 | ZffErrorKind, 18 | HEADER_IDENTIFIER_HASH_HEADER, 19 | HEADER_IDENTIFIER_HASH_VALUE, 20 | ERROR_HEADER_DECODER_UNKNOWN_HASH_TYPE, 21 | }; 22 | 23 | // - external 24 | use ed25519_dalek::SIGNATURE_LENGTH; 25 | #[cfg(feature = "serde")] 26 | use serde::{ 27 | Serialize, 28 | ser::{Serializer, SerializeStruct}, 29 | }; 30 | #[cfg(feature = "serde")] 31 | use base64::{Engine, engine::general_purpose::STANDARD as base64engine}; 32 | #[cfg(feature = "serde")] 33 | use hex; 34 | 35 | /// Header for the hash values of the dumped data stream. 36 | /// This header is part of various footers and contains 0 or more hash values of the dumped data.\ 37 | #[derive(Debug,Clone,Eq,PartialEq)] 38 | pub struct HashHeader { 39 | /// The hash values of the dumped data. 40 | pub hashes: Vec, 41 | } 42 | 43 | #[cfg(feature = "serde")] 44 | impl Serialize for HashHeader { 45 | fn serialize(&self, serializer: S) -> std::result::Result 46 | where 47 | S: Serializer, 48 | { 49 | let mut state = serializer.serialize_struct(Self::struct_name(), 3)?; 50 | state.serialize_field("version", &Self::version())?; 51 | let mut hashes = HashMap::new(); 52 | for hashvalue in &self.hashes { 53 | hashes.insert(hashvalue.hash_type.to_string(), hashvalue); 54 | } 55 | state.serialize_field("hash", &hashes)?; 56 | state.end() 57 | } 58 | } 59 | 60 | impl HashHeader { 61 | /// creates a new HashHeader by given values/hashes. 62 | pub fn new(hashes: Vec) -> HashHeader { 63 | Self { 64 | hashes, 65 | } 66 | } 67 | } 68 | 69 | impl HeaderCoding for HashHeader { 70 | type Item = HashHeader; 71 | 72 | fn identifier() -> u32 { 73 | HEADER_IDENTIFIER_HASH_HEADER 74 | } 75 | 76 | fn version() -> u8 { 77 | DEFAULT_HEADER_VERSION_HASH_HEADER 78 | } 79 | 80 | fn encode_header(&self) -> Vec { 81 | let mut vec = Vec::new(); 82 | vec.append(&mut Self::version().encode_directly()); 83 | vec.append(&mut self.hashes.encode_directly()); 84 | 85 | vec 86 | } 87 | 88 | fn decode_content(data: Vec) -> Result { 89 | let mut cursor = Cursor::new(data); 90 | Self::check_version(&mut cursor)?; 91 | let hashes = Vec::::decode_directly(&mut cursor)?; 92 | Ok(HashHeader::new(hashes)) 93 | } 94 | 95 | fn struct_name() -> &'static str { 96 | "HashHeader" 97 | } 98 | } 99 | 100 | // - implement fmt::Display 101 | impl fmt::Display for HashHeader { 102 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 103 | write!(f, "{}", Self::struct_name()) 104 | } 105 | } 106 | 107 | /// This is a part of the [HashHeader]. 108 | /// The HashValue-struct contains the appropriate hash algorithm and the hash. This struct has a version also. 109 | #[derive(Debug,Clone,PartialEq,Eq)] 110 | pub struct HashValue { 111 | /// The hash algorithm. 112 | pub hash_type: HashType, 113 | /// The hash value. 114 | pub hash: Vec, 115 | /// The ed25519 signature. 116 | pub ed25519_signature: Option<[u8; SIGNATURE_LENGTH]>, 117 | } 118 | 119 | impl HashValue { 120 | /// creates a new [HashValue] with the given parameters. 121 | pub fn new(hash_type: HashType, hash: Vec, ed25519_signature: Option<[u8; SIGNATURE_LENGTH]>,) -> HashValue{ 122 | Self { 123 | hash_type, 124 | hash, 125 | ed25519_signature, 126 | } 127 | } 128 | /// creates a new, empty [HashValue] for a given hashtype. 129 | pub fn new_empty(hash_type: HashType) -> HashValue { 130 | let hash_default_len = hash_type.default_len(); 131 | Self { 132 | hash_type, 133 | hash: vec!(0u8; hash_default_len/8), 134 | ed25519_signature: None 135 | } 136 | } 137 | 138 | /// returns the type of hash as [HashType](crate::hashing::HashType). 139 | pub fn hash_type(&self) -> &HashType { 140 | &self.hash_type 141 | } 142 | 143 | /// sets the hash value. 144 | pub fn set_hash(&mut self, hash: Vec) { 145 | self.hash = hash 146 | } 147 | 148 | /// returns the underlying hash value 149 | pub fn hash(&self) -> &Vec { 150 | &self.hash 151 | } 152 | 153 | /// sets the appropriate ed25519 signature 154 | pub fn set_ed25519_signature(&mut self, signature: [u8; SIGNATURE_LENGTH]) { 155 | self.ed25519_signature = Some(signature) 156 | } 157 | 158 | /// returns the appropriate signature 159 | pub fn ed25519_signature(&self) -> Option<[u8; SIGNATURE_LENGTH]> { 160 | self.ed25519_signature 161 | } 162 | } 163 | 164 | 165 | impl HeaderCoding for HashValue { 166 | type Item = HashValue; 167 | 168 | fn identifier() -> u32 { 169 | HEADER_IDENTIFIER_HASH_VALUE 170 | } 171 | 172 | fn version() -> u8 { 173 | DEFAULT_HEADER_VERSION_HASH_VALUE_HEADER 174 | } 175 | 176 | fn encode_header(&self) -> Vec { 177 | let mut vec = Vec::new(); 178 | vec.append(&mut Self::version().encode_directly()); 179 | vec.push(self.hash_type.clone() as u8); 180 | vec.append(&mut self.hash.encode_directly()); 181 | match self.ed25519_signature { 182 | None => (), 183 | Some(signature) => vec.append(&mut signature.encode_directly()), 184 | }; 185 | vec 186 | } 187 | 188 | fn decode_content(data: Vec) -> Result { 189 | let mut cursor = Cursor::new(&data); 190 | Self::check_version(&mut cursor)?; 191 | let hash_type = match u8::decode_directly(&mut cursor)? { 192 | 0 => HashType::Blake2b512, 193 | 1 => HashType::SHA256, 194 | 2 => HashType::SHA512, 195 | 3 => HashType::SHA3_256, 196 | 4 => HashType::Blake3, 197 | _ => return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_UNKNOWN_HASH_TYPE)), 198 | }; 199 | let hash = Vec::::decode_directly(&mut cursor)?; 200 | 201 | let mut ed25519_signature = None; 202 | if cursor.position() < (data.len() as u64 - 1) { 203 | let mut buffer = [0; SIGNATURE_LENGTH]; 204 | cursor.read_exact(&mut buffer)?; 205 | ed25519_signature = Some(buffer); 206 | } 207 | 208 | Ok(HashValue::new(hash_type, hash, ed25519_signature)) 209 | } 210 | 211 | fn struct_name() -> &'static str { 212 | "HashValue" 213 | } 214 | } 215 | 216 | // - implement fmt::Display 217 | impl fmt::Display for HashValue { 218 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 219 | write!(f, "{}", Self::struct_name()) 220 | } 221 | } 222 | 223 | #[cfg(feature = "serde")] 224 | impl Serialize for HashValue { 225 | fn serialize(&self, serializer: S) -> std::result::Result 226 | where 227 | S: Serializer, 228 | { 229 | let mut state = serializer.serialize_struct(Self::struct_name(), 3)?; 230 | state.serialize_field("hash_type", &self.hash_type.to_string())?; 231 | state.serialize_field("hash", &hex::encode(&self.hash))?; 232 | if let Some(signature) = &self.ed25519_signature { 233 | state.serialize_field("ed25519_signature", &base64engine.encode(signature))?; 234 | } 235 | state.end() 236 | } 237 | } -------------------------------------------------------------------------------- /src/lib/header/mod.rs: -------------------------------------------------------------------------------- 1 | // - modules 2 | mod segment_header; 3 | mod object_header; 4 | mod file_header; 5 | mod chunk_map; 6 | mod compression_header; 7 | mod description_header; 8 | mod pbe_header; 9 | mod hash_header; 10 | mod encryption_header; 11 | mod virtual_maps; 12 | mod chunk_header; 13 | 14 | // - re-export 15 | pub use hash_header::*; 16 | pub use encryption_header::*; 17 | pub use pbe_header::*; 18 | pub use compression_header::*; 19 | pub use description_header::*; 20 | pub use segment_header::*; 21 | pub use object_header::*; 22 | pub use file_header::*; 23 | pub use chunk_map::*; 24 | pub use virtual_maps::*; 25 | pub use chunk_header::*; -------------------------------------------------------------------------------- /src/lib/header/pbe_header.rs: -------------------------------------------------------------------------------- 1 | // STD 2 | use std::io::{Read,Cursor}; 3 | use std::fmt; 4 | 5 | use crate::constants::DEFAULT_HEADER_VERSION_PBE_HEADER; 6 | // - internal 7 | use crate::{ 8 | Result, 9 | HeaderCoding, 10 | ValueEncoder, 11 | ValueDecoder, 12 | KDFScheme, 13 | PBEScheme, 14 | ZffError, 15 | ZffErrorKind, 16 | }; 17 | 18 | use crate::{ 19 | HEADER_IDENTIFIER_PBE_HEADER, 20 | PBE_KDF_PARAMETERS_PBKDF2, 21 | PBE_KDF_PARAMETERS_SCRYPT, 22 | PBE_KDF_PARAMETERS_ARGON2ID, 23 | ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER_KDF, 24 | ERROR_HEADER_DECODER_UNKNOWN_PBE_SCHEME, 25 | ERROR_HEADER_DECODER_UNKNOWN_KDF_SCHEME, 26 | METADATA_EXT_TYPE_IDENTIFIER_UNKNOWN, 27 | }; 28 | 29 | // - external 30 | use byteorder::{BigEndian, ReadBytesExt}; 31 | #[cfg(feature = "serde")] 32 | use serde::{ 33 | Deserialize, 34 | Serialize, 35 | }; 36 | 37 | /// The pbe header contains all informations for the encryption of the encryption key. 38 | /// 39 | /// The encryption key, used for the chunk encryption, can be found at the [EncryptionHeader](struct.EncryptionHeader.html) - 40 | /// encrypted with an user password.\ 41 | /// This encryption of the encryption key is done via a password-based encryption (PBE).\ 42 | /// All metadata about this PBE can be found in this PBEHeader.\ 43 | #[derive(Debug,Clone,PartialEq,Eq)] 44 | #[cfg_attr(feature = "serde", derive(Deserialize))] 45 | #[cfg_attr(feature = "serde", derive(Serialize))] 46 | pub struct PBEHeader { 47 | /// The kdf scheme used for the encryption key derivation. 48 | pub kdf_scheme: KDFScheme, 49 | /// The encryption scheme used for the encryption key derivation. 50 | pub encryption_scheme: PBEScheme, 51 | /// The kdf parameters. 52 | pub kdf_parameters: KDFParameters, 53 | /// The nonce used for the encryption of the encryption key. 54 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::helper::buffer_to_hex"))] 55 | pub pbencryption_nonce: [u8; 16], 56 | } 57 | 58 | impl PBEHeader { 59 | /// returns a new pbe header with the given values. 60 | pub fn new( 61 | kdf_scheme: KDFScheme, 62 | encryption_scheme: PBEScheme, 63 | kdf_parameters: KDFParameters, 64 | pbencryption_nonce: [u8; 16], 65 | ) -> PBEHeader { 66 | Self { 67 | kdf_scheme, 68 | encryption_scheme, 69 | kdf_parameters, 70 | pbencryption_nonce, 71 | } 72 | } 73 | } 74 | 75 | impl HeaderCoding for PBEHeader { 76 | type Item = PBEHeader; 77 | 78 | fn identifier() -> u32 { 79 | HEADER_IDENTIFIER_PBE_HEADER 80 | } 81 | 82 | fn version() -> u8 { 83 | DEFAULT_HEADER_VERSION_PBE_HEADER 84 | } 85 | 86 | fn encode_header(&self) -> Vec { 87 | let mut vec = vec![Self::version(), self.kdf_scheme.clone() as u8, self.encryption_scheme.clone() as u8]; 88 | vec.append(&mut self.kdf_parameters.encode_directly()); 89 | vec.append(&mut self.pbencryption_nonce.encode_directly()); 90 | vec 91 | } 92 | 93 | fn decode_content(data: Vec) -> Result { 94 | let mut cursor = Cursor::new(data); 95 | Self::check_version(&mut cursor)?; 96 | let kdf_scheme = match u8::decode_directly(&mut cursor)? { 97 | 0 => KDFScheme::PBKDF2SHA256, 98 | 1 => KDFScheme::Scrypt, 99 | 2 => KDFScheme::Argon2id, 100 | _ => return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_UNKNOWN_KDF_SCHEME)) 101 | }; 102 | let encryption_scheme = match u8::decode_directly(&mut cursor)? { 103 | 0 => PBEScheme::AES128CBC, 104 | 1 => PBEScheme::AES256CBC, 105 | _ => return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_UNKNOWN_PBE_SCHEME)), 106 | }; 107 | let kdf_params = KDFParameters::decode_directly(&mut cursor)?; 108 | let mut encryption_nonce = [0; 16]; 109 | cursor.read_exact(&mut encryption_nonce)?; 110 | Ok(PBEHeader::new(kdf_scheme, encryption_scheme, kdf_params, encryption_nonce)) 111 | } 112 | 113 | fn struct_name() -> &'static str { 114 | "PBEHeader" 115 | } 116 | } 117 | 118 | // - implement fmt::Display 119 | impl fmt::Display for PBEHeader { 120 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 121 | write!(f, "{}", Self::struct_name()) 122 | } 123 | } 124 | 125 | /// enum to handle the stored parameters for the appropriate key deriavation function (KDF). 126 | #[repr(u8)] 127 | #[non_exhaustive] 128 | #[derive(Debug,Clone,Eq,PartialEq)] 129 | #[cfg_attr(feature = "serde", derive(Deserialize))] 130 | #[cfg_attr(feature = "serde", derive(Serialize))] 131 | pub enum KDFParameters { 132 | /// stores a struct [PBKDF2SHA256Parameters]. 133 | PBKDF2SHA256Parameters(PBKDF2SHA256Parameters), 134 | /// stores a struct [ScryptParameters]. 135 | ScryptParameters(ScryptParameters), 136 | /// stores a struct [Argon2idParameters]. 137 | Argon2idParameters(Argon2idParameters), 138 | } 139 | 140 | impl ValueEncoder for KDFParameters { 141 | fn encode_directly(&self) -> Vec { 142 | match self { 143 | KDFParameters::PBKDF2SHA256Parameters(params) => params.encode_directly(), 144 | KDFParameters::ScryptParameters(params) => params.encode_directly(), 145 | KDFParameters::Argon2idParameters(params) => params.encode_directly(), 146 | } 147 | } 148 | 149 | fn identifier(&self) -> u8 { 150 | METADATA_EXT_TYPE_IDENTIFIER_UNKNOWN 151 | } 152 | } 153 | 154 | impl ValueDecoder for KDFParameters { 155 | type Item = KDFParameters; 156 | 157 | fn decode_directly(data: &mut R) -> Result { 158 | let identifier = data.read_u32::()?; 159 | let size = u64::decode_directly(data)?; 160 | let mut params = vec![0u8; (size-12) as usize]; 161 | data.read_exact(&mut params)?; 162 | 163 | let mut params_cursor = Cursor::new(params); 164 | 165 | if identifier == PBKDF2SHA256Parameters::identifier() { 166 | let iterations = u32::decode_directly(&mut params_cursor)?; 167 | let mut salt = [0; 32]; 168 | params_cursor.read_exact(&mut salt)?; 169 | let parameters = PBKDF2SHA256Parameters::new(iterations, salt); 170 | Ok(KDFParameters::PBKDF2SHA256Parameters(parameters)) 171 | } else if identifier == ScryptParameters::identifier() { 172 | let logn = u8::decode_directly(&mut params_cursor)?; 173 | let r = u32::decode_directly(&mut params_cursor)?; 174 | let p = u32::decode_directly(&mut params_cursor)?; 175 | let mut salt = [0; 32]; 176 | params_cursor.read_exact(&mut salt)?; 177 | let parameters = ScryptParameters::new(logn, r, p, salt); 178 | Ok(KDFParameters::ScryptParameters(parameters)) 179 | } else if identifier == Argon2idParameters::identifier() { 180 | let mem_cost = u32::decode_directly(&mut params_cursor)?; 181 | let lanes = u32::decode_directly(&mut params_cursor)?; 182 | let iterations = u32::decode_directly(&mut params_cursor)?; 183 | let mut salt = [0; 32]; 184 | params_cursor.read_exact(&mut salt)?; 185 | let parameters = Argon2idParameters::new(mem_cost, lanes, iterations, salt); 186 | Ok(KDFParameters::Argon2idParameters(parameters)) 187 | } else { 188 | Err(ZffError::new( 189 | ZffErrorKind::Invalid, 190 | ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER_KDF)) 191 | } 192 | } 193 | } 194 | 195 | /// struct to store the parameters for the KDF PBKDF2-SHA256. 196 | #[derive(Debug,Clone,Eq,PartialEq)] 197 | #[cfg_attr(feature = "serde", derive(Deserialize))] 198 | #[cfg_attr(feature = "serde", derive(Serialize))] 199 | pub struct PBKDF2SHA256Parameters { 200 | /// The iterations to use. 201 | pub iterations: u32, 202 | /// The salt value. 203 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::helper::buffer_to_hex"))] 204 | pub salt: [u8; 32], 205 | } 206 | 207 | impl PBKDF2SHA256Parameters { 208 | /// returns a new [PBKDF2SHA256Parameters] with the given values. 209 | pub fn new(iterations: u32, salt: [u8; 32]) -> PBKDF2SHA256Parameters { 210 | Self { 211 | iterations, 212 | salt, 213 | } 214 | } 215 | } 216 | 217 | impl HeaderCoding for PBKDF2SHA256Parameters { 218 | type Item = PBKDF2SHA256Parameters; 219 | 220 | fn identifier() -> u32 { 221 | PBE_KDF_PARAMETERS_PBKDF2 222 | } 223 | 224 | /// just a placeholder, because this structure is not a header, but only a part of another header. 225 | fn version() -> u8 { 226 | 0 227 | } 228 | 229 | fn encode_header(&self) -> Vec { 230 | let mut vec = Vec::new(); 231 | vec.append(&mut self.iterations.encode_directly()); 232 | vec.append(&mut self.salt.encode_directly()); 233 | vec 234 | } 235 | 236 | fn decode_content(data: Vec) -> Result { 237 | let mut cursor = Cursor::new(data); 238 | 239 | let iterations = u32::decode_directly(&mut cursor)?; 240 | let mut salt = [0; 32]; 241 | cursor.read_exact(&mut salt)?; 242 | let parameters = PBKDF2SHA256Parameters::new(iterations, salt); 243 | Ok(parameters) 244 | } 245 | 246 | fn struct_name() -> &'static str { 247 | "PBKDF2SHA256Parameters" 248 | } 249 | } 250 | 251 | /// struct to store the parameters for the KDF Scrypt. 252 | #[derive(Debug,Clone,Eq,PartialEq)] 253 | #[cfg_attr(feature = "serde", derive(Deserialize))] 254 | #[cfg_attr(feature = "serde", derive(Serialize))] 255 | pub struct ScryptParameters { 256 | /// The log n parameter for Scrypt. 257 | pub logn: u8, 258 | /// The r parameter for Scrypt. 259 | pub r: u32, 260 | /// The p parameter for Scrypt. 261 | pub p: u32, 262 | /// The used salt. 263 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::helper::buffer_to_hex"))] 264 | pub salt: [u8; 32], 265 | } 266 | 267 | impl ScryptParameters { 268 | /// returns a new [ScryptParameters] with the given values. 269 | pub fn new(logn: u8, r: u32, p: u32, salt: [u8; 32]) -> ScryptParameters { 270 | Self { 271 | logn, 272 | r, 273 | p, 274 | salt, 275 | } 276 | } 277 | } 278 | 279 | impl HeaderCoding for ScryptParameters { 280 | type Item = ScryptParameters; 281 | 282 | fn identifier() -> u32 { 283 | PBE_KDF_PARAMETERS_SCRYPT 284 | } 285 | 286 | /// just a placeholder, because this structure is not a header, but only a part of another header. 287 | fn version() -> u8 { 288 | 0 289 | } 290 | 291 | fn encode_header(&self) -> Vec { 292 | let mut vec = Vec::new(); 293 | vec.append(&mut self.logn.encode_directly()); 294 | vec.append(&mut self.r.encode_directly()); 295 | vec.append(&mut self.p.encode_directly()); 296 | vec.append(&mut self.salt.encode_directly()); 297 | vec 298 | } 299 | 300 | fn decode_content(data: Vec) -> Result { 301 | let mut cursor = Cursor::new(data); 302 | 303 | let logn = u8::decode_directly(&mut cursor)?; 304 | let r = u32::decode_directly(&mut cursor)?; 305 | let p = u32::decode_directly(&mut cursor)?; 306 | let mut salt = [0; 32]; 307 | cursor.read_exact(&mut salt)?; 308 | let parameters = ScryptParameters::new(logn, r, p, salt); 309 | Ok(parameters) 310 | } 311 | 312 | fn struct_name() -> &'static str { 313 | "ScryptParameters" 314 | } 315 | 316 | } 317 | 318 | 319 | /// struct to store the parameters for the KDF Scrypt. 320 | #[derive(Debug,Clone,Eq,PartialEq)] 321 | #[cfg_attr(feature = "serde", derive(Deserialize))] 322 | #[cfg_attr(feature = "serde", derive(Serialize))] 323 | pub struct Argon2idParameters { 324 | /// The memory cost parameter for Argon2id. 325 | pub mem_cost: u32, 326 | /// The used number of lanes for Argon2id. 327 | pub lanes: u32, 328 | /// The iterations value for Argon2id. 329 | pub iterations: u32, 330 | /// The used salt. 331 | pub salt: [u8; 32], 332 | } 333 | 334 | impl Argon2idParameters { 335 | /// returns a new [ScryptParameters] with the given values. 336 | pub fn new(mem_cost: u32, lanes: u32, iterations: u32, salt: [u8; 32]) -> Argon2idParameters { 337 | Self { 338 | mem_cost, 339 | lanes, 340 | iterations, 341 | salt, 342 | } 343 | } 344 | } 345 | 346 | impl HeaderCoding for Argon2idParameters { 347 | type Item = Argon2idParameters; 348 | 349 | fn identifier() -> u32 { 350 | PBE_KDF_PARAMETERS_ARGON2ID 351 | } 352 | 353 | /// just a placeholder, because this structure is not a header, but only a part of another header. 354 | fn version() -> u8 { 355 | 0 356 | } 357 | 358 | fn encode_header(&self) -> Vec { 359 | let mut vec = Vec::new(); 360 | vec.append(&mut self.mem_cost.encode_directly()); 361 | vec.append(&mut self.lanes.encode_directly()); 362 | vec.append(&mut self.iterations.encode_directly()); 363 | vec.append(&mut self.salt.encode_directly()); 364 | vec 365 | } 366 | 367 | fn decode_content(data: Vec) -> Result { 368 | let mut cursor = Cursor::new(data); 369 | let mem_cost = u32::decode_directly(&mut cursor)?; 370 | let lanes = u32::decode_directly(&mut cursor)?; 371 | let iterations = u32::decode_directly(&mut cursor)?; 372 | let mut salt = [0; 32]; 373 | cursor.read_exact(&mut salt)?; 374 | let parameters = Argon2idParameters::new(mem_cost, lanes, iterations, salt); 375 | Ok(parameters) 376 | } 377 | 378 | fn struct_name() -> &'static str { 379 | "Argon2idParameters" 380 | } 381 | 382 | } -------------------------------------------------------------------------------- /src/lib/header/segment_header.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::io::Cursor; 3 | use std::fmt; 4 | 5 | // - internal 6 | use crate::{ 7 | Result, 8 | HeaderCoding, 9 | ValueEncoder, 10 | ValueDecoder, 11 | HEADER_IDENTIFIER_SEGMENT_HEADER, 12 | DEFAULT_HEADER_VERSION_SEGMENT_HEADER, 13 | }; 14 | 15 | // - external 16 | #[cfg(feature = "serde")] 17 | use serde::{ 18 | Deserialize, 19 | Serialize, 20 | }; 21 | 22 | /// The [SegmentHeader] contains a lot of initial metadata of the appropriate segment. Each segment has its own segment header.\ 23 | /// The following metadata are included in the [SegmentHeader]: 24 | /// - The unique identifier value 25 | /// - the number of the appropriate segment (the first segment starts always with a 1). 26 | /// - the target chunkmap size. 27 | #[derive(Debug,Clone,Eq)] 28 | #[cfg_attr(feature = "serde", derive(Deserialize))] 29 | #[cfg_attr(feature = "serde", derive(Serialize))] 30 | pub struct SegmentHeader { 31 | /// the unique identifier. Segments at the same group (=same zff container) should have the same identifier. 32 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::helper::as_hex"))] 33 | pub unique_identifier: u64, 34 | /// the appropriate segment number. 35 | pub segment_number: u64, 36 | /// the target size of a chunkmap. 37 | pub chunkmap_size: u64, 38 | } 39 | 40 | impl SegmentHeader { 41 | /// returns a new [SegmentHeader] with the given values. 42 | pub fn new(unique_identifier: u64, segment_number: u64, chunkmap_size: u64) -> SegmentHeader { 43 | Self { 44 | unique_identifier, 45 | segment_number, 46 | chunkmap_size, 47 | } 48 | } 49 | 50 | /// sets the segment number to the next number. This can be useful, for example, 51 | /// if you clone a segment header from the previous one or something like that. 52 | pub fn next_header(&self) -> SegmentHeader { 53 | SegmentHeader { 54 | unique_identifier: self.unique_identifier, 55 | segment_number: self.segment_number+1, 56 | chunkmap_size: self.chunkmap_size 57 | } 58 | } 59 | } 60 | 61 | impl HeaderCoding for SegmentHeader { 62 | type Item = SegmentHeader; 63 | 64 | fn identifier() -> u32 { 65 | HEADER_IDENTIFIER_SEGMENT_HEADER 66 | } 67 | 68 | fn version() -> u8 { 69 | DEFAULT_HEADER_VERSION_SEGMENT_HEADER 70 | } 71 | 72 | fn encode_header(&self) -> Vec { 73 | let mut vec = Vec::new(); 74 | vec.append(&mut Self::version().encode_directly()); 75 | vec.append(&mut self.unique_identifier.encode_directly()); 76 | vec.append(&mut self.segment_number.encode_directly()); 77 | vec.append(&mut self.chunkmap_size.encode_directly()); 78 | vec 79 | } 80 | 81 | fn decode_content(data: Vec) -> Result { 82 | let mut cursor = Cursor::new(data); 83 | Self::check_version(&mut cursor)?; // check version (and skip it) 84 | let unique_identifier = u64::decode_directly(&mut cursor)?; 85 | let segment_number = u64::decode_directly(&mut cursor)?; 86 | let chunkmap_size = u64::decode_directly(&mut cursor)?; 87 | Ok(SegmentHeader::new(unique_identifier, segment_number, chunkmap_size)) 88 | } 89 | 90 | fn struct_name() -> &'static str { 91 | "SegmentHeader" 92 | } 93 | } 94 | 95 | impl PartialEq for SegmentHeader { 96 | fn eq(&self, other: &Self) -> bool { 97 | self.segment_number == other.segment_number 98 | } 99 | } 100 | 101 | // - implement fmt::Display 102 | impl fmt::Display for SegmentHeader { 103 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 104 | write!(f, "{}", Self::struct_name()) 105 | } 106 | } -------------------------------------------------------------------------------- /src/lib/header/virtual_maps.rs: -------------------------------------------------------------------------------- 1 | // - STD 2 | use std::borrow::Borrow; 3 | use std::cmp::PartialEq; 4 | use std::io::{Cursor, Read}; 5 | use std::fmt; 6 | use std::collections::{BTreeMap, BTreeSet}; 7 | 8 | // - internal 9 | use crate::{ 10 | Result, 11 | HeaderCoding, 12 | ValueEncoder, 13 | ValueDecoder, 14 | ZffError, 15 | ZffErrorKind, 16 | header::EncryptionInformation, 17 | Encryption, 18 | constants::*, 19 | }; 20 | 21 | // - external 22 | #[cfg(feature = "serde")] 23 | use serde::{ 24 | Deserialize, 25 | Serialize, 26 | }; 27 | 28 | 29 | /// Contains the information of the appropriate virtual mapping. 30 | /// The counterpart offset has to be stored outside of this structure (in the [VirtualObjectMap]). 31 | #[derive(Debug,Clone,PartialEq,Eq)] 32 | #[cfg_attr(feature = "serde", derive(Deserialize))] 33 | #[cfg_attr(feature = "serde", derive(Serialize))] 34 | pub struct VirtualMappingInformation { 35 | /// The object number of the appropriate start chunk. 36 | /// This is necessary to be able to decrypt an encrypted chunk. 37 | pub object_number: u64, 38 | /// The number of the first affected chunk for this offset. 39 | pub start_chunk_no: u64, 40 | /// The start offset inside of this chunk. 41 | pub chunk_offset: u64, 42 | /// The full length of the data offset. 43 | pub length: u64, 44 | } 45 | 46 | impl VirtualMappingInformation { 47 | /// returns a new [VirtualMappingInformation] with the given values. 48 | pub fn with_data(object_number: u64, start_chunk_no: u64, chunk_offset: u64, length: u64) -> Self { 49 | Self { 50 | object_number, 51 | start_chunk_no, 52 | chunk_offset, 53 | length, 54 | } 55 | } 56 | 57 | /// encrypts the mapping information by the given encryption information and the original offset as nonce value, 58 | /// and returns the encrypted data. 59 | pub fn encrypt_directly(&self, encryption_information: E, offset: u64) -> Result> 60 | where 61 | E: Borrow 62 | { 63 | let mut vec = Vec::new(); 64 | let encrypted_content = VirtualMappingInformation::encrypt( 65 | &encryption_information.borrow().encryption_key, 66 | self.encode_content(), 67 | offset, 68 | &encryption_information.borrow().algorithm)?; 69 | let identifier = Self::identifier(); 70 | let encoded_header_length = ( 71 | DEFAULT_LENGTH_HEADER_IDENTIFIER + 72 | DEFAULT_LENGTH_VALUE_HEADER_LENGTH + 73 | Self::version().encode_directly().len() + 74 | encrypted_content.encode_directly().len()) as u64; //4 bytes identifier + 8 bytes for length + length itself 75 | vec.append(&mut identifier.to_be_bytes().to_vec()); 76 | vec.append(&mut encoded_header_length.encode_directly()); 77 | vec.append(&mut Self::version().encode_directly()); 78 | vec.append(&mut encrypted_content.encode_directly()); 79 | 80 | Ok(vec) 81 | } 82 | 83 | /// decodes the encrypted structure with the given [crate::header::EncryptionInformation] and the appropriate original data offset. 84 | pub fn decode_encrypted_structure_with_key(data: &mut R, offset: u64, encryption_information: E) -> Result 85 | where 86 | R: Read, 87 | E: Borrow 88 | { 89 | if !Self::check_identifier(data) { 90 | return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER)); 91 | }; 92 | let header_length = Self::decode_header_length(data)? as usize; 93 | let mut header_content = vec![0u8; header_length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH]; 94 | data.read_exact(&mut header_content)?; 95 | let mut cursor = Cursor::new(header_content); 96 | Self::check_version(&mut cursor)?; // check version (and skip it) 97 | let encrypted_data = Vec::::decode_directly(&mut cursor)?; 98 | let algorithm = &encryption_information.borrow().algorithm; 99 | let decrypted_data = VirtualMappingInformation::decrypt( 100 | &encryption_information.borrow().encryption_key, 101 | encrypted_data, 102 | offset, 103 | algorithm)?; 104 | let mut cursor = Cursor::new(decrypted_data); 105 | let (object_number, 106 | start_chunk_no, 107 | chunk_offset, 108 | length) = Self::decode_inner_content(&mut cursor)?; 109 | Ok(Self::with_data( 110 | object_number, 111 | start_chunk_no, 112 | chunk_offset, 113 | length)) 114 | } 115 | 116 | fn encode_content(&self) -> Vec { 117 | let mut vec = Vec::new(); 118 | vec.append(&mut self.object_number.encode_directly()); 119 | vec.append(&mut self.start_chunk_no.encode_directly()); 120 | vec.append(&mut self.chunk_offset.encode_directly()); 121 | vec.append(&mut self.length.encode_directly()); 122 | vec 123 | } 124 | 125 | fn decode_inner_content(data: &mut R) -> Result<( 126 | u64, //object_number 127 | u64, //start_chunk_no 128 | u64, //chunk_offset 129 | u64, //length 130 | )> { 131 | let object_number = u64::decode_directly(data)?; 132 | let start_chunk_no = u64::decode_directly(data)?; 133 | let chunk_offset = u64::decode_directly(data)?; 134 | let length = u64::decode_directly(data)?; 135 | Ok(( 136 | object_number, 137 | start_chunk_no, 138 | chunk_offset, 139 | length 140 | )) 141 | } 142 | 143 | } 144 | 145 | impl Encryption for VirtualMappingInformation { 146 | fn crypto_nonce_padding() -> u8 { 147 | 0b00000010 148 | } 149 | } 150 | 151 | impl HeaderCoding for VirtualMappingInformation { 152 | type Item = VirtualMappingInformation; 153 | 154 | fn identifier() -> u32 { 155 | HEADER_IDENTIFIER_VIRTUAL_MAPPING_INFORMATION 156 | } 157 | 158 | fn version() -> u8 { 159 | DEFAULT_HEADER_VERSION_VIRTUAL_MAPPING_INFORMATION 160 | } 161 | 162 | fn encode_header(&self) -> Vec { 163 | let mut vec = vec![Self::version()]; 164 | vec.append(&mut self.encode_content()); 165 | vec 166 | } 167 | 168 | fn decode_content(data: Vec) -> Result { 169 | let mut cursor = Cursor::new(data); 170 | let version = u8::decode_directly(&mut cursor)?; 171 | if version != DEFAULT_HEADER_VERSION_VIRTUAL_MAPPING_INFORMATION { 172 | return Err(ZffError::new( 173 | ZffErrorKind::Unsupported, 174 | format!("{ERROR_UNSUPPORTED_VERSION}{version}"))) 175 | }; 176 | let (object_number, 177 | start_chunk_no, 178 | chunk_offset, 179 | length) = Self::decode_inner_content(&mut cursor)?; 180 | 181 | Ok(Self::with_data(object_number, start_chunk_no, chunk_offset, length)) 182 | } 183 | 184 | fn struct_name() -> &'static str { 185 | "VirtualMappingInformation" 186 | } 187 | } 188 | 189 | // - implement fmt::Display 190 | impl fmt::Display for VirtualMappingInformation { 191 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 192 | write!(f, "{}", Self::struct_name()) 193 | } 194 | } 195 | 196 | /// The [VirtualObjectMap] contains the appropriate offset map to find the counterpart [VirtualMappingInformation]. 197 | /// 198 | /// As the name of this struct already suggests, this structure can be layered multiple times. 199 | #[derive(Debug,Clone,PartialEq,Eq)] 200 | #[cfg_attr(feature = "serde", derive(Deserialize))] 201 | #[cfg_attr(feature = "serde", derive(Serialize))] 202 | pub struct VirtualObjectMap { 203 | /// The appropriate offset maps. 204 | pub offsetmaps: BTreeSet>, // 205 | } 206 | 207 | impl VirtualObjectMap { 208 | /// returns a new [VirtualObjectMap] with the given values. 209 | pub fn with_data(offsetmaps: BTreeSet>) -> Self { 210 | Self { 211 | offsetmaps, 212 | } 213 | } 214 | 215 | /// encrypts the mapping information by the given encryption information and the original offset as nonce value, 216 | /// and returns the encrypted data. 217 | /// Needs the object number of the appropriate virtual Object to encrypt. 218 | pub fn encrypt_directly(&self, encryption_information: E, object_number: u64) -> Result> 219 | where 220 | E: Borrow 221 | { 222 | let mut vec = Vec::new(); 223 | let encrypted_content = VirtualObjectMap::encrypt( 224 | &encryption_information.borrow().encryption_key, 225 | self.encode_content(), 226 | object_number, 227 | &encryption_information.borrow().algorithm)?; 228 | let identifier = Self::identifier(); 229 | let encoded_header_length = ( 230 | DEFAULT_LENGTH_HEADER_IDENTIFIER + 231 | DEFAULT_LENGTH_VALUE_HEADER_LENGTH + 232 | Self::version().encode_directly().len() + 233 | encrypted_content.encode_directly().len()) as u64; //4 bytes identifier + 8 bytes for length + length itself 234 | vec.append(&mut identifier.to_be_bytes().to_vec()); 235 | vec.append(&mut encoded_header_length.encode_directly()); 236 | vec.append(&mut Self::version().encode_directly()); 237 | vec.append(&mut encrypted_content.encode_directly()); 238 | 239 | Ok(vec) 240 | } 241 | 242 | /// decodes the encrypted structure with the given [crate::header::EncryptionInformation] and the appropriate original data offset. 243 | pub fn decode_encrypted_structure_with_key(data: &mut R, encryption_information: E, object_number: u64) -> Result 244 | where 245 | R: Read, 246 | E: Borrow 247 | { 248 | if !Self::check_identifier(data) { 249 | return Err(ZffError::new(ZffErrorKind::Invalid, ERROR_HEADER_DECODER_MISMATCH_IDENTIFIER)); 250 | }; 251 | let header_length = Self::decode_header_length(data)? as usize; 252 | let mut header_content = vec![0u8; header_length-DEFAULT_LENGTH_HEADER_IDENTIFIER-DEFAULT_LENGTH_VALUE_HEADER_LENGTH]; 253 | data.read_exact(&mut header_content)?; 254 | let mut cursor = Cursor::new(header_content); 255 | let version = u8::decode_directly(&mut cursor)?; 256 | if version != DEFAULT_HEADER_VERSION_VIRTUAL_OBJECT_MAP { 257 | return Err(ZffError::new( 258 | ZffErrorKind::Unsupported, 259 | format!("{ERROR_UNSUPPORTED_VERSION}{version}"))) 260 | }; 261 | let encrypted_data = Vec::::decode_directly(&mut cursor)?; 262 | let algorithm = &encryption_information.borrow().algorithm; 263 | let decrypted_data = VirtualMappingInformation::decrypt( 264 | &encryption_information.borrow().encryption_key, 265 | encrypted_data, 266 | object_number, 267 | algorithm)?; 268 | let mut cursor = Cursor::new(decrypted_data); 269 | let offsetmaps = Self::decode_inner_content(&mut cursor)?; 270 | Ok(Self::with_data(offsetmaps)) 271 | } 272 | 273 | fn encode_content(&self) -> Vec { 274 | let mut vec = Vec::new(); 275 | vec.append(&mut self.offsetmaps.encode_directly()); 276 | vec 277 | } 278 | 279 | fn decode_inner_content(data: &mut R) -> Result>> { 280 | let offsetmaps = BTreeSet::>::decode_directly(data)?; 281 | Ok(offsetmaps) 282 | } 283 | } 284 | 285 | impl HeaderCoding for VirtualObjectMap { 286 | type Item = VirtualObjectMap; 287 | 288 | fn identifier() -> u32 { 289 | HEADER_IDENTIFIER_VIRTUAL_OBJECT_MAP 290 | } 291 | 292 | fn version() -> u8 { 293 | DEFAULT_HEADER_VERSION_VIRTUAL_OBJECT_MAP 294 | } 295 | 296 | fn encode_header(&self) -> Vec { 297 | let mut vec = vec![Self::version()]; 298 | vec.append(&mut self.encode_content()); 299 | vec 300 | } 301 | 302 | fn decode_content(data: Vec) -> Result { 303 | let mut cursor = Cursor::new(data); 304 | Self::check_version(&mut cursor)?; // check version (and skip it) 305 | let offsetmaps = Self::decode_inner_content(&mut cursor)?; 306 | 307 | Ok(Self::with_data(offsetmaps)) 308 | } 309 | 310 | fn struct_name() -> &'static str { 311 | "VirtualObjectMap" 312 | } 313 | } 314 | 315 | // - implement fmt::Display 316 | impl fmt::Display for VirtualObjectMap { 317 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 318 | write!(f, "{}", Self::struct_name()) 319 | } 320 | } 321 | 322 | impl Encryption for VirtualObjectMap { 323 | fn crypto_nonce_padding() -> u8 { 324 | 0b01000000 325 | } 326 | } -------------------------------------------------------------------------------- /src/lib/helper.rs: -------------------------------------------------------------------------------- 1 | // STD 2 | use std::collections::{BTreeMap, BTreeSet}; 3 | 4 | // internal 5 | use crate::{Result, ZffError, ZffErrorKind, constants::*}; 6 | 7 | // external 8 | #[cfg(feature = "log")] 9 | use log::debug; 10 | #[cfg(feature = "serde")] 11 | use hex::FromHex; 12 | #[cfg(feature = "serde")] 13 | use base64::{Engine, engine::general_purpose::STANDARD as base64engine}; 14 | #[cfg(feature = "serde")] 15 | use serde::Deserialize; 16 | 17 | #[cfg(feature = "serde")] 18 | pub(crate) fn string_to_str(s: String) -> &'static str { 19 | Box::leak(s.into_boxed_str()) 20 | } 21 | 22 | #[cfg(feature = "serde")] 23 | pub(crate) fn as_hex(x: &u64, s: S) -> std::result::Result 24 | where 25 | S: serde::Serializer, 26 | { 27 | s.serialize_str(&format!("0x{:X}", x)) 28 | } 29 | 30 | pub(crate) fn find_vmi_offset(offset_maps: &BTreeSet>, offset: u64) -> Option<(u64, u64)> { 31 | let set_index = binary_search_for_map_in_set(offset_maps, offset).ok()?; 32 | let map = offset_maps.iter().nth(set_index)?; 33 | let (_, (segment_no, offset)) = map.range(..=offset).next_back()?; 34 | Some((*segment_no, *offset)) 35 | } 36 | 37 | fn binary_search_for_map_in_set(set: &BTreeSet>, offset: u64) -> Result { 38 | // The zombie counter is used to prevent infinite loops (if the set is malformed, etc.) 39 | let mut zombie_counter = 0; 40 | let mut low = 0; 41 | let mut high = set.len() - 1; 42 | while low <= high { 43 | if zombie_counter > DEFAULT_BINARY_SEARCH_MAX_ITERATIONS { 44 | #[cfg(feature = "log")] 45 | debug!("Malformed VMI map. Exiting."); 46 | return Err(ZffError::new(ZffErrorKind::NotFound,ERROR_BINARY_SEARCH_EXCEEDED_MAX_ITERATIONS)); 47 | } 48 | let mid = (low + high) / 2; 49 | let lowest_offset = set.iter().nth(mid).unwrap().keys().next().unwrap(); //TODO: remove unwraps 50 | let highest_offset = set.iter().nth(mid).unwrap().keys().next_back().unwrap(); //TODO: remove unwraps 51 | if lowest_offset <= &offset && highest_offset >= &offset { 52 | // returns the appropriate set index 53 | return Ok(mid); 54 | } else if lowest_offset > &offset { 55 | // search left 56 | high = mid - 1; 57 | zombie_counter += 1; 58 | } else { 59 | // search right 60 | low = mid + 1; 61 | zombie_counter += 1; 62 | } 63 | } 64 | #[cfg(feature = "log")] 65 | debug!("Empty map"); 66 | Err(ZffError::new(ZffErrorKind::NotFound, ERROR_MAP_EMPTY)) 67 | } 68 | 69 | 70 | #[cfg(feature = "serde")] 71 | /// Serializes `buffer` to a lowercase hex string. 72 | pub fn buffer_to_hex(buffer: &T, serializer: S) -> std::result::Result 73 | where T: AsRef<[u8]>, 74 | S: serde::Serializer 75 | { 76 | serializer.serialize_str(&hex::encode(buffer)) 77 | } 78 | 79 | #[cfg(feature = "serde")] 80 | /// Deserializes a lowercase hex string to a `Vec`. 81 | pub fn hex_to_buffer<'de, D>(deserializer: D) -> std::result::Result, D::Error> 82 | where D: serde::Deserializer<'de> 83 | { 84 | use serde::de::Error; 85 | String::deserialize(deserializer) 86 | .and_then(|string| Vec::from_hex(string).map_err(|err| Error::custom(err.to_string()))) 87 | } 88 | 89 | #[cfg(feature = "serde")] 90 | /// Serializes `buffer` to a lowercase base64 string. 91 | pub fn buffer_to_base64(buffer: &T, serializer: S) -> std::result::Result 92 | where 93 | T: AsRef<[u8]>, 94 | S: serde::Serializer 95 | { 96 | serializer.serialize_str(&base64engine.encode(buffer)) 97 | } 98 | 99 | #[cfg(feature = "serde")] 100 | /// Serializes `buffer` (Option) to a lowecase base64 Option. 101 | pub fn option_buffer_to_base64(buffer: &Option>, serializer: S) -> std::result::Result 102 | where 103 | S: serde::Serializer 104 | { 105 | match buffer { 106 | Some(buffer) => buffer_to_base64(&buffer, serializer), 107 | None => serializer.serialize_none(), 108 | } 109 | } 110 | 111 | #[cfg(feature = "serde")] 112 | /// Deserializes a lowercase base64 string to a `Vec`. 113 | pub fn base64_to_buffer<'de, D>(deserializer: D) -> std::result::Result, D::Error> 114 | where D: serde::Deserializer<'de> 115 | { 116 | use serde::de::Error; 117 | String::deserialize(deserializer).and_then(|string| base64engine.decode(string).map_err(|err| Error::custom(err.to_string()))) 118 | } 119 | 120 | 121 | /// Returns the segment number of a given chunk number. 122 | pub fn get_segment_of_chunk_no(chunk_no: u64, mainfooter_chunkmap: &BTreeMap) -> Option { 123 | // If the chunk_no is exactly matched, return the corresponding value. 124 | if let Some(&value) = mainfooter_chunkmap.get(&chunk_no) { 125 | return Some(value); 126 | } 127 | 128 | // If the chunk_no is higher than the highest key, return None. 129 | if let Some((&highest_chunk_no, _)) = mainfooter_chunkmap.iter().next_back() { 130 | if chunk_no > highest_chunk_no { 131 | return None; 132 | } 133 | } 134 | 135 | // Find the next higher key and return its value. 136 | if let Some((_, &segment_no)) = mainfooter_chunkmap.iter().find(|(&key, _)| key > chunk_no) { 137 | return Some(segment_no); 138 | } 139 | 140 | // If no next higher key is found, it means the chunk_no is higher than all keys, 141 | // so we should return None. 142 | None 143 | } 144 | -------------------------------------------------------------------------------- /src/lib/io/zffreader/redb_handling.rs: -------------------------------------------------------------------------------- 1 | // - Parent 2 | use super::*; 3 | 4 | // - external 5 | 6 | // Will copy a redb to another redb. 7 | pub(crate) fn copy_redb(input_db: &Database, output_db: &mut Database) -> Result<()> { 8 | // prepare read context of input_db 9 | let read_txn = input_db.begin_read()?; 10 | let read_table = read_txn.open_table(PRELOADED_CHUNK_OFFSET_MAP_TABLE)?; 11 | let mut table_iterator = read_table.iter()?; 12 | 13 | // prepare write context of output_db 14 | let write_txn = output_db.begin_write()?; 15 | let mut write_table = write_txn.open_table(PRELOADED_CHUNK_OFFSET_MAP_TABLE)?; 16 | 17 | while let Some(data) = table_iterator.next_back() { 18 | let (key, value) = data?; 19 | write_table.insert(key.value(), value.value())?; 20 | } 21 | 22 | // prepare read context of input_db 23 | let read_table = read_txn.open_table(PRELOADED_CHUNK_SIZE_MAP_TABLE)?; 24 | let mut table_iterator = read_table.iter()?; 25 | let mut write_table = write_txn.open_table(PRELOADED_CHUNK_SIZE_MAP_TABLE)?; 26 | 27 | while let Some(data) = table_iterator.next_back() { 28 | let (key, value) = data?; 29 | write_table.insert(key.value(), value.value())?; 30 | } 31 | 32 | // prepare read context of input_db 33 | let read_table = read_txn.open_table(PRELOADED_CHUNK_FLAGS_MAP_TABLE)?; 34 | let mut table_iterator = read_table.iter()?; 35 | let mut write_table = write_txn.open_table(PRELOADED_CHUNK_FLAGS_MAP_TABLE)?; 36 | 37 | while let Some(data) = table_iterator.next_back() { 38 | let (key, value) = data?; 39 | write_table.insert(key.value(), value.value())?; 40 | } 41 | 42 | // prepare read context of input_db 43 | let read_table = read_txn.open_table(PRELOADED_CHUNK_XXHASH_MAP_TABLE)?; 44 | let mut table_iterator = read_table.iter()?; 45 | let mut write_table = write_txn.open_table(PRELOADED_CHUNK_XXHASH_MAP_TABLE)?; 46 | 47 | while let Some(data) = table_iterator.next_back() { 48 | let (key, value) = data?; 49 | let buf = value.value(); 50 | write_table.insert(key.value(), buf)?; 51 | } 52 | 53 | // prepare read context of input_db 54 | let read_table = read_txn.open_table(PRELOADED_CHUNK_SAME_BYTES_MAP_TABLE)?; 55 | let mut table_iterator = read_table.iter()?; 56 | let mut write_table = write_txn.open_table(PRELOADED_CHUNK_SAME_BYTES_MAP_TABLE)?; 57 | 58 | while let Some(data) = table_iterator.next_back() { 59 | let (key, value) = data?; 60 | write_table.insert(key.value(), value.value())?; 61 | } 62 | 63 | // prepare read context of input_db 64 | let read_table = read_txn.open_table(PRELOADED_CHUNK_DUPLICATION_MAP_TABLE)?; 65 | let mut table_iterator = read_table.iter()?; 66 | let mut write_table = write_txn.open_table(PRELOADED_CHUNK_DUPLICATION_MAP_TABLE)?; 67 | 68 | while let Some(data) = table_iterator.next_back() { 69 | let (key, value) = data?; 70 | write_table.insert(key.value(), value.value())?; 71 | } 72 | 73 | Ok(()) 74 | } 75 | 76 | pub(crate) fn convert_in_memory_preloaded_chunkmaps_into_redb(db: &mut Database, maps: &PreloadedChunkMapsInMemory) -> Result<()> { 77 | initialize_redb_table_chunk_header_map(db, &maps.chunk_header)?; 78 | initialize_redb_table_samebytes_map(db, &maps.same_bytes)?; 79 | initialize_redb_table_dedup_map(db, &maps.duplicate_chunks)?; 80 | Ok(()) 81 | } 82 | 83 | pub(crate) fn initialize_redb_table_chunk_header_map(db: &mut Database, map: &HashMap) -> Result<()> { 84 | let write_txn = db.begin_write()?; 85 | { 86 | let mut table_offset = write_txn.open_table(PRELOADED_CHUNK_OFFSET_MAP_TABLE)?; 87 | let mut table_size = write_txn.open_table(PRELOADED_CHUNK_SIZE_MAP_TABLE)?; 88 | let mut table_flags = write_txn.open_table(PRELOADED_CHUNK_FLAGS_MAP_TABLE)?; 89 | let mut table_integrity_hash = write_txn.open_table(PRELOADED_CHUNK_XXHASH_MAP_TABLE)?; 90 | for (key, value) in map { 91 | table_offset.insert(key, value.offset)?; 92 | table_size.insert(key, value.size)?; 93 | table_flags.insert(key, value.flags.as_bytes())?; 94 | table_integrity_hash.insert(key, value.integrity_hash)?; 95 | } 96 | } 97 | write_txn.commit()?; 98 | Ok(()) 99 | } 100 | 101 | pub(crate) fn initialize_redb_table_samebytes_map(db: &mut Database, map: &HashMap) -> Result<()> { 102 | let write_txn = db.begin_write()?; 103 | { 104 | let mut table = write_txn.open_table(PRELOADED_CHUNK_SAME_BYTES_MAP_TABLE)?; 105 | for (key, value) in map { 106 | table.insert(key, value)?; 107 | } 108 | } 109 | write_txn.commit()?; 110 | Ok(()) 111 | } 112 | 113 | pub(crate) fn initialize_redb_table_dedup_map(db: &mut Database, map: &HashMap) -> Result<()> { 114 | let write_txn = db.begin_write()?; 115 | { 116 | let mut table = write_txn.open_table(PRELOADED_CHUNK_DUPLICATION_MAP_TABLE)?; 117 | for (key, value) in map { 118 | table.insert(key, value)?; 119 | } 120 | } 121 | write_txn.commit()?; 122 | Ok(()) 123 | } 124 | 125 | pub(crate) fn convert_redb_into_in_memory_preloaded_chunkmaps(db: &mut Database) -> Result { 126 | let chunk_header_map = extract_redb_chunk_header_map(db)?; 127 | let samebytes_map = extract_redb_samebytes_map(db)?; 128 | let dedup_map = extract_redb_dedup_map(db)?; 129 | Ok(PreloadedChunkMapsInMemory::with_data( 130 | chunk_header_map, samebytes_map, dedup_map)) 131 | } 132 | 133 | pub(crate) fn extract_redb_chunk_header_map(db: &mut Database) -> Result> { 134 | let mut new_map = HashMap::new(); 135 | let read_txn = db.begin_read()?; 136 | let table_offset = read_txn.open_table(PRELOADED_CHUNK_OFFSET_MAP_TABLE)?; 137 | let table_size = read_txn.open_table(PRELOADED_CHUNK_SIZE_MAP_TABLE)?; 138 | let table_flags = read_txn.open_table(PRELOADED_CHUNK_FLAGS_MAP_TABLE)?; 139 | let table_integrity_hash = read_txn.open_table(PRELOADED_CHUNK_XXHASH_MAP_TABLE)?; 140 | let mut table_iterator = table_offset.iter()?; 141 | while let Some(offset_data) = table_iterator.next_back() { 142 | let (chunk_no, offset) = offset_data?; 143 | let size = table_size.get(chunk_no.value())?.unwrap(); 144 | let flags = table_flags.get(chunk_no.value())?.unwrap(); 145 | let integrity_hash = table_integrity_hash.get(chunk_no.value())?.unwrap(); 146 | let chunk_header = ChunkHeader::new( 147 | offset.value(), 148 | size.value(), 149 | ChunkFlags::from(flags.value()), 150 | integrity_hash.value(), 151 | ); 152 | new_map.insert(chunk_no.value(), chunk_header); 153 | } 154 | Ok(new_map) 155 | } 156 | 157 | pub(crate) fn extract_redb_samebytes_map(db: &mut Database) -> Result> { 158 | let mut new_map = HashMap::new(); 159 | let read_txn = db.begin_read()?; 160 | let table = read_txn.open_table(PRELOADED_CHUNK_SAME_BYTES_MAP_TABLE)?; 161 | let mut table_iterator = table.iter()?; 162 | while let Some(data) = table_iterator.next_back() { 163 | let (key, value) = data?; 164 | new_map.insert(key.value(), value.value()); 165 | } 166 | Ok(new_map) 167 | } 168 | 169 | pub(crate) fn extract_redb_dedup_map(db: &mut Database) -> Result> { 170 | let mut new_map = HashMap::new(); 171 | let read_txn = db.begin_read()?; 172 | let table = read_txn.open_table(PRELOADED_CHUNK_DUPLICATION_MAP_TABLE)?; 173 | let mut table_iterator = table.iter()?; 174 | while let Some(data) = table_iterator.next_back() { 175 | let (key, value) = data?; 176 | new_map.insert(key.value(), value.value()); 177 | } 178 | Ok(new_map) 179 | } -------------------------------------------------------------------------------- /src/lib/mod.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | #![deny(missing_docs)] 3 | //#![deny(warnings)] 4 | //! This crate provides the reference implementation of the forensic file format Zff. 5 | //! Zff is a new file format for forensic images, as an alternative to EWF and AFF. 6 | //! Zff is focused on speed and security. If you want to learn more about ZFF, visit [https://github.com/ph0llux/zff](https://github.com/ph0llux/zff). 7 | 8 | // adds #![feature(windows_by_handle)] to the crate for windows platforms only. 9 | #![cfg_attr(target_os = "windows", feature(windows_by_handle))] 10 | 11 | 12 | // - modules 13 | /// This module contains all constants, used in this crate. 14 | pub mod constants; 15 | /// This module contains all header, could be found in the zff specification (header version 1 and header version 2). 16 | pub mod header; 17 | /// This module contains all footer, could be found in the zff specification (footer version 1 and footer version 2). 18 | pub mod footer; 19 | /// Contains several stuff to handle zff container (e.g. create, extend or read zff container). 20 | pub mod io; 21 | /// Contains some little helper functions 22 | pub mod helper; 23 | mod hashing; 24 | mod compression; 25 | /// Contains various functions, methods and traits to handle encryption in zff. 26 | pub mod encryption; 27 | mod traits; 28 | mod error; 29 | mod signatures; 30 | mod file_extension; 31 | mod object; 32 | mod file; 33 | mod segment; 34 | mod chunk; 35 | 36 | // - re-exports 37 | pub use hashing::*; 38 | pub use compression::*; 39 | pub use encryption::*; 40 | pub use error::*; 41 | pub use signatures::*; 42 | pub use traits::*; 43 | pub use file_extension::*; 44 | use constants::*; 45 | pub use object::*; 46 | pub use file::*; 47 | pub use segment::*; 48 | pub use chunk::*; 49 | 50 | // - types 51 | /// Result for std::result::Result. 52 | pub type Result = std::result::Result; -------------------------------------------------------------------------------- /src/lib/signatures.rs: -------------------------------------------------------------------------------- 1 | // - external 2 | use ed25519_dalek::{ 3 | SigningKey, 4 | VerifyingKey, 5 | Signature as Ed25519Signature, 6 | Signer, 7 | Verifier, 8 | KEYPAIR_LENGTH, 9 | SECRET_KEY_LENGTH, 10 | PUBLIC_KEY_LENGTH 11 | }; 12 | use rand::{rng, RngCore}; 13 | use base64::{Engine, engine::general_purpose::STANDARD as base64engine}; 14 | 15 | // - internal 16 | use crate::{ 17 | Result, 18 | ZffError, 19 | ZffErrorKind, 20 | constants::*, 21 | }; 22 | 23 | 24 | /// structure contains serveral methods to handle signing of chunked data. 25 | pub struct Signature; 26 | 27 | impl Signature { 28 | /// generates a new, random keypair. 29 | pub fn new_signing_key() -> SigningKey { 30 | let mut csprng = rng(); 31 | let mut secret_key = [0u8; SECRET_KEY_LENGTH]; 32 | csprng.fill_bytes(&mut secret_key); 33 | SigningKey::from_bytes(&secret_key) 34 | } 35 | 36 | /// returns a signingkey, parsed from the input data (formatted as base64).\ 37 | /// Input data can be a secret key (32 bytes) or a secret/public keypair (64 bytes). 38 | pub fn new_signingkey_from_base64>(key: K) -> Result { 39 | //decodes the base64 content. 40 | let key = base64engine.decode(key.into())?; 41 | // check if the content is a keypair or a secret key 42 | if key.len() == KEYPAIR_LENGTH { 43 | let mut key_slice = [0u8; KEYPAIR_LENGTH]; 44 | key_slice.copy_from_slice(&key); 45 | Ok(SigningKey::from_keypair_bytes(&key_slice)?) 46 | } else if key.len() == SECRET_KEY_LENGTH { 47 | let mut key_slice = [0u8; SECRET_KEY_LENGTH]; 48 | key_slice.copy_from_slice(&key); 49 | Ok(SigningKey::from_bytes(&key_slice)) 50 | } else { 51 | Err(ZffError::new(ZffErrorKind::SigningError, ERROR_WRONG_SIGNATURE_KEY_LENGTH)) 52 | } 53 | } 54 | 55 | /// Converts bytes of a secret key or a keypair into a SigningKey 56 | pub fn bytes_to_signingkey>(key: K) -> Result { 57 | let key = key.as_ref(); 58 | // check if the content is a keypair or a secret key 59 | if key.len() == KEYPAIR_LENGTH { 60 | let mut key_slice = [0u8; KEYPAIR_LENGTH]; 61 | key_slice.copy_from_slice(key); 62 | Ok(SigningKey::from_keypair_bytes(&key_slice)?) 63 | } else if key.len() == SECRET_KEY_LENGTH { 64 | let mut key_slice = [0u8; SECRET_KEY_LENGTH]; 65 | key_slice.copy_from_slice(key); 66 | Ok(SigningKey::from_bytes(&key_slice)) 67 | } else { 68 | Err(ZffError::new(ZffErrorKind::SigningError, ERROR_WRONG_SIGNATURE_KEY_LENGTH)) 69 | } 70 | } 71 | 72 | /// sign the data with the given signing key. 73 | pub fn sign(signing_key: &SigningKey, message: &[u8]) -> [u8; ED25519_DALEK_SIGNATURE_LEN] { 74 | let signature = signing_key.sign(message); 75 | signature.to_bytes() 76 | } 77 | 78 | /// verify the data with the given base64 encoded key (signing key or verifying keys are possible to use here). 79 | pub fn verify_with_base64_key>(key: K, message: &[u8], signature: [u8; ED25519_DALEK_SIGNATURE_LEN]) -> Result { 80 | let key = base64engine.decode(key.into())?; 81 | Signature::verify(key, message, signature) 82 | } 83 | 84 | /// verify the data with the given key bytes (signing key or verifying keys are possible to use here). 85 | pub fn verify(key: K, message: &[u8], signature: [u8; ED25519_DALEK_SIGNATURE_LEN]) -> Result 86 | where 87 | K: AsRef<[u8]> 88 | { 89 | let key = key.as_ref(); 90 | // check if the content is a signing key, a secret key or a verifying key. 91 | let verifying_key = if key.len() == KEYPAIR_LENGTH { // if the key is a signing key 92 | let mut key_slice = [0u8; KEYPAIR_LENGTH]; 93 | key_slice.copy_from_slice(key); 94 | let sign_key = SigningKey::from_keypair_bytes(&key_slice)?; 95 | sign_key.verifying_key() 96 | } else if key.len() == PUBLIC_KEY_LENGTH { 97 | let mut key_slice = [0u8; PUBLIC_KEY_LENGTH]; 98 | key_slice.copy_from_slice(key); 99 | VerifyingKey::from_bytes(&key_slice)? 100 | } else { 101 | return Err(ZffError::new(ZffErrorKind::SigningError,ERROR_WRONG_SIGNATURE_KEY_LENGTH)); 102 | }; 103 | let signature = Ed25519Signature::from_bytes(&signature); 104 | match verifying_key.verify(message, &signature) { 105 | Ok(_) => Ok(true), 106 | Err(_) => Ok(false), 107 | } 108 | } 109 | } --------------------------------------------------------------------------------