aboutsummaryrefslogtreecommitdiff
path: root/src/zip/file
diff options
context:
space:
mode:
Diffstat (limited to 'src/zip/file')
-rw-r--r--src/zip/file/info.rs43
-rw-r--r--src/zip/file/read.rs99
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 @@
1use crate::driver::ArchiveFileInfo; 1use crate::driver::ArchiveFileInfo;
2use crate::zip::{ZipError, ZipResult}; 2use crate::zip::datetime::DosDateTime;
3use chrono::{DateTime, Local}; 3use 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
16impl CompressionMethod { 16impl 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)]
32pub enum EncryptionMethod { 32pub 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
38impl 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)]
54pub struct BitFlag { 42pub struct BitFlag {
55 flag: u16, 43 flag: u16,
@@ -159,7 +147,7 @@ pub struct ZipFileInfo {
159} 147}
160 148
161impl ZipFileInfo { 149impl 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 @@
1use crate::driver::FileDriver; 1use crate::driver::FileDriver;
2use crate::utils::{IoCursor, ReadUtils}; 2use crate::utils::{IoCursor, ReadUtils};
3use crate::zip::encryption::WeakDecoder; 3use crate::zip::encryption::{AesDecoder, Keys, WeakDecoder};
4use crate::zip::structs::FILE_HEADER_SIGNATURE;
4use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult}; 5use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult};
6use aes::cipher::KeyInit;
7use aes::{Aes128, Aes192, Aes256};
5use bzip2::read::BzDecoder; 8use bzip2::read::BzDecoder;
6use flate2::read::DeflateDecoder; 9use flate2::read::DeflateDecoder;
7use liblzma::read::XzDecoder; 10use liblzma::read::XzDecoder;
8use liblzma::stream::{Filters, LzmaOptions, Stream}; 11use liblzma::stream::{Filters, LzmaOptions, Stream};
12use pbkdf2::pbkdf2_hmac_array;
13use sha1::Sha1;
9use std::io::{ 14use 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;
14enum Encryption<Io: Read> { 19enum 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
19impl<Io: Read> Encryption<Io> { 27impl<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
35impl<Io: Read> Read for Encryption<Io> { 102impl<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
44impl<Io: Read + Seek> Seek for Encryption<Io> { 115impl<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
64impl<Io: Read + Seek> Compression<Io> { 136impl<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
97impl<Io: Read> Read for Compression<Io> { 170impl<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
109impl<Io: Read + Seek> Seek for Compression<Io> { 183impl<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