Make InvalidPassword a kind of ZipError
This commit is contained in:
parent
eb3c5ede64
commit
ece098d393
5 changed files with 40 additions and 53 deletions
|
@ -229,7 +229,7 @@
|
||||||
|
|
||||||
- Updated dependencies.
|
- Updated dependencies.
|
||||||
|
|
||||||
## [0.10.4]
|
## [0.11.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -238,4 +238,9 @@
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Updated dependencies.
|
- `InvalidPassword` is now a kind of `ZipError` to eliminate the need for nested `Result` structs.
|
||||||
|
- Updated dependencies.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed some rare bugs that could cause panics when trying to read an invalid ZIP file or using an incorrect password.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "zip_next"
|
name = "zip_next"
|
||||||
version = "0.10.4"
|
version = "0.11.0"
|
||||||
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>", "Marli Frost <marli@frost.red>", "Ryan Levick <ryan.levick@gmail.com>",
|
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>", "Marli Frost <marli@frost.red>", "Ryan Levick <ryan.levick@gmail.com>",
|
||||||
"Chris Hennick <hennickc@amazon.com>"]
|
"Chris Hennick <hennickc@amazon.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
51
src/read.rs
51
src/read.rs
|
@ -5,7 +5,7 @@ use crate::aes::{AesReader, AesReaderValid};
|
||||||
use crate::compression::CompressionMethod;
|
use crate::compression::CompressionMethod;
|
||||||
use crate::cp437::FromCp437;
|
use crate::cp437::FromCp437;
|
||||||
use crate::crc32::Crc32Reader;
|
use crate::crc32::Crc32Reader;
|
||||||
use crate::result::{InvalidPassword, ZipError, ZipResult};
|
use crate::result::{ZipError, ZipResult};
|
||||||
use crate::spec;
|
use crate::spec;
|
||||||
use crate::types::{AesMode, AesVendorVersion, AtomicU64, DateTime, System, ZipFileData};
|
use crate::types::{AesMode, AesVendorVersion, AtomicU64, DateTime, System, ZipFileData};
|
||||||
use crate::zipcrypto::{ZipCryptoReader, ZipCryptoReaderValid, ZipCryptoValidator};
|
use crate::zipcrypto::{ZipCryptoReader, ZipCryptoReaderValid, ZipCryptoValidator};
|
||||||
|
@ -74,6 +74,7 @@ pub(crate) mod zip_archive {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::result::ZipError::InvalidPassword;
|
||||||
pub use zip_archive::ZipArchive;
|
pub use zip_archive::ZipArchive;
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
@ -229,7 +230,7 @@ pub(crate) fn make_crypto_reader<'a>(
|
||||||
password: Option<&[u8]>,
|
password: Option<&[u8]>,
|
||||||
aes_info: Option<(AesMode, AesVendorVersion)>,
|
aes_info: Option<(AesMode, AesVendorVersion)>,
|
||||||
#[cfg(feature = "aes-crypto")] compressed_size: u64,
|
#[cfg(feature = "aes-crypto")] compressed_size: u64,
|
||||||
) -> ZipResult<Result<CryptoReader<'a>, InvalidPassword>> {
|
) -> ZipResult<CryptoReader<'a>> {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
{
|
{
|
||||||
if let CompressionMethod::Unsupported(_) = compression_method {
|
if let CompressionMethod::Unsupported(_) = compression_method {
|
||||||
|
@ -247,7 +248,7 @@ pub(crate) fn make_crypto_reader<'a>(
|
||||||
#[cfg(feature = "aes-crypto")]
|
#[cfg(feature = "aes-crypto")]
|
||||||
(Some(password), Some((aes_mode, vendor_version))) => {
|
(Some(password), Some((aes_mode, vendor_version))) => {
|
||||||
match AesReader::new(reader, aes_mode, compressed_size).validate(password)? {
|
match AesReader::new(reader, aes_mode, compressed_size).validate(password)? {
|
||||||
None => return Ok(Err(InvalidPassword)),
|
None => return Err(InvalidPassword),
|
||||||
Some(r) => CryptoReader::Aes {
|
Some(r) => CryptoReader::Aes {
|
||||||
reader: r,
|
reader: r,
|
||||||
vendor_version,
|
vendor_version,
|
||||||
|
@ -261,14 +262,14 @@ pub(crate) fn make_crypto_reader<'a>(
|
||||||
ZipCryptoValidator::PkzipCrc32(crc32)
|
ZipCryptoValidator::PkzipCrc32(crc32)
|
||||||
};
|
};
|
||||||
match ZipCryptoReader::new(reader, password).validate(validator)? {
|
match ZipCryptoReader::new(reader, password).validate(validator)? {
|
||||||
None => return Ok(Err(InvalidPassword)),
|
None => return Err(InvalidPassword),
|
||||||
Some(r) => CryptoReader::ZipCrypto(r),
|
Some(r) => CryptoReader::ZipCrypto(r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(None, Some(_)) => return Ok(Err(InvalidPassword)),
|
(None, Some(_)) => return Err(InvalidPassword),
|
||||||
(None, None) => CryptoReader::Plaintext(reader),
|
(None, None) => CryptoReader::Plaintext(reader),
|
||||||
};
|
};
|
||||||
Ok(Ok(reader))
|
Ok(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn make_reader(
|
pub(crate) fn make_reader(
|
||||||
|
@ -588,24 +589,20 @@ impl<R: Read + Seek> ZipArchive<R> {
|
||||||
/// There are many passwords out there that will also pass the validity checks
|
/// There are many passwords out there that will also pass the validity checks
|
||||||
/// we are able to perform. This is a weakness of the ZipCrypto algorithm,
|
/// we are able to perform. This is a weakness of the ZipCrypto algorithm,
|
||||||
/// due to its fairly primitive approach to cryptography.
|
/// due to its fairly primitive approach to cryptography.
|
||||||
pub fn by_name_decrypt<'a>(
|
pub fn by_name_decrypt(&mut self, name: &str, password: &[u8]) -> ZipResult<ZipFile> {
|
||||||
&'a mut self,
|
|
||||||
name: &str,
|
|
||||||
password: &[u8],
|
|
||||||
) -> ZipResult<Result<ZipFile<'a>, InvalidPassword>> {
|
|
||||||
self.by_name_with_optional_password(name, Some(password))
|
self.by_name_with_optional_password(name, Some(password))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for a file entry by name
|
/// Search for a file entry by name
|
||||||
pub fn by_name(&mut self, name: &str) -> ZipResult<ZipFile> {
|
pub fn by_name(&mut self, name: &str) -> ZipResult<ZipFile> {
|
||||||
Ok(self.by_name_with_optional_password(name, None)?.unwrap())
|
self.by_name_with_optional_password(name, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn by_name_with_optional_password<'a>(
|
fn by_name_with_optional_password<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
password: Option<&[u8]>,
|
password: Option<&[u8]>,
|
||||||
) -> ZipResult<Result<ZipFile<'a>, InvalidPassword>> {
|
) -> ZipResult<ZipFile<'a>> {
|
||||||
let index = match self.shared.names_map.get(name) {
|
let index = match self.shared.names_map.get(name) {
|
||||||
Some(index) => *index,
|
Some(index) => *index,
|
||||||
None => {
|
None => {
|
||||||
|
@ -632,15 +629,13 @@ impl<R: Read + Seek> ZipArchive<R> {
|
||||||
&mut self,
|
&mut self,
|
||||||
file_number: usize,
|
file_number: usize,
|
||||||
password: &[u8],
|
password: &[u8],
|
||||||
) -> ZipResult<Result<ZipFile, InvalidPassword>> {
|
) -> ZipResult<ZipFile<'_>> {
|
||||||
self.by_index_with_optional_password(file_number, Some(password))
|
self.by_index_with_optional_password(file_number, Some(password))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a contained file by index
|
/// Get a contained file by index
|
||||||
pub fn by_index(&mut self, file_number: usize) -> ZipResult<ZipFile<'_>> {
|
pub fn by_index(&mut self, file_number: usize) -> ZipResult<ZipFile<'_>> {
|
||||||
Ok(self
|
self.by_index_with_optional_password(file_number, None)
|
||||||
.by_index_with_optional_password(file_number, None)?
|
|
||||||
.unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a contained file by index without decompressing it
|
/// Get a contained file by index without decompressing it
|
||||||
|
@ -663,7 +658,7 @@ impl<R: Read + Seek> ZipArchive<R> {
|
||||||
&mut self,
|
&mut self,
|
||||||
file_number: usize,
|
file_number: usize,
|
||||||
mut password: Option<&[u8]>,
|
mut password: Option<&[u8]>,
|
||||||
) -> ZipResult<Result<ZipFile, InvalidPassword>> {
|
) -> ZipResult<ZipFile<'_>> {
|
||||||
let data = self
|
let data = self
|
||||||
.shared
|
.shared
|
||||||
.files
|
.files
|
||||||
|
@ -677,7 +672,7 @@ impl<R: Read + Seek> ZipArchive<R> {
|
||||||
}
|
}
|
||||||
let limit_reader = find_content(data, &mut self.reader)?;
|
let limit_reader = find_content(data, &mut self.reader)?;
|
||||||
|
|
||||||
match make_crypto_reader(
|
let crypto_reader = make_crypto_reader(
|
||||||
data.compression_method,
|
data.compression_method,
|
||||||
data.crc32,
|
data.crc32,
|
||||||
data.last_modified_time,
|
data.last_modified_time,
|
||||||
|
@ -687,15 +682,12 @@ impl<R: Read + Seek> ZipArchive<R> {
|
||||||
data.aes_mode,
|
data.aes_mode,
|
||||||
#[cfg(feature = "aes-crypto")]
|
#[cfg(feature = "aes-crypto")]
|
||||||
data.compressed_size,
|
data.compressed_size,
|
||||||
) {
|
)?;
|
||||||
Ok(Ok(crypto_reader)) => Ok(Ok(ZipFile {
|
Ok(ZipFile {
|
||||||
crypto_reader: Some(crypto_reader),
|
crypto_reader: Some(crypto_reader),
|
||||||
reader: ZipFileReader::NoReader,
|
reader: ZipFileReader::NoReader,
|
||||||
data: Cow::Borrowed(data),
|
data: Cow::Borrowed(data),
|
||||||
})),
|
})
|
||||||
Err(e) => Err(e),
|
|
||||||
Ok(Err(e)) => Ok(Err(e)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unwrap and return the inner reader object
|
/// Unwrap and return the inner reader object
|
||||||
|
@ -1186,8 +1178,7 @@ pub fn read_zipfile_from_stream<'a, R: Read>(reader: &'a mut R) -> ZipResult<Opt
|
||||||
None,
|
None,
|
||||||
#[cfg(feature = "aes-crypto")]
|
#[cfg(feature = "aes-crypto")]
|
||||||
result.compressed_size,
|
result.compressed_size,
|
||||||
)?
|
)?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(Some(ZipFile {
|
Ok(Some(ZipFile {
|
||||||
data: Cow::Owned(result),
|
data: Cow::Owned(result),
|
||||||
|
|
|
@ -9,20 +9,9 @@ use std::num::TryFromIntError;
|
||||||
/// Generic result type with ZipError as its error variant
|
/// Generic result type with ZipError as its error variant
|
||||||
pub type ZipResult<T> = Result<T, ZipError>;
|
pub type ZipResult<T> = Result<T, ZipError>;
|
||||||
|
|
||||||
/// The given password is wrong
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct InvalidPassword;
|
|
||||||
|
|
||||||
impl fmt::Display for InvalidPassword {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(fmt, "invalid password for file in archive")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for InvalidPassword {}
|
|
||||||
|
|
||||||
/// Error type for Zip
|
/// Error type for Zip
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum ZipError {
|
pub enum ZipError {
|
||||||
/// An Error caused by I/O
|
/// An Error caused by I/O
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
|
@ -35,6 +24,9 @@ pub enum ZipError {
|
||||||
|
|
||||||
/// The requested file could not be found in the archive
|
/// The requested file could not be found in the archive
|
||||||
FileNotFound,
|
FileNotFound,
|
||||||
|
|
||||||
|
/// The password provided is incorrect
|
||||||
|
InvalidPassword,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for ZipError {
|
impl From<io::Error> for ZipError {
|
||||||
|
@ -56,6 +48,7 @@ impl fmt::Display for ZipError {
|
||||||
ZipError::InvalidArchive(err) => write!(fmt, "invalid Zip archive: {err}"),
|
ZipError::InvalidArchive(err) => write!(fmt, "invalid Zip archive: {err}"),
|
||||||
ZipError::UnsupportedArchive(err) => write!(fmt, "unsupported Zip archive: {err}"),
|
ZipError::UnsupportedArchive(err) => write!(fmt, "unsupported Zip archive: {err}"),
|
||||||
ZipError::FileNotFound => write!(fmt, "specified file not found in archive"),
|
ZipError::FileNotFound => write!(fmt, "specified file not found in archive"),
|
||||||
|
ZipError::InvalidPassword => write!(fmt, "incorrect password for encrypted file"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
// 000000c5
|
// 000000c5
|
||||||
|
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::io::Read;
|
use zip_next::result::ZipError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encrypting_file() {
|
fn encrypting_file() {
|
||||||
|
@ -36,13 +36,14 @@ fn encrypting_file() {
|
||||||
archive.finish().unwrap();
|
archive.finish().unwrap();
|
||||||
drop(archive);
|
drop(archive);
|
||||||
let mut archive = zip_next::ZipArchive::new(Cursor::new(&mut buf)).unwrap();
|
let mut archive = zip_next::ZipArchive::new(Cursor::new(&mut buf)).unwrap();
|
||||||
let mut file = archive.by_index_decrypt(0, b"password").unwrap().unwrap();
|
let mut file = archive.by_index_decrypt(0, b"password").unwrap();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
file.read_to_end(&mut buf).unwrap();
|
file.read_to_end(&mut buf).unwrap();
|
||||||
assert_eq!(buf, b"test");
|
assert_eq!(buf, b"test");
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn encrypted_file() {
|
fn encrypted_file() {
|
||||||
|
use std::io::Read;
|
||||||
let zip_file_bytes = &mut Cursor::new(vec![
|
let zip_file_bytes = &mut Cursor::new(vec![
|
||||||
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0xbd, 0xb5, 0x50, 0x2f,
|
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0xbd, 0xb5, 0x50, 0x2f,
|
||||||
0x20, 0x79, 0x55, 0x2f, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
0x20, 0x79, 0x55, 0x2f, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
||||||
|
@ -82,20 +83,17 @@ fn encrypted_file() {
|
||||||
// Wrong password
|
// Wrong password
|
||||||
let file = archive.by_index_decrypt(0, b"wrong password");
|
let file = archive.by_index_decrypt(0, b"wrong password");
|
||||||
match file {
|
match file {
|
||||||
Ok(Err(zip_next::result::InvalidPassword)) => (),
|
Err(ZipError::InvalidPassword) => (),
|
||||||
Err(_) => panic!(
|
Err(_) => panic!(
|
||||||
"Expected InvalidPassword error when opening encrypted file with wrong password"
|
"Expected InvalidPassword error when opening encrypted file with wrong password"
|
||||||
),
|
),
|
||||||
Ok(Ok(_)) => panic!("Error: Successfully opened encrypted file with wrong password?!"),
|
Ok(_) => panic!("Error: Successfully opened encrypted file with wrong password?!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Correct password, read contents
|
// Correct password, read contents
|
||||||
let mut file = archive
|
let mut file = archive.by_index_decrypt(0, "test".as_bytes()).unwrap();
|
||||||
.by_index_decrypt(0, "test".as_bytes())
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
let file_name = file.enclosed_name().unwrap();
|
let file_name = file.enclosed_name().unwrap();
|
||||||
assert_eq!(file_name, std::path::PathBuf::from("test.txt"));
|
assert_eq!(file_name, std::path::PathBuf::from("test.txt"));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue