aboutsummaryrefslogtreecommitdiff
path: root/src/zip/driver.rs
diff options
context:
space:
mode:
authorIgor Tolmachev <me@igorek.dev>2024-07-20 16:52:39 +0900
committerIgor Tolmachev <me@igorek.dev>2024-07-20 16:52:39 +0900
commit7bcdc3b4ca460aec2b98fb2dca6165788c562b05 (patch)
tree63f9616fc1b7f9ca6e414a4d32910720e155690c /src/zip/driver.rs
parent5f4ceda88c7299deb317f8d22a99ab2521c5a380 (diff)
downloadarchivator-7bcdc3b4ca460aec2b98fb2dca6165788c562b05.tar.gz
archivator-7bcdc3b4ca460aec2b98fb2dca6165788c562b05.zip
Partial aes implementation and others improvements
Diffstat (limited to 'src/zip/driver.rs')
-rw-r--r--src/zip/driver.rs179
1 files changed, 89 insertions, 90 deletions
diff --git a/src/zip/driver.rs b/src/zip/driver.rs
index e7878cf..8dc902f 100644
--- a/src/zip/driver.rs
+++ b/src/zip/driver.rs
@@ -1,48 +1,51 @@
1use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; 1use crate::driver::{ArchiveRead, ArchiveWrite, Driver};
2use crate::utils::ReadUtils; 2use crate::utils::ReadUtils;
3use crate::zip::cp437::FromCp437; 3use crate::zip::cp437::FromCp437;
4use crate::zip::structs::{deserialize, Cdr, Eocdr, Eocdr64, Eocdr64Locator, ExtraHeader}; 4use crate::zip::datetime::DosDateTime;
5use crate::zip::structs::{
6 deserialize, AesField, Cdr, Eocdr, Eocdr64, Eocdr64Locator, ExtraHeader, CDR_SIGNATURE,
7 EOCDR64_LOCATOR_SIGNATURE, EOCDR64_SIGNATURE, EOCDR_SIGNATURE,
8};
5use crate::zip::{ 9use crate::zip::{
6 BitFlag, CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipFileReader, 10 BitFlag, CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipFileReader,
7 ZipFileWriter, ZipResult, 11 ZipFileWriter, ZipResult,
8}; 12};
9use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime}; 13use chrono::{DateTime, Local};
10use std::collections::HashMap as Map; 14use std::collections::HashMap as Map;
11use std::fs::File; 15use std::fs::File;
12use std::io::{BufReader, Read, Seek, SeekFrom, Write}; 16use std::io::{BufReader, Read, Seek, SeekFrom, Write};
13 17
14fn dos_to_local(date: u16, time: u16) -> ZipResult<DateTime<Local>> { 18#[inline]
15 Ok(NaiveDateTime::new( 19fn split_fields(bytes: &[u8]) -> Option<Vec<(u16, &[u8])>> {
16 NaiveDate::from_ymd_opt( 20 let mut fields = Vec::new();
17 (date as i32 >> 9 & 0x7F) + 1980, 21
18 date as u32 >> 5 & 0xF, 22 let mut p = 0;
19 date as u32 & 0x1F, 23 while p < bytes.len() {
20 ) 24 let header: ExtraHeader = deserialize(bytes.get(p..p + 4)?).unwrap();
21 .ok_or(ZipError::InvalidDate)?, 25 p += 4;
22 NaiveTime::from_hms_opt( 26 let data = bytes.get(p..p + header.size as usize)?;
23 (time as u32 >> 11) & 0x1F, 27 p += header.size as usize;
24 (time as u32 >> 5) & 0x3F, 28
25 (time as u32 & 0x1F) * 2, 29 fields.push((header.id, data));
26 ) 30 }
27 .ok_or(ZipError::InvalidTime)?, 31
28 ) 32 Some(fields)
29 .and_local_timezone(Local)
30 .unwrap())
31} 33}
32 34
33fn ntfs_to_local(time: u64) -> ZipResult<DateTime<Local>> { 35#[inline]
34 Ok(DateTime::from_timestamp( 36fn ntfs_to_local(time: u64) -> Option<DateTime<Local>> {
35 (time / 10000000 - 11644473600) as i64, 37 Some(
36 (time % 10000000) as u32, 38 DateTime::from_timestamp(
39 (time / 10000000 - 11644473600) as i64,
40 (time % 10000000) as u32,
41 )?
42 .with_timezone(&Local),
37 ) 43 )
38 .ok_or(ZipError::InvalidTime)?
39 .with_timezone(&Local))
40} 44}
41 45
42fn timestamp_to_local(time: i32) -> ZipResult<DateTime<Local>> { 46#[inline]
43 Ok(DateTime::from_timestamp(time as i64, 0) 47fn timestamp_to_local(time: i32) -> Option<DateTime<Local>> {
44 .ok_or(ZipError::InvalidTime)? 48 Some(DateTime::from_timestamp(time as i64, 0)?.with_timezone(&Local))
45 .with_timezone(&Local))
46} 49}
47 50
48pub struct Zip<Io = File> { 51pub struct Zip<Io = File> {
@@ -74,7 +77,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
74 .ok_or(ZipError::EocdrNotFound)?, 77 .ok_or(ZipError::EocdrNotFound)?,
75 )? 78 )?
76 .windows(4) 79 .windows(4)
77 .rposition(|v| u32::from_le_bytes(v.try_into().unwrap()) == 0x06054b50) 80 .rposition(|v| v == EOCDR_SIGNATURE)
78 .ok_or(ZipError::EocdrNotFound)? as u64; 81 .ok_or(ZipError::EocdrNotFound)? as u64;
79 82
80 // Read eocdr 83 // Read eocdr
@@ -88,13 +91,13 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
88 let buf = io.read_arr::<20>()?; 91 let buf = io.read_arr::<20>()?;
89 let (cd_pointer, cd_size, cd_records) = 92 let (cd_pointer, cd_size, cd_records) =
90 // If locator found then read eocdr64 93 // If locator found then read eocdr64
91 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) == 0x07064b50 { 94 if buf[0..4] == EOCDR64_LOCATOR_SIGNATURE {
92 let eocdr64locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap(); 95 let eocdr64locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap();
93 96
94 io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; 97 io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?;
95 let buf = io.read_arr::<56>()?; 98 let buf = io.read_arr::<56>()?;
96 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 { 99 if buf[0..4] != EOCDR64_SIGNATURE {
97 return Err(ZipError::InvalidEOCDR64Signature.into()); 100 return Err(ZipError::InvalidEOCDR64Signature);
98 } 101 }
99 let eocdr64: Eocdr64 = deserialize(&buf[4..]).unwrap(); 102 let eocdr64: Eocdr64 = deserialize(&buf[4..]).unwrap();
100 103
@@ -116,8 +119,8 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
116 for i in 0..cd_records as usize { 119 for i in 0..cd_records as usize {
117 let buf = buf_reader.read_arr::<46>()?; 120 let buf = buf_reader.read_arr::<46>()?;
118 121
119 if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x02014b50 { 122 if buf[..4] != CDR_SIGNATURE {
120 return Err(ZipError::InvalidCDRSignature.into()); 123 return Err(ZipError::InvalidCDRSignature);
121 } 124 }
122 let cdr: Cdr = deserialize(&buf[4..46]).unwrap(); 125 let cdr: Cdr = deserialize(&buf[4..46]).unwrap();
123 let bit_flag = BitFlag::new(cdr.bit_flag); 126 let bit_flag = BitFlag::new(cdr.bit_flag);
@@ -138,91 +141,87 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
138 String::from_cp437(comment) 141 String::from_cp437(comment)
139 }; 142 };
140 143
144 let mut compression_method = cdr.compression_method;
145 let mut encryption_method = if !bit_flag.is_encrypted() {
146 EncryptionMethod::None
147 } else if !bit_flag.is_strong_encryption() {
148 EncryptionMethod::Weak
149 } else {
150 EncryptionMethod::Unsupported
151 };
152
141 let mut compressed_size = cdr.compressed_size as u64; 153 let mut compressed_size = cdr.compressed_size as u64;
142 let mut size = cdr.size as u64; 154 let mut size = cdr.size as u64;
143 let mut header_pointer = cdr.header_pointer as u64; 155 let mut header_pointer = cdr.header_pointer as u64;
144 156
145 let mut mtime = dos_to_local(cdr.dos_date, cdr.dos_time)?; 157 let mut mtime = DateTime::from_dos_date_time(cdr.dos_date, cdr.dos_time, Local)?;
146 let mut atime = None; 158 let mut atime = None;
147 let mut ctime = None; 159 let mut ctime = None;
148 160
149 // Parse extensible data fields 161 // Parse extensible data fields
150 let mut ep: usize = 0; 162 for (id, mut data) in split_fields(&extra_fields).ok_or(ZipError::InvalidExtraFields)? {
151 while ep < cdr.extra_field_len as usize { 163 match id {
152 let header: ExtraHeader = deserialize(&extra_fields[ep..ep + 4]).unwrap();
153 ep += 4;
154
155 match header.id {
156 // Zip64 164 // Zip64
157 0x0001 => { 165 0x0001 => {
158 if size == 0xFFFFFFFF { 166 if size == 0xFFFFFFFF {
159 compressed_size = 167 size = u64::from_le_bytes(data.read_arr()?);
160 u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap());
161 ep += 8;
162 } 168 }
163 if compressed_size == 0xFFFFFFFF { 169 if compressed_size == 0xFFFFFFFF {
164 size = u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap()); 170 compressed_size = u64::from_le_bytes(data.read_arr()?);
165 ep += 8;
166 } 171 }
167 if header_pointer == 0xFFFFFFFF { 172 if header_pointer == 0xFFFFFFFF {
168 header_pointer = 173 header_pointer = u64::from_le_bytes(data.read_arr()?);
169 u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap());
170 ep += 8;
171 }
172 if cdr.disk == 0xFFFF {
173 ep += 4
174 } 174 }
175 } 175 }
176 // NTFS 176 // NTFS
177 0x000a => { 177 0x000a => {
178 let mut tp = ep + 4; 178 for (id, mut data) in
179 ep += header.size as usize; 179 split_fields(&data).ok_or(ZipError::InvalidExtraFields)?
180 180 {
181 while tp < ep { 181 match id {
182 let header: ExtraHeader =
183 deserialize(&extra_fields[tp..tp + 4]).unwrap();
184 tp += 4;
185
186 match header.id {
187 0x0001 => { 182 0x0001 => {
188 mtime = ntfs_to_local(u64::from_le_bytes( 183 mtime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?))
189 extra_fields[tp..tp + 8].try_into().unwrap(), 184 .unwrap_or(mtime);
190 ))?; 185 atime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?));
191 tp += 8; 186 ctime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?));
192 atime = Some(ntfs_to_local(u64::from_le_bytes(
193 extra_fields[tp..tp + 8].try_into().unwrap(),
194 ))?);
195 tp += 8;
196 ctime = Some(ntfs_to_local(u64::from_le_bytes(
197 extra_fields[tp..tp + 8].try_into().unwrap(),
198 ))?);
199 tp += 8;
200 }
201 _ => {
202 tp += header.size as usize;
203 } 187 }
188 _ => {}
204 } 189 }
205 } 190 }
206 } 191 }
207 // Unix 192 // Unix
208 0x000d => { 193 0x000d => {
209 atime = Some(timestamp_to_local(i32::from_le_bytes( 194 atime = timestamp_to_local(i32::from_le_bytes(data.read_arr()?));
210 extra_fields[ep..ep + 4].try_into().unwrap(), 195 mtime = timestamp_to_local(i32::from_le_bytes(data.read_arr()?))
211 ))?); 196 .unwrap_or(mtime);
212 mtime = timestamp_to_local(i32::from_le_bytes( 197 }
213 extra_fields[ep + 4..ep + 8].try_into().unwrap(), 198 // AES
214 ))?; 199 0x9901 => {
215 ep += header.size as usize 200 let aes: AesField = deserialize(&data.read_arr::<7>()?).unwrap();
201 if aes.id != 0x4541 {
202 return Err(ZipError::InvalidExtraFields);
203 }
204 encryption_method = match aes.strength {
205 0x01 => EncryptionMethod::Aes128,
206 0x02 => EncryptionMethod::Aes192,
207 0x03 => EncryptionMethod::Aes256,
208 _ => EncryptionMethod::Unsupported,
209 };
210 compression_method = aes.compression_method;
216 } 211 }
217 // Skip unrecognized header 212 // Skip unrecognized header
218 _ => ep += header.size as usize, 213 _ => {}
219 } 214 }
220 } 215 }
221 216
217 if compression_method == 99 {
218 return Err(ZipError::AesExtraFieldNotFound);
219 }
220
222 indexes.insert(name.clone(), i); 221 indexes.insert(name.clone(), i);
223 files.push(ZipFileInfo::new( 222 files.push(ZipFileInfo::new(
224 CompressionMethod::from_struct_id(cdr.compression_method)?, 223 CompressionMethod::from_struct_id(compression_method),
225 EncryptionMethod::from_bif_flag(bit_flag, cdr.crc, cdr.dos_time), 224 encryption_method,
226 bit_flag, 225 bit_flag,
227 mtime, 226 mtime,
228 atime, 227 atime,
@@ -248,15 +247,15 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
248 &self.files 247 &self.files
249 } 248 }
250 249
251 fn get_file_index(&self, name: &str) -> crate::ArchiveResult<usize, Self::Error> { 250 fn get_file_index(&self, name: &str) -> ZipResult<usize> {
252 self.indexes 251 self.indexes
253 .get(name) 252 .get(name)
254 .ok_or(ZipError::FileNotFound.into()) 253 .ok_or(ZipError::FileNotFound)
255 .copied() 254 .copied()
256 } 255 }
257 256
258 fn get_file_info(&self, index: usize) -> ZipResult<&Self::FileInfo> { 257 fn get_file_info(&self, index: usize) -> ZipResult<&Self::FileInfo> {
259 self.files.get(index).ok_or(ZipError::FileNotFound.into()) 258 self.files.get(index).ok_or(ZipError::FileNotFound)
260 } 259 }
261 260
262 #[inline] 261 #[inline]