From a83767f9fbd51df654901b52bdba7838f6a10bf9 Mon Sep 17 00:00:00 2001 From: Igor Tolmachev Date: Tue, 16 Jul 2024 01:59:53 +0900 Subject: Add traditional PKWARE decryption. - Compression and encryption may not work together - Password check is not yet implemented - Unoptimized crc32 function --- src/zip/file/read.rs | 159 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 108 insertions(+), 51 deletions(-) (limited to 'src/zip/file/read.rs') diff --git a/src/zip/file/read.rs b/src/zip/file/read.rs index f5a54f3..aa665c3 100644 --- a/src/zip/file/read.rs +++ b/src/zip/file/read.rs @@ -1,6 +1,7 @@ use crate::driver::FileDriver; use crate::utils::{IoCursor, ReadUtils}; -use crate::zip::{CompressionMethod, ZipError, ZipFileInfo, ZipResult}; +use crate::zip::encryption::WeakDecoder; +use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult}; use bzip2::read::BzDecoder; use flate2::read::DeflateDecoder; use liblzma::read::XzDecoder; @@ -10,6 +11,47 @@ use std::io::{ }; use zstd::stream::Decoder as ZstdDecoder; +enum Encryption { + None(Io), + Weak(WeakDecoder), +} + +impl Encryption { + pub fn new(io: Io, method: EncryptionMethod, password: Option<&str>) -> ZipResult { + Ok(match method { + EncryptionMethod::None => Self::None(io), + EncryptionMethod::Weak => Self::Weak(WeakDecoder::new( + io, + password.ok_or(ZipError::PasswordIsNotSpecified)?, + )?), + EncryptionMethod::Unsupported => { + return Err(ZipError::UnsupportedEncryptionMethod.into()) + } + }) + } +} + +impl Read for Encryption { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + match self { + Self::None(io) => io.read(buf), + Self::Weak(io) => io.read(buf), + } + } +} + +impl Seek for Encryption { + fn seek(&mut self, pos: SeekFrom) -> IoResult { + match self { + Self::None(io) => io.seek(pos), + _ => Err(IoError::new( + IoErrorKind::Unsupported, + ZipError::EncryptedDataIsUnseekable, + )), + } + } +} + enum Compression { Store(Io), Deflate(DeflateDecoder), @@ -18,8 +60,48 @@ enum Compression { Xz(XzDecoder), } +impl Compression { + pub fn new(io: Io, method: CompressionMethod) -> ZipResult { + Ok(match method { + CompressionMethod::Store => Self::Store(io), + CompressionMethod::Deflate => Self::Deflate(DeflateDecoder::new(io)), + CompressionMethod::BZip2 => Self::BZip2(BzDecoder::new(io)), + CompressionMethod::Lzma => panic!(), + CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?), + CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)), + CompressionMethod::Unsupported(id) => { + return Err(ZipError::UnsupportedCompressionMethod(id).into()) + } + }) + } +} + +impl Read for Compression { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + match self { + Compression::Store(io) => io.read(buf), + Compression::Deflate(io) => io.read(buf), + Compression::BZip2(io) => io.read(buf), + Compression::Zstd(io) => io.read(buf), + Compression::Xz(io) => io.read(buf), + } + } +} + +impl Seek for Compression { + fn seek(&mut self, pos: SeekFrom) -> IoResult { + match self { + Compression::Store(io) => io.seek(pos), + _ => Err(IoError::new( + IoErrorKind::Unsupported, + ZipError::CompressedDataIsUnseekable, + )), + } + } +} + pub struct ZipFileReader<'d, Io: Read> { - io: Compression>, + io: Compression>>, info: &'d ZipFileInfo, } @@ -33,7 +115,7 @@ impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> { } impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { - pub fn new(io: &'d mut Io, info: &'d ZipFileInfo) -> ZipResult { + pub fn new(io: &'d mut Io, info: &'d ZipFileInfo, password: Option<&str>) -> ZipResult { io.seek(SeekFrom::Start(info.header_pointer))?; let buf = io.read_arr::<30>()?; @@ -45,27 +127,22 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; + io.seek(SeekFrom::Start(data_pointer))?; + Ok(Self { io: match info.compression_method { - CompressionMethod::Store => Compression::Store(IoCursor::new( - io, - data_pointer, - data_pointer + info.compressed_size, - )?), - CompressionMethod::Deflate => Compression::Deflate(DeflateDecoder::new( - IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?, - )), - CompressionMethod::BZip2 => Compression::BZip2(BzDecoder::new(IoCursor::new( - io, - data_pointer, - data_pointer + info.compressed_size, - )?)), CompressionMethod::Lzma => { - io.seek(SeekFrom::Start(data_pointer))?; let buf = io.read_arr::<9>()?; - Compression::Xz(XzDecoder::new_stream( - IoCursor::new(io, data_pointer + 9, data_pointer + info.compressed_size)?, + Encryption::new( + IoCursor::new( + io, + data_pointer + 9, + data_pointer + info.compressed_size, + )?, + info.encryption_method, + password, + )?, Stream::new_raw_decoder( Filters::new().lzma1( LzmaOptions::new() @@ -80,30 +157,22 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { .unwrap(), )) } - CompressionMethod::Zstd => Compression::Zstd( - ZstdDecoder::new(IoCursor::new( - io, - data_pointer, - data_pointer + info.compressed_size, - )?) - .unwrap(), - ), - CompressionMethod::Xz => Compression::Xz(XzDecoder::new(IoCursor::new( - io, - data_pointer, - data_pointer + info.compressed_size, - )?)), - CompressionMethod::Unsupported => { - return Err(ZipError::UnsupportedCompressionMethod.into()) - } + _ => Compression::new( + Encryption::new( + IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?, + info.encryption_method, + password, + )?, + info.compression_method, + )?, }, info, }) } - pub fn seekable(&self) -> bool { + pub fn is_seekable(&self) -> bool { match self.io { - Compression::Store(..) => true, + Compression::Store(Encryption::None(..)) => true, _ => false, } } @@ -111,24 +180,12 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { impl<'d, Io: Read> Read for ZipFileReader<'d, Io> { fn read(&mut self, buf: &mut [u8]) -> IoResult { - match &mut self.io { - Compression::Store(io) => io.read(buf), - Compression::Deflate(io) => io.read(buf), - Compression::BZip2(io) => io.read(buf), - Compression::Zstd(io) => io.read(buf), - Compression::Xz(io) => io.read(buf), - } + self.io.read(buf) } } impl<'d, Io: Read + Seek> Seek for ZipFileReader<'d, Io> { fn seek(&mut self, pos: SeekFrom) -> IoResult { - match &mut self.io { - Compression::Store(io) => io.seek(pos), - _ => Err(IoError::new( - IoErrorKind::Unsupported, - ZipError::CompressedDataIsUnseekable, - )), - } + self.io.seek(pos) } } -- cgit v1.2.3