Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor readers to use type parameters and not concrete vtables #207

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ time = { workspace = true, optional = true, features = [
"std",
] }
zeroize = { version = "1.8.1", optional = true, features = ["zeroize_derive"] }
zstd = { version = "0.13.1", optional = true, default-features = false }
# zstd = { version = "0.13.1", optional = true, default-features = false }
zstd = { git = "https://github.com/gyscos/zstd-rs", rev = "a3738d680542e34b5529207ee30491ed7b69ed71", optional = true, default-features = false }
cosmicexplorer marked this conversation as resolved.
Show resolved Hide resolved
zopfli = { version = "0.8.1", optional = true }
deflate64 = { version = "0.1.8", optional = true }
lzma-rs = { version = "0.3.0", default-features = false, optional = true }
Expand Down
8 changes: 5 additions & 3 deletions src/aes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub struct AesReader<R> {
data_length: u64,
}

impl<R: Read> AesReader<R> {
impl<R> AesReader<R> {
pub const fn new(reader: R, aes_mode: AesMode, compressed_size: u64) -> AesReader<R> {
let data_length = compressed_size
- (PWD_VERIFY_LENGTH + AUTH_CODE_LENGTH + aes_mode.salt_length()) as u64;
Expand All @@ -77,7 +77,9 @@ impl<R: Read> AesReader<R> {
data_length,
}
}
}

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

impl<R: Read> AesReaderValid<R> {
impl<R> AesReaderValid<R> {
/// Consumes this decoder, returning the underlying reader.
pub fn into_inner(self) -> R {
self.reader
Expand Down
105 changes: 105 additions & 0 deletions src/crc32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
self.check == self.hasher.clone().finalize()
}

#[allow(dead_code)]
cosmicexplorer marked this conversation as resolved.
Show resolved Hide resolved
pub fn into_inner(self) -> R {
self.inner
}
Expand Down Expand Up @@ -83,6 +84,110 @@
}
}

pub(crate) mod non_crypto {
use std::io;
use std::io::prelude::*;

use crc32fast::Hasher;

/// Reader that validates the CRC32 when it reaches the EOF.
pub struct Crc32Reader<R> {
inner: R,
hasher: Hasher,
check: u32,
}

impl<R> Crc32Reader<R> {
/// Get a new Crc32Reader which checks the inner reader against checksum.
pub(crate) fn new(inner: R, checksum: u32) -> Self {
Crc32Reader {
inner,
hasher: Hasher::new(),
check: checksum,
}
}

fn check_matches(&self) -> Result<(), &'static str> {
let res = self.hasher.clone().finalize();
if self.check == res {
Ok(())
} else {
/* TODO: make this into our own Crc32Error error type! */

Check notice

Code scanning / devskim

A "TODO" or similar was left in source code, possibly indicating incomplete functionality Note

Suspicious comment
Dismissed Show dismissed Hide dismissed
Err("Invalid checksum")
}
}
}

impl<R: Read> Read for Crc32Reader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
/* We want to make sure we only check the hash when the input stream is exhausted. */
if buf.is_empty() {
/* If the input buf is empty (this shouldn't happen, but isn't guaranteed), we
* still want to "pull" from the source in case it surfaces an i/o error. This will
* always return a count of Ok(0) if successful. */
return self.inner.read(buf);
}

let count = self.inner.read(buf)?;
if count == 0 {
return self
.check_matches()
.map(|()| 0)
/* TODO: use io::Error::other for MSRV >=1.74 */
Dismissed Show dismissed Hide dismissed
.map_err(|e| io::Error::new(io::ErrorKind::Other, e));
}
self.hasher.update(&buf[..count]);
Ok(count)
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_empty_reader() {
let data: &[u8] = b"";
let mut buf = [0; 1];

let mut reader = Crc32Reader::new(data, 0);
assert_eq!(reader.read(&mut buf).unwrap(), 0);

let mut reader = Crc32Reader::new(data, 1);
assert!(reader
.read(&mut buf)
.unwrap_err()
.to_string()
.contains("Invalid checksum"));
}

#[test]
fn test_byte_by_byte() {
let data: &[u8] = b"1234";
let mut buf = [0; 1];

let mut reader = Crc32Reader::new(data, 0x9be3e0a3);
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(reader.read(&mut buf).unwrap(), 0);
// Can keep reading 0 bytes after the end
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}

#[test]
fn test_zero_read() {
let data: &[u8] = b"1234";
let mut buf = [0; 5];

let mut reader = Crc32Reader::new(data, 0x9be3e0a3);
assert_eq!(reader.read(&mut buf[..0]).unwrap(), 0);
assert_eq!(reader.read(&mut buf).unwrap(), 4);
}
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
Loading
Loading