Skip to content

Commit dcbabcc

Browse files
add ZipEntry API in src/unstable/read.rs
- also use git dep for zstd to pull in removal of R: BufRead bound - move new methods into unstable/read.rs - support getting aes verification info from a raw ZipEntry - flesh out other methods from ZipFile -> ZipEntry - now create a streaming abstraction - move some methods around - make AesModeInfo pub(crate) to make its intention clear - make ArchiveEntry more publicly accessible - make ZipFile impl EntryData
1 parent 3f6768e commit dcbabcc

File tree

11 files changed

+1197
-174
lines changed

11 files changed

+1197
-174
lines changed

src/aes.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub struct AesReader<R> {
6666
data_length: u64,
6767
}
6868

69-
impl<R: Read> AesReader<R> {
69+
impl<R> AesReader<R> {
7070
pub const fn new(reader: R, aes_mode: AesMode, compressed_size: u64) -> AesReader<R> {
7171
let data_length = compressed_size
7272
- (PWD_VERIFY_LENGTH + AUTH_CODE_LENGTH + aes_mode.salt_length()) as u64;
@@ -77,7 +77,9 @@ impl<R: Read> AesReader<R> {
7777
data_length,
7878
}
7979
}
80+
}
8081

82+
impl<R: Read> AesReader<R> {
8183
/// Read the AES header bytes and validate the password.
8284
///
8385
/// Even if the validation succeeds, there is still a 1 in 65536 chance that an incorrect
@@ -150,7 +152,7 @@ impl<R: Read> AesReader<R> {
150152
/// There is a 1 in 65536 chance that an invalid password passes that check.
151153
/// After the data has been read and decrypted an HMAC will be checked and provide a final means
152154
/// to check if either the password is invalid or if the data has been changed.
153-
pub struct AesReaderValid<R: Read> {
155+
pub struct AesReaderValid<R> {
154156
reader: R,
155157
data_remaining: u64,
156158
cipher: Cipher,
@@ -214,7 +216,7 @@ impl<R: Read> Read for AesReaderValid<R> {
214216
}
215217
}
216218

217-
impl<R: Read> AesReaderValid<R> {
219+
impl<R> AesReaderValid<R> {
218220
/// Consumes this decoder, returning the underlying reader.
219221
pub fn into_inner(self) -> R {
220222
self.reader

src/crc32.rs

+104
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,110 @@ impl<R: Read> Read for Crc32Reader<R> {
8383
}
8484
}
8585

86+
pub(crate) mod non_crypto {
87+
use std::io;
88+
use std::io::prelude::*;
89+
90+
use crc32fast::Hasher;
91+
92+
/// Reader that validates the CRC32 when it reaches the EOF.
93+
pub struct Crc32Reader<R> {
94+
inner: R,
95+
hasher: Hasher,
96+
check: u32,
97+
}
98+
99+
impl<R> Crc32Reader<R> {
100+
/// Get a new Crc32Reader which checks the inner reader against checksum.
101+
pub(crate) fn new(inner: R, checksum: u32) -> Self {
102+
Crc32Reader {
103+
inner,
104+
hasher: Hasher::new(),
105+
check: checksum,
106+
}
107+
}
108+
109+
fn check_matches(&self) -> Result<(), &'static str> {
110+
let res = self.hasher.clone().finalize();
111+
if self.check == res {
112+
Ok(())
113+
} else {
114+
/* TODO: make this into our own Crc32Error error type! */
115+
Err("Invalid checksum")
116+
}
117+
}
118+
}
119+
120+
impl<R: Read> Read for Crc32Reader<R> {
121+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
122+
/* We want to make sure we only check the hash when the input stream is exhausted. */
123+
if buf.is_empty() {
124+
/* If the input buf is empty (this shouldn't happen, but isn't guaranteed), we
125+
* still want to "pull" from the source in case it surfaces an i/o error. This will
126+
* always return a count of Ok(0) if successful. */
127+
return self.inner.read(buf);
128+
}
129+
130+
let count = self.inner.read(buf)?;
131+
if count == 0 {
132+
return self
133+
.check_matches()
134+
.map(|()| 0)
135+
/* TODO: use io::Error::other for MSRV >=1.74 */
136+
.map_err(|e| io::Error::new(io::ErrorKind::Other, e));
137+
}
138+
self.hasher.update(&buf[..count]);
139+
Ok(count)
140+
}
141+
}
142+
143+
#[cfg(test)]
144+
mod test {
145+
use super::*;
146+
147+
#[test]
148+
fn test_empty_reader() {
149+
let data: &[u8] = b"";
150+
let mut buf = [0; 1];
151+
152+
let mut reader = Crc32Reader::new(data, 0);
153+
assert_eq!(reader.read(&mut buf).unwrap(), 0);
154+
155+
let mut reader = Crc32Reader::new(data, 1);
156+
assert!(reader
157+
.read(&mut buf)
158+
.unwrap_err()
159+
.to_string()
160+
.contains("Invalid checksum"));
161+
}
162+
163+
#[test]
164+
fn test_byte_by_byte() {
165+
let data: &[u8] = b"1234";
166+
let mut buf = [0; 1];
167+
168+
let mut reader = Crc32Reader::new(data, 0x9be3e0a3);
169+
assert_eq!(reader.read(&mut buf).unwrap(), 1);
170+
assert_eq!(reader.read(&mut buf).unwrap(), 1);
171+
assert_eq!(reader.read(&mut buf).unwrap(), 1);
172+
assert_eq!(reader.read(&mut buf).unwrap(), 1);
173+
assert_eq!(reader.read(&mut buf).unwrap(), 0);
174+
// Can keep reading 0 bytes after the end
175+
assert_eq!(reader.read(&mut buf).unwrap(), 0);
176+
}
177+
178+
#[test]
179+
fn test_zero_read() {
180+
let data: &[u8] = b"1234";
181+
let mut buf = [0; 5];
182+
183+
let mut reader = Crc32Reader::new(data, 0x9be3e0a3);
184+
assert_eq!(reader.read(&mut buf[..0]).unwrap(), 0);
185+
assert_eq!(reader.read(&mut buf).unwrap(), 4);
186+
}
187+
}
188+
}
189+
86190
#[cfg(test)]
87191
mod test {
88192
use super::*;

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
#![warn(missing_docs)]
3333
#![allow(unexpected_cfgs)] // Needed for cfg(fuzzing) on nightly as of 2024-05-06
3434
pub use crate::compression::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};
35-
pub use crate::read::HasZipMetadata;
3635
pub use crate::read::ZipArchive;
36+
pub use crate::read::{EntryData, HasZipMetadata};
3737
pub use crate::spec::{ZIP64_BYTES_THR, ZIP64_ENTRY_THR};
3838
pub use crate::types::{AesMode, DateTime};
3939
pub use crate::write::ZipWriter;

0 commit comments

Comments
 (0)