use std::borrow::Borrow;
use std::io::{Read,Seek,SeekFrom};
use std::collections::HashMap;
use crate::{
Result,
HeaderCoding,
header::version1::{SegmentHeader, ChunkHeader},
footer::version1::{SegmentFooter},
ZffError,
ZffErrorKind,
CompressionAlgorithm,
Encryption,
EncryptionAlgorithm,
Signature,
ED25519_DALEK_PUBKEY_LEN
};
use zstd;
use lz4_flex;
pub struct Segment<R: Read + Seek> {
header: SegmentHeader,
data: R,
chunk_offsets: HashMap<u64, u64> }
impl<R: 'static + Read + Seek> Segment<R> {
fn new(header: SegmentHeader, data: R, chunk_offsets: HashMap<u64, u64>) -> Segment<R> {
Self {
header,
data,
chunk_offsets,
}
}
pub fn new_from_reader(mut data: R) -> Result<Segment<R>> {
let stream_position = data.stream_position()?; let segment_header = SegmentHeader::decode_directly(&mut data)?;
let footer_offset = segment_header.footer_offset();
let initial_chunk_header = ChunkHeader::decode_directly(&mut data)?;
let initial_chunk_number = initial_chunk_header.chunk_number();
data.seek(SeekFrom::Start(footer_offset))?;
let segment_footer = SegmentFooter::decode_directly(&mut data)?;
let mut chunk_offsets = HashMap::new();
let mut chunk_number = initial_chunk_number;
for offset in segment_footer.chunk_offsets() {
chunk_offsets.insert(chunk_number, *offset);
chunk_number += 1;
}
data.seek(SeekFrom::Start(stream_position))?;
let _ = SegmentHeader::decode_directly(&mut data)?;
Ok(Self::new(segment_header, data, chunk_offsets))
}
pub fn chunk_data<C>(&mut self, chunk_number: u64, compression_algorithm: C) -> Result<Vec<u8>>
where
C: Borrow<CompressionAlgorithm>,
{
let chunk_offset = match self.chunk_offsets.get(&chunk_number) {
Some(offset) => offset,
None => return Err(ZffError::new(ZffErrorKind::DataDecodeChunkNumberNotInSegment, chunk_number.to_string()))
};
self.data.seek(SeekFrom::Start(*chunk_offset))?;
let chunk_header = ChunkHeader::decode_directly(&mut self.data)?;
let chunk_size = chunk_header.chunk_size();
self.data.seek(SeekFrom::Start(chunk_header.header_size() as u64 + *chunk_offset))?;
let mut chunk_data = vec![0; *chunk_size as usize];
self.data.read_exact(&mut chunk_data)?;
let mut buffer = Vec::new();
if !chunk_header.compression_flag() {
return Ok(chunk_data);
};
match compression_algorithm.borrow() {
CompressionAlgorithm::None => {
Ok(chunk_data)
}
CompressionAlgorithm::Zstd => {
let mut decoder = zstd::stream::read::Decoder::new(chunk_data.as_slice())?;
decoder.read_to_end(&mut buffer)?;
Ok(buffer)
},
CompressionAlgorithm::Lz4 => {
let mut decompressor = lz4_flex::frame::FrameDecoder::new(chunk_data.as_slice());
decompressor.read_to_end(&mut buffer)?;
Ok(buffer)
}
}
}
pub fn chunk_data_decrypted<C, K, E>(
&mut self,
chunk_number: u64,
compression_algorithm: C,
decryption_key: K,
encryption_algorithm: E) -> Result<Vec<u8>>
where
C: Borrow<CompressionAlgorithm>,
K: AsRef<[u8]>,
E: Borrow<EncryptionAlgorithm>,
{
let chunk_offset = match self.chunk_offsets.get(&chunk_number) {
Some(offset) => offset,
None => return Err(ZffError::new(ZffErrorKind::DataDecodeChunkNumberNotInSegment, chunk_number.to_string()))
};
self.data.seek(SeekFrom::Start(*chunk_offset))?;
let chunk_header = ChunkHeader::decode_directly(&mut self.data)?;
let chunk_size = chunk_header.chunk_size();
self.data.seek(SeekFrom::Start(chunk_header.header_size() as u64 + *chunk_offset))?;
let mut encrypted_data = vec![0; *chunk_size as usize];
self.data.read_exact(&mut encrypted_data)?;
let decrypted_chunk_data = Encryption::decrypt_message(decryption_key, encrypted_data, chunk_number, encryption_algorithm)?;
if !chunk_header.compression_flag() {
return Ok(decrypted_chunk_data);
};
match compression_algorithm.borrow() {
CompressionAlgorithm::None => {
Ok(decrypted_chunk_data)
}
CompressionAlgorithm::Zstd => {
let mut decompressed_buffer = Vec::new();
let mut decoder = zstd::stream::read::Decoder::new(decrypted_chunk_data.as_slice())?;
decoder.read_to_end(&mut decompressed_buffer)?;
Ok(decompressed_buffer)
},
CompressionAlgorithm::Lz4 => {
let mut decompressed_buffer = Vec::new();
let mut decompressor = lz4_flex::frame::FrameDecoder::new(decrypted_chunk_data.as_slice());
decompressor.read_to_end(&mut decompressed_buffer)?;
Ok(decompressed_buffer)
}
}
}
pub fn verify_chunk<C>(&mut self, chunk_number: u64, compression_algorithm: C, publickey: [u8; ED25519_DALEK_PUBKEY_LEN]) -> Result<bool>
where
C: Borrow<CompressionAlgorithm>,
{
let chunk_data = self.chunk_data(chunk_number, compression_algorithm)?;
let chunk_offset = match self.chunk_offsets.get(&chunk_number) {
Some(offset) => offset,
None => return Err(ZffError::new(ZffErrorKind::DataDecodeChunkNumberNotInSegment, chunk_number.to_string()))
};
self.data.seek(SeekFrom::Start(*chunk_offset))?;
let chunk_header = ChunkHeader::decode_directly(&mut self.data)?;
let signature = match chunk_header.signature() {
Some(sig) => sig,
None => return Err(ZffError::new(ZffErrorKind::NoSignatureFoundAtChunk, "")),
};
Signature::verify(publickey, &chunk_data, *signature)
}
pub fn verify_chunk_decrypted<C, E, K>(
&mut self,
chunk_number: u64,
compression_algorithm: C,
decryption_key: K,
encryption_algorithm: E,
publickey: [u8; ED25519_DALEK_PUBKEY_LEN]) -> Result<bool>
where
C: Borrow<CompressionAlgorithm>,
K: AsRef<[u8]>,
E: Borrow<EncryptionAlgorithm>,
{
let chunk_data = self.chunk_data_decrypted(chunk_number, compression_algorithm, decryption_key, encryption_algorithm)?;
let chunk_offset = match self.chunk_offsets.get(&chunk_number) {
Some(offset) => offset,
None => return Err(ZffError::new(ZffErrorKind::DataDecodeChunkNumberNotInSegment, chunk_number.to_string()))
};
self.data.seek(SeekFrom::Start(*chunk_offset))?;
let chunk_header = ChunkHeader::decode_directly(&mut self.data)?;
let signature = match chunk_header.signature() {
Some(sig) => sig,
None => return Err(ZffError::new(ZffErrorKind::NoSignatureFoundAtChunk, "")),
};
Signature::verify(publickey, &chunk_data, *signature)
}
pub fn header(&self) -> &SegmentHeader {
&self.header
}
pub fn chunk_offsets(&self) -> &HashMap<u64, u64> {
&self.chunk_offsets
}
pub fn data(&self) -> &R {
&self.data
}
}