diff options
Diffstat (limited to 'src/zip/file')
| -rw-r--r-- | src/zip/file/info.rs | 43 | ||||
| -rw-r--r-- | src/zip/file/read.rs | 99 |
2 files changed, 107 insertions, 35 deletions
diff --git a/src/zip/file/info.rs b/src/zip/file/info.rs index 599dcc3..38ea984 100644 --- a/src/zip/file/info.rs +++ b/src/zip/file/info.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use crate::driver::ArchiveFileInfo; | 1 | use crate::driver::ArchiveFileInfo; |
| 2 | use crate::zip::{ZipError, ZipResult}; | 2 | use crate::zip::datetime::DosDateTime; |
| 3 | use chrono::{DateTime, Local}; | 3 | use chrono::{DateTime, Local}; |
| 4 | 4 | ||
| 5 | #[derive(Debug, PartialEq, Eq, Clone)] | 5 | #[derive(Debug, PartialEq, Eq, Clone)] |
| @@ -14,42 +14,30 @@ pub enum CompressionMethod { | |||
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | impl CompressionMethod { | 16 | impl CompressionMethod { |
| 17 | pub(crate) fn from_struct_id(id: u16) -> ZipResult<Self> { | 17 | #[inline] |
| 18 | Ok(match id { | 18 | pub(crate) fn from_struct_id(id: u16) -> Self { |
| 19 | match id { | ||
| 19 | 0 => Self::Store, | 20 | 0 => Self::Store, |
| 20 | 8 => Self::Deflate, | 21 | 8 => Self::Deflate, |
| 21 | 12 => Self::BZip2, | 22 | 12 => Self::BZip2, |
| 22 | 14 => Self::Lzma, | 23 | 14 => Self::Lzma, |
| 23 | 93 => Self::Zstd, | 24 | 93 => Self::Zstd, |
| 24 | 95 => Self::Xz, | 25 | 95 => Self::Xz, |
| 25 | 1..=7 | 9..=11 | 13 | 15..=20 | 94 | 96..=99 => Self::Unsupported(id), | 26 | _ => Self::Unsupported(id), |
| 26 | 21..=92 | 100.. => return Err(ZipError::InvalidCompressionMethod(id).into()), | 27 | } |
| 27 | }) | ||
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | #[derive(Debug, PartialEq, Eq, Clone)] | 31 | #[derive(Debug, PartialEq, Eq, Clone)] |
| 32 | pub enum EncryptionMethod { | 32 | pub enum EncryptionMethod { |
| 33 | None, | 33 | None, |
| 34 | Weak(u8), | 34 | Weak, // ZipCrypto |
| 35 | Aes128, // WinZip encryption | ||
| 36 | Aes192, | ||
| 37 | Aes256, | ||
| 35 | Unsupported, | 38 | Unsupported, |
| 36 | } | 39 | } |
| 37 | 40 | ||
| 38 | impl EncryptionMethod { | ||
| 39 | pub(crate) fn from_bif_flag(bit_flag: BitFlag, crc: u32, dos_time: u16) -> EncryptionMethod { | ||
| 40 | match (bit_flag.is_encrypted(), bit_flag.is_strong_encryption()) { | ||
| 41 | (false, false) => EncryptionMethod::None, | ||
| 42 | (true, false) => EncryptionMethod::Weak(if bit_flag.is_has_data_descriptor() { | ||
| 43 | (dos_time >> 8) as u8 // Info-ZIP modification | ||
| 44 | } else { | ||
| 45 | (crc >> 24) as u8 | ||
| 46 | }), | ||
| 47 | (true, true) => EncryptionMethod::Unsupported, | ||
| 48 | _ => panic!("impossible"), | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 41 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| 54 | pub struct BitFlag { | 42 | pub struct BitFlag { |
| 55 | flag: u16, | 43 | flag: u16, |
| @@ -159,7 +147,7 @@ pub struct ZipFileInfo { | |||
| 159 | } | 147 | } |
| 160 | 148 | ||
| 161 | impl ZipFileInfo { | 149 | impl ZipFileInfo { |
| 162 | pub fn new( | 150 | pub(crate) fn new( |
| 163 | compression_method: CompressionMethod, | 151 | compression_method: CompressionMethod, |
| 164 | encryption_method: EncryptionMethod, | 152 | encryption_method: EncryptionMethod, |
| 165 | bit_flag: BitFlag, | 153 | bit_flag: BitFlag, |
| @@ -189,6 +177,15 @@ impl ZipFileInfo { | |||
| 189 | } | 177 | } |
| 190 | } | 178 | } |
| 191 | 179 | ||
| 180 | #[inline] | ||
| 181 | pub(crate) fn password_check(&self) -> u8 { | ||
| 182 | if self.bit_flag.is_has_data_descriptor() { | ||
| 183 | (self.mtime.to_dos_time() >> 8) as u8 | ||
| 184 | } else { | ||
| 185 | (self.crc >> 24) as u8 | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 192 | pub fn is_dir(&self) -> bool { | 189 | pub fn is_dir(&self) -> bool { |
| 193 | self.name.ends_with("/") | 190 | self.name.ends_with("/") |
| 194 | } | 191 | } |
diff --git a/src/zip/file/read.rs b/src/zip/file/read.rs index c26b304..80bdcfb 100644 --- a/src/zip/file/read.rs +++ b/src/zip/file/read.rs | |||
| @@ -1,11 +1,16 @@ | |||
| 1 | use crate::driver::FileDriver; | 1 | use crate::driver::FileDriver; |
| 2 | use crate::utils::{IoCursor, ReadUtils}; | 2 | use crate::utils::{IoCursor, ReadUtils}; |
| 3 | use crate::zip::encryption::WeakDecoder; | 3 | use crate::zip::encryption::{AesDecoder, Keys, WeakDecoder}; |
| 4 | use crate::zip::structs::FILE_HEADER_SIGNATURE; | ||
| 4 | use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult}; | 5 | use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult}; |
| 6 | use aes::cipher::KeyInit; | ||
| 7 | use aes::{Aes128, Aes192, Aes256}; | ||
| 5 | use bzip2::read::BzDecoder; | 8 | use bzip2::read::BzDecoder; |
| 6 | use flate2::read::DeflateDecoder; | 9 | use flate2::read::DeflateDecoder; |
| 7 | use liblzma::read::XzDecoder; | 10 | use liblzma::read::XzDecoder; |
| 8 | use liblzma::stream::{Filters, LzmaOptions, Stream}; | 11 | use liblzma::stream::{Filters, LzmaOptions, Stream}; |
| 12 | use pbkdf2::pbkdf2_hmac_array; | ||
| 13 | use sha1::Sha1; | ||
| 9 | use std::io::{ | 14 | use std::io::{ |
| 10 | BufReader, Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, | 15 | BufReader, Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, |
| 11 | }; | 16 | }; |
| @@ -14,34 +19,101 @@ use zstd::stream::Decoder as ZstdDecoder; | |||
| 14 | enum Encryption<Io: Read> { | 19 | enum Encryption<Io: Read> { |
| 15 | None(Io), | 20 | None(Io), |
| 16 | Weak(WeakDecoder<Io>), | 21 | Weak(WeakDecoder<Io>), |
| 22 | Aes128(AesDecoder<Io, Aes128>), | ||
| 23 | Aes192(AesDecoder<Io, Aes192>), | ||
| 24 | Aes256(AesDecoder<Io, Aes256>), | ||
| 17 | } | 25 | } |
| 18 | 26 | ||
| 19 | impl<Io: Read> Encryption<Io> { | 27 | impl<Io: Read> Encryption<Io> { |
| 20 | pub fn new(io: Io, info: &ZipFileInfo, password: Option<&[u8]>) -> ZipResult<Self> { | 28 | #[inline] |
| 29 | pub fn new(mut io: Io, info: &ZipFileInfo, password: Option<&[u8]>) -> ZipResult<Self> { | ||
| 21 | Ok(match info.encryption_method { | 30 | Ok(match info.encryption_method { |
| 22 | EncryptionMethod::None => Self::None(io), | 31 | EncryptionMethod::None => Self::None(io), |
| 23 | EncryptionMethod::Weak(check) => Self::Weak(WeakDecoder::new( | 32 | EncryptionMethod::Weak => { |
| 24 | io, | 33 | let mut keys = Keys::new(); |
| 25 | check, | 34 | keys.set_password(password.ok_or(ZipError::PasswordIsNotSpecified)?); |
| 26 | password.ok_or(ZipError::PasswordIsNotSpecified)?, | 35 | |
| 27 | )?), | 36 | let check = keys.set_header(io.read_arr()?); |
| 28 | EncryptionMethod::Unsupported => { | 37 | if check != info.password_check() { |
| 29 | return Err(ZipError::UnsupportedEncryptionMethod.into()) | 38 | return Err(ZipError::IncorrectPassword); |
| 39 | } | ||
| 40 | |||
| 41 | Self::Weak(WeakDecoder::new(io, keys)) | ||
| 42 | } | ||
| 43 | EncryptionMethod::Aes128 => { | ||
| 44 | let header = io.read_arr::<10>()?; | ||
| 45 | let salt = &header[..8]; | ||
| 46 | let check = &header[8..]; | ||
| 47 | |||
| 48 | let hash = pbkdf2_hmac_array::<Sha1, 34>( | ||
| 49 | password.ok_or(ZipError::PasswordIsNotSpecified)?, | ||
| 50 | salt, | ||
| 51 | 1000, | ||
| 52 | ); | ||
| 53 | let key = &hash[..16]; | ||
| 54 | |||
| 55 | if check != &hash[32..] { | ||
| 56 | return Err(ZipError::IncorrectPassword); | ||
| 57 | } | ||
| 58 | |||
| 59 | Self::Aes128(AesDecoder::new(io, Aes128::new(key.into()))?) | ||
| 60 | } | ||
| 61 | EncryptionMethod::Aes192 => { | ||
| 62 | let header = io.read_arr::<14>()?; | ||
| 63 | let salt = &header[..12]; | ||
| 64 | let check = &header[12..]; | ||
| 65 | |||
| 66 | let hash = pbkdf2_hmac_array::<Sha1, 50>( | ||
| 67 | password.ok_or(ZipError::PasswordIsNotSpecified)?, | ||
| 68 | salt, | ||
| 69 | 1000, | ||
| 70 | ); | ||
| 71 | let key = &hash[..24]; | ||
| 72 | |||
| 73 | if check != &hash[48..] { | ||
| 74 | return Err(ZipError::IncorrectPassword); | ||
| 75 | } | ||
| 76 | |||
| 77 | Self::Aes192(AesDecoder::new(io, Aes192::new(key.into()))?) | ||
| 78 | } | ||
| 79 | EncryptionMethod::Aes256 => { | ||
| 80 | let header = io.read_arr::<18>()?; | ||
| 81 | let salt = &header[..16]; | ||
| 82 | let check = &header[16..]; | ||
| 83 | |||
| 84 | let hash = pbkdf2_hmac_array::<Sha1, 66>( | ||
| 85 | password.ok_or(ZipError::PasswordIsNotSpecified)?, | ||
| 86 | salt, | ||
| 87 | 1000, | ||
| 88 | ); | ||
| 89 | let key = &hash[..32]; | ||
| 90 | |||
| 91 | if check != &hash[64..] { | ||
| 92 | return Err(ZipError::IncorrectPassword); | ||
| 93 | } | ||
| 94 | |||
| 95 | Self::Aes256(AesDecoder::new(io, Aes256::new(key.into()))?) | ||
| 30 | } | 96 | } |
| 97 | EncryptionMethod::Unsupported => return Err(ZipError::UnsupportedEncryptionMethod), | ||
| 31 | }) | 98 | }) |
| 32 | } | 99 | } |
| 33 | } | 100 | } |
| 34 | 101 | ||
| 35 | impl<Io: Read> Read for Encryption<Io> { | 102 | impl<Io: Read> Read for Encryption<Io> { |
| 103 | #[inline] | ||
| 36 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | 104 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
| 37 | match self { | 105 | match self { |
| 38 | Self::None(io) => io.read(buf), | 106 | Self::None(io) => io.read(buf), |
| 39 | Self::Weak(io) => io.read(buf), | 107 | Self::Weak(io) => io.read(buf), |
| 108 | Self::Aes128(io) => io.read(buf), | ||
| 109 | Self::Aes192(io) => io.read(buf), | ||
| 110 | Self::Aes256(io) => io.read(buf), | ||
| 40 | } | 111 | } |
| 41 | } | 112 | } |
| 42 | } | 113 | } |
| 43 | 114 | ||
| 44 | impl<Io: Read + Seek> Seek for Encryption<Io> { | 115 | impl<Io: Read + Seek> Seek for Encryption<Io> { |
| 116 | #[inline] | ||
| 45 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { | 117 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { |
| 46 | match self { | 118 | match self { |
| 47 | Self::None(io) => io.seek(pos), | 119 | Self::None(io) => io.seek(pos), |
| @@ -62,6 +134,7 @@ enum Compression<Io: Read> { | |||
| 62 | } | 134 | } |
| 63 | 135 | ||
| 64 | impl<Io: Read + Seek> Compression<Io> { | 136 | impl<Io: Read + Seek> Compression<Io> { |
| 137 | #[inline] | ||
| 65 | pub fn new(mut io: Io, info: &ZipFileInfo) -> ZipResult<Self> { | 138 | pub fn new(mut io: Io, info: &ZipFileInfo) -> ZipResult<Self> { |
| 66 | Ok(match info.compression_method { | 139 | Ok(match info.compression_method { |
| 67 | CompressionMethod::Store => Self::Store(io), | 140 | CompressionMethod::Store => Self::Store(io), |
| @@ -88,13 +161,14 @@ impl<Io: Read + Seek> Compression<Io> { | |||
| 88 | CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?), | 161 | CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?), |
| 89 | CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)), | 162 | CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)), |
| 90 | CompressionMethod::Unsupported(id) => { | 163 | CompressionMethod::Unsupported(id) => { |
| 91 | return Err(ZipError::UnsupportedCompressionMethod(id).into()) | 164 | return Err(ZipError::UnsupportedCompressionMethod(id)) |
| 92 | } | 165 | } |
| 93 | }) | 166 | }) |
| 94 | } | 167 | } |
| 95 | } | 168 | } |
| 96 | 169 | ||
| 97 | impl<Io: Read> Read for Compression<Io> { | 170 | impl<Io: Read> Read for Compression<Io> { |
| 171 | #[inline] | ||
| 98 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | 172 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
| 99 | match self { | 173 | match self { |
| 100 | Compression::Store(io) => io.read(buf), | 174 | Compression::Store(io) => io.read(buf), |
| @@ -107,6 +181,7 @@ impl<Io: Read> Read for Compression<Io> { | |||
| 107 | } | 181 | } |
| 108 | 182 | ||
| 109 | impl<Io: Read + Seek> Seek for Compression<Io> { | 183 | impl<Io: Read + Seek> Seek for Compression<Io> { |
| 184 | #[inline] | ||
| 110 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { | 185 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { |
| 111 | match self { | 186 | match self { |
| 112 | Compression::Store(io) => io.seek(pos), | 187 | Compression::Store(io) => io.seek(pos), |
| @@ -137,8 +212,8 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | |||
| 137 | io.seek(SeekFrom::Start(info.header_pointer))?; | 212 | io.seek(SeekFrom::Start(info.header_pointer))?; |
| 138 | 213 | ||
| 139 | let buf = io.read_arr::<30>()?; | 214 | let buf = io.read_arr::<30>()?; |
| 140 | if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 { | 215 | if buf[..4] != FILE_HEADER_SIGNATURE { |
| 141 | return Err(ZipError::InvalidFileHeaderSignature.into()); | 216 | return Err(ZipError::InvalidFileHeaderSignature); |
| 142 | } | 217 | } |
| 143 | let data_pointer = info.header_pointer | 218 | let data_pointer = info.header_pointer |
| 144 | + 30 | 219 | + 30 |
