aboutsummaryrefslogtreecommitdiff
path: root/src/zip
diff options
context:
space:
mode:
Diffstat (limited to 'src/zip')
-rw-r--r--src/zip/archive.rs19
-rw-r--r--src/zip/driver.rs25
-rw-r--r--src/zip/encryption.rs49
-rw-r--r--src/zip/error.rs2
-rw-r--r--src/zip/file/info.rs16
-rw-r--r--src/zip/file/read.rs88
-rw-r--r--src/zip/file/write.rs6
-rw-r--r--src/zip/structs.rs6
8 files changed, 128 insertions, 83 deletions
diff --git a/src/zip/archive.rs b/src/zip/archive.rs
index 79e8ca1..569ad87 100644
--- a/src/zip/archive.rs
+++ b/src/zip/archive.rs
@@ -1,3 +1,4 @@
1use crate::zip::{ZipFileReader, ZipResult};
1use crate::{Archive, Zip}; 2use crate::{Archive, Zip};
2use std::io::{Read, Seek, Write}; 3use std::io::{Read, Seek, Write};
3 4
@@ -5,6 +6,24 @@ impl<Io: Read + Seek> Archive<Zip<Io>> {
5 pub fn comment(&self) -> &String { 6 pub fn comment(&self) -> &String {
6 self.driver.comment() 7 self.driver.comment()
7 } 8 }
9
10 pub fn get_file_reader_by_index_with_password<'d>(
11 &'d mut self,
12 index: usize,
13 password: &[u8],
14 ) -> ZipResult<ZipFileReader<'d, Io>> {
15 self.driver
16 .get_file_reader_with_optional_password(index, Some(password))
17 }
18
19 #[inline]
20 pub fn get_file_reader_by_name_with_password<'d>(
21 &'d mut self,
22 name: &str,
23 password: &[u8],
24 ) -> ZipResult<ZipFileReader<'d, Io>> {
25 self.get_file_reader_by_index_with_password(self.get_file_index(name)?, password)
26 }
8} 27}
9 28
10impl<Io: Read + Write + Seek> Archive<Zip<Io>> {} 29impl<Io: Read + Write + Seek> Archive<Zip<Io>> {}
diff --git a/src/zip/driver.rs b/src/zip/driver.rs
index 87f9c1a..62da39f 100644
--- a/src/zip/driver.rs
+++ b/src/zip/driver.rs
@@ -221,7 +221,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
221 indexes.insert(name.clone(), i); 221 indexes.insert(name.clone(), i);
222 files.push(ZipFileInfo::new( 222 files.push(ZipFileInfo::new(
223 CompressionMethod::from_struct_id(cdr.compression_method)?, 223 CompressionMethod::from_struct_id(cdr.compression_method)?,
224 EncryptionMethod::from_bit_flag(bit_flag), 224 EncryptionMethod::from_bif_flag(bit_flag, cdr.crc, cdr.dos_time),
225 bit_flag, 225 bit_flag,
226 mtime, 226 mtime,
227 atime, 227 atime,
@@ -258,11 +258,22 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
258 self.files.get(index).ok_or(ZipError::FileNotFound.into()) 258 self.files.get(index).ok_or(ZipError::FileNotFound.into())
259 } 259 }
260 260
261 fn get_file_reader<'d>( 261 #[inline]
262 fn get_file_reader<'d>(&'d mut self, index: usize) -> ZipResult<Self::FileReader<'d>> {
263 self.get_file_reader_with_optional_password(index, None)
264 }
265}
266
267impl<Io: Read + Seek> Zip<Io> {
268 pub fn comment(&self) -> &String {
269 &self.comment
270 }
271
272 pub fn get_file_reader_with_optional_password<'d>(
262 &'d mut self, 273 &'d mut self,
263 index: usize, 274 index: usize,
264 password: Option<&str>, 275 password: Option<&[u8]>,
265 ) -> ZipResult<Self::FileReader<'d>> { 276 ) -> ZipResult<<Self as ArchiveRead>::FileReader<'d>> {
266 Ok(ZipFileReader::new( 277 Ok(ZipFileReader::new(
267 &mut self.io, 278 &mut self.io,
268 self.files.get(index).ok_or(ZipError::FileNotFound)?, 279 self.files.get(index).ok_or(ZipError::FileNotFound)?,
@@ -271,12 +282,6 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
271 } 282 }
272} 283}
273 284
274impl<Io: Read + Seek> Zip<Io> {
275 pub fn comment(&self) -> &String {
276 &self.comment
277 }
278}
279
280impl<Io: Read + Write + Seek> ArchiveWrite for Zip<Io> { 285impl<Io: Read + Write + Seek> ArchiveWrite for Zip<Io> {
281 type FileWriter<'d> = ZipFileWriter<'d, Io> where Io: 'd; 286 type FileWriter<'d> = ZipFileWriter<'d, Io> where Io: 'd;
282} 287}
diff --git a/src/zip/encryption.rs b/src/zip/encryption.rs
index 84e30d5..28a6bdb 100644
--- a/src/zip/encryption.rs
+++ b/src/zip/encryption.rs
@@ -1,12 +1,35 @@
1use crate::utils::ReadUtils; 1use crate::utils::ReadUtils;
2use crate::zip::ZipResult; 2use crate::zip::{ZipError, ZipResult};
3use crc32fast::Hasher;
4use std::io::{Read, Result as IoResult}; 3use std::io::{Read, Result as IoResult};
5 4
6fn crc32(byte: u8, crc32: u32) -> u32 { 5const TABLE: [u32; 256] = generate_table();
7 let mut hasher = Hasher::new_with_initial(crc32 ^ 0xFFFFFFFF); 6
8 hasher.update(&[byte]); 7const fn generate_table() -> [u32; 256] {
9 hasher.finalize() ^ 0xFFFFFFFF 8 let mut table = [0; 256];
9
10 let mut b = 0;
11 while b <= 255 {
12 let mut crc = b as u32;
13
14 let mut i = 0;
15 while i < 8 {
16 if (crc & 1) > 0 {
17 crc = (crc >> 1) ^ 0xEDB88320
18 } else {
19 crc >>= 1
20 }
21 i += 1;
22 }
23
24 table[b] = crc;
25 b += 1
26 }
27
28 table
29}
30
31fn crc32(byte: u8, crc: u32) -> u32 {
32 (crc >> 8) ^ TABLE[((crc & 0xFF) as u8 ^ byte) as usize]
10} 33}
11 34
12pub struct WeakDecoder<Io: Read> { 35pub struct WeakDecoder<Io: Read> {
@@ -17,7 +40,7 @@ pub struct WeakDecoder<Io: Read> {
17} 40}
18 41
19impl<Io: Read> WeakDecoder<Io> { 42impl<Io: Read> WeakDecoder<Io> {
20 pub fn new(io: Io, password: &str) -> ZipResult<Self> { 43 pub fn new(io: Io, check: u8, password: &[u8]) -> ZipResult<Self> {
21 let mut decoder = Self { 44 let mut decoder = Self {
22 key0: 305419896, 45 key0: 305419896,
23 key1: 591751049, 46 key1: 591751049,
@@ -25,11 +48,14 @@ impl<Io: Read> WeakDecoder<Io> {
25 io, 48 io,
26 }; 49 };
27 50
28 for c in password.chars() { 51 for c in password {
29 decoder.update_keys(c as u8) 52 decoder.update_keys(*c)
30 } 53 }
31 54
32 let buf = decoder.read_arr::<12>()?; 55 let buf = decoder.read_arr::<12>()?;
56 if check != buf[11] {
57 return Err(ZipError::IncorrectPassword.into());
58 }
33 59
34 Ok(decoder) 60 Ok(decoder)
35 } 61 }
@@ -46,7 +72,9 @@ impl<Io: Read> WeakDecoder<Io> {
46 72
47 fn decode_byte(&mut self, byte: u8) -> u8 { 73 fn decode_byte(&mut self, byte: u8) -> u8 {
48 let key = self.key2 | 2; 74 let key = self.key2 | 2;
49 byte ^ (((key * (key ^ 1)) >> 8) as u8) 75 let byte = byte ^ ((key * (key ^ 1)) >> 8) as u8;
76 self.update_keys(byte);
77 byte
50 } 78 }
51} 79}
52 80
@@ -55,7 +83,6 @@ impl<Io: Read> Read for WeakDecoder<Io> {
55 let bytes = self.io.read(buf)?; 83 let bytes = self.io.read(buf)?;
56 for i in 0..bytes { 84 for i in 0..bytes {
57 buf[i] = self.decode_byte(buf[i]); 85 buf[i] = self.decode_byte(buf[i]);
58 self.update_keys(buf[i])
59 } 86 }
60 Ok(bytes) 87 Ok(bytes)
61 } 88 }
diff --git a/src/zip/error.rs b/src/zip/error.rs
index 9ec0956..a4b8c2b 100644
--- a/src/zip/error.rs
+++ b/src/zip/error.rs
@@ -20,6 +20,7 @@ pub enum ZipError {
20 InvalidFileComment, 20 InvalidFileComment,
21 21
22 FileNotFound, 22 FileNotFound,
23 IncorrectPassword,
23 PasswordIsNotSpecified, 24 PasswordIsNotSpecified,
24 CompressedDataIsUnseekable, 25 CompressedDataIsUnseekable,
25 EncryptedDataIsUnseekable, 26 EncryptedDataIsUnseekable,
@@ -66,6 +67,7 @@ impl Display for ZipError {
66 Self::InvalidFileComment => write!(f, "Invalid file comment"), 67 Self::InvalidFileComment => write!(f, "Invalid file comment"),
67 68
68 Self::FileNotFound => write!(f, "File not found"), 69 Self::FileNotFound => write!(f, "File not found"),
70 Self::IncorrectPassword => write!(f, "Incorrect password"),
69 Self::PasswordIsNotSpecified => write!(f, "Password is not specified"), 71 Self::PasswordIsNotSpecified => write!(f, "Password is not specified"),
70 Self::CompressedDataIsUnseekable => write!(f, "Compressed data is unseekable"), 72 Self::CompressedDataIsUnseekable => write!(f, "Compressed data is unseekable"),
71 Self::EncryptedDataIsUnseekable => write!(f, "Encrypted data is unseekable"), 73 Self::EncryptedDataIsUnseekable => write!(f, "Encrypted data is unseekable"),
diff --git a/src/zip/file/info.rs b/src/zip/file/info.rs
index f5d4d8a..599dcc3 100644
--- a/src/zip/file/info.rs
+++ b/src/zip/file/info.rs
@@ -2,7 +2,7 @@ use crate::driver::ArchiveFileInfo;
2use crate::zip::{ZipError, ZipResult}; 2use crate::zip::{ZipError, ZipResult};
3use chrono::{DateTime, Local}; 3use chrono::{DateTime, Local};
4 4
5#[derive(Debug, PartialEq, Eq, Clone, Copy)] 5#[derive(Debug, PartialEq, Eq, Clone)]
6pub enum CompressionMethod { 6pub enum CompressionMethod {
7 Store, 7 Store,
8 Deflate, 8 Deflate,
@@ -28,18 +28,22 @@ impl CompressionMethod {
28 } 28 }
29} 29}
30 30
31#[derive(Debug, PartialEq, Eq, Clone, Copy)] 31#[derive(Debug, PartialEq, Eq, Clone)]
32pub enum EncryptionMethod { 32pub enum EncryptionMethod {
33 None, 33 None,
34 Weak, 34 Weak(u8),
35 Unsupported, 35 Unsupported,
36} 36}
37 37
38impl EncryptionMethod { 38impl EncryptionMethod {
39 pub(crate) fn from_bit_flag(bit_flag: BitFlag) -> 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()) { 40 match (bit_flag.is_encrypted(), bit_flag.is_strong_encryption()) {
41 (false, false) => EncryptionMethod::None, 41 (false, false) => EncryptionMethod::None,
42 (true, false) => EncryptionMethod::Weak, 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 }),
43 (true, true) => EncryptionMethod::Unsupported, 47 (true, true) => EncryptionMethod::Unsupported,
44 _ => panic!("impossible"), 48 _ => panic!("impossible"),
45 } 49 }
@@ -52,7 +56,7 @@ pub struct BitFlag {
52} 56}
53 57
54pub mod bit { 58pub mod bit {
55 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 59 #[derive(Debug, PartialEq, Eq, Clone)]
56 pub enum DeflateMode { 60 pub enum DeflateMode {
57 Normal, 61 Normal,
58 Maximum, 62 Maximum,
diff --git a/src/zip/file/read.rs b/src/zip/file/read.rs
index aa665c3..c26b304 100644
--- a/src/zip/file/read.rs
+++ b/src/zip/file/read.rs
@@ -17,11 +17,12 @@ enum Encryption<Io: Read> {
17} 17}
18 18
19impl<Io: Read> Encryption<Io> { 19impl<Io: Read> Encryption<Io> {
20 pub fn new(io: Io, method: EncryptionMethod, password: Option<&str>) -> ZipResult<Self> { 20 pub fn new(io: Io, info: &ZipFileInfo, password: Option<&[u8]>) -> ZipResult<Self> {
21 Ok(match method { 21 Ok(match info.encryption_method {
22 EncryptionMethod::None => Self::None(io), 22 EncryptionMethod::None => Self::None(io),
23 EncryptionMethod::Weak => Self::Weak(WeakDecoder::new( 23 EncryptionMethod::Weak(check) => Self::Weak(WeakDecoder::new(
24 io, 24 io,
25 check,
25 password.ok_or(ZipError::PasswordIsNotSpecified)?, 26 password.ok_or(ZipError::PasswordIsNotSpecified)?,
26 )?), 27 )?),
27 EncryptionMethod::Unsupported => { 28 EncryptionMethod::Unsupported => {
@@ -61,12 +62,29 @@ enum Compression<Io: Read> {
61} 62}
62 63
63impl<Io: Read + Seek> Compression<Io> { 64impl<Io: Read + Seek> Compression<Io> {
64 pub fn new(io: Io, method: CompressionMethod) -> ZipResult<Self> { 65 pub fn new(mut io: Io, info: &ZipFileInfo) -> ZipResult<Self> {
65 Ok(match method { 66 Ok(match info.compression_method {
66 CompressionMethod::Store => Self::Store(io), 67 CompressionMethod::Store => Self::Store(io),
67 CompressionMethod::Deflate => Self::Deflate(DeflateDecoder::new(io)), 68 CompressionMethod::Deflate => Self::Deflate(DeflateDecoder::new(io)),
68 CompressionMethod::BZip2 => Self::BZip2(BzDecoder::new(io)), 69 CompressionMethod::BZip2 => Self::BZip2(BzDecoder::new(io)),
69 CompressionMethod::Lzma => panic!(), 70 CompressionMethod::Lzma => {
71 let buf = io.read_arr::<9>()?;
72 Compression::Xz(XzDecoder::new_stream(
73 io,
74 Stream::new_raw_decoder(
75 Filters::new().lzma1(
76 LzmaOptions::new()
77 .literal_context_bits((buf[4] % 9) as u32)
78 .literal_position_bits((buf[4] / 9 % 5) as u32)
79 .position_bits((buf[4] / 45) as u32)
80 .dict_size(
81 u32::from_le_bytes(buf[5..9].try_into().unwrap()).max(4096),
82 ),
83 ),
84 )
85 .unwrap(),
86 ))
87 }
70 CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?), 88 CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?),
71 CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)), 89 CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)),
72 CompressionMethod::Unsupported(id) => { 90 CompressionMethod::Unsupported(id) => {
@@ -108,14 +126,14 @@ pub struct ZipFileReader<'d, Io: Read> {
108impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> { 126impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> {
109 type Io = Io; 127 type Io = Io;
110 type FileInfo = ZipFileInfo; 128 type FileInfo = ZipFileInfo;
111
112 fn info(&self) -> &Self::FileInfo {
113 self.info
114 }
115} 129}
116 130
117impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { 131impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> {
118 pub fn new(io: &'d mut Io, info: &'d ZipFileInfo, password: Option<&str>) -> ZipResult<Self> { 132 pub(crate) fn new(
133 io: &'d mut Io,
134 info: &'d ZipFileInfo,
135 password: Option<&[u8]>,
136 ) -> ZipResult<Self> {
119 io.seek(SeekFrom::Start(info.header_pointer))?; 137 io.seek(SeekFrom::Start(info.header_pointer))?;
120 138
121 let buf = io.read_arr::<30>()?; 139 let buf = io.read_arr::<30>()?;
@@ -127,49 +145,23 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> {
127 + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 145 + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64
128 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; 146 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64;
129 147
130 io.seek(SeekFrom::Start(data_pointer))?;
131
132 Ok(Self { 148 Ok(Self {
133 io: match info.compression_method { 149 io: Compression::new(
134 CompressionMethod::Lzma => { 150 Encryption::new(
135 let buf = io.read_arr::<9>()?; 151 IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?,
136 Compression::Xz(XzDecoder::new_stream( 152 info,
137 Encryption::new( 153 password,
138 IoCursor::new(
139 io,
140 data_pointer + 9,
141 data_pointer + info.compressed_size,
142 )?,
143 info.encryption_method,
144 password,
145 )?,
146 Stream::new_raw_decoder(
147 Filters::new().lzma1(
148 LzmaOptions::new()
149 .literal_context_bits((buf[4] % 9) as u32)
150 .literal_position_bits((buf[4] / 9 % 5) as u32)
151 .position_bits((buf[4] / 45) as u32)
152 .dict_size(
153 u32::from_le_bytes(buf[5..9].try_into().unwrap()).max(4096),
154 ),
155 ),
156 )
157 .unwrap(),
158 ))
159 }
160 _ => Compression::new(
161 Encryption::new(
162 IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?,
163 info.encryption_method,
164 password,
165 )?,
166 info.compression_method,
167 )?, 154 )?,
168 }, 155 info,
156 )?,
169 info, 157 info,
170 }) 158 })
171 } 159 }
172 160
161 pub fn info(&self) -> &ZipFileInfo {
162 self.info
163 }
164
173 pub fn is_seekable(&self) -> bool { 165 pub fn is_seekable(&self) -> bool {
174 match self.io { 166 match self.io {
175 Compression::Store(Encryption::None(..)) => true, 167 Compression::Store(Encryption::None(..)) => true,
diff --git a/src/zip/file/write.rs b/src/zip/file/write.rs
index d5b686c..20e53b3 100644
--- a/src/zip/file/write.rs
+++ b/src/zip/file/write.rs
@@ -2,8 +2,8 @@ use crate::driver::FileDriver;
2use crate::zip::ZipFileInfo; 2use crate::zip::ZipFileInfo;
3use std::io::Write; 3use std::io::Write;
4 4
5#[allow(dead_code)]
5pub struct ZipFileWriter<'d, Io: Write> { 6pub struct ZipFileWriter<'d, Io: Write> {
6 #[allow(dead_code)]
7 io: &'d mut Io, 7 io: &'d mut Io,
8 info: &'d ZipFileInfo, 8 info: &'d ZipFileInfo,
9} 9}
@@ -11,8 +11,4 @@ pub struct ZipFileWriter<'d, Io: Write> {
11impl<'d, Io: Write> FileDriver for ZipFileWriter<'d, Io> { 11impl<'d, Io: Write> FileDriver for ZipFileWriter<'d, Io> {
12 type Io = Io; 12 type Io = Io;
13 type FileInfo = ZipFileInfo; 13 type FileInfo = ZipFileInfo;
14
15 fn info(&self) -> &Self::FileInfo {
16 self.info
17 }
18} 14}
diff --git a/src/zip/structs.rs b/src/zip/structs.rs
index a44659b..9fd1aeb 100644
--- a/src/zip/structs.rs
+++ b/src/zip/structs.rs
@@ -1,4 +1,4 @@
1use crate::structs::{Settings, StructResult}; 1use crate::structs::{ByteOrder, Settings, StructResult, VariantIndexType};
2use serde::{Deserialize, Serialize}; 2use serde::{Deserialize, Serialize};
3 3
4#[derive(Serialize, Deserialize)] 4#[derive(Serialize, Deserialize)]
@@ -60,9 +60,9 @@ pub struct ExtraHeader {
60 60
61#[allow(dead_code)] 61#[allow(dead_code)]
62pub fn serialize<T: Serialize>(object: &mut T) -> StructResult<Vec<u8>> { 62pub fn serialize<T: Serialize>(object: &mut T) -> StructResult<Vec<u8>> {
63 Settings::default().serialize(object) 63 Settings::new(ByteOrder::Le, VariantIndexType::U8).serialize(object)
64} 64}
65 65
66pub fn deserialize<'de, T: Deserialize<'de>>(object: &'de [u8]) -> StructResult<T> { 66pub fn deserialize<'de, T: Deserialize<'de>>(object: &'de [u8]) -> StructResult<T> {
67 Settings::default().deserialize(object) 67 Settings::new(ByteOrder::Le, VariantIndexType::U8).deserialize(object)
68} 68}