initial aes reader
This commit is contained in:
parent
4877a6afd4
commit
852ab625fb
7 changed files with 207 additions and 29 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -11,13 +11,16 @@ Library to support the reading and writing of zip files.
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes = { version = "0.5.0", optional = true }
|
aes = "0.5.0"
|
||||||
flate2 = { version = "1.0.0", default-features = false, optional = true }
|
|
||||||
time = { version = "0.1", optional = true }
|
|
||||||
byteorder = "1.3"
|
byteorder = "1.3"
|
||||||
bzip2 = { version = "0.4", optional = true }
|
bzip2 = { version = "0.4", optional = true }
|
||||||
crc32fast = "1.0"
|
crc32fast = "1.0"
|
||||||
|
flate2 = { version = "1.0.0", default-features = false, optional = true }
|
||||||
|
hmac = "0.9.0"
|
||||||
|
pbkdf2 = "0.5.0"
|
||||||
|
sha-1 = "0.9.1"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
time = { version = "0.1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bencher = "0.1"
|
bencher = "0.1"
|
||||||
|
@ -25,7 +28,6 @@ rand = "0.7"
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
aes-crypto = ["aes"]
|
|
||||||
deflate = ["flate2/rust_backend"]
|
deflate = ["flate2/rust_backend"]
|
||||||
deflate-miniz = ["flate2/default"]
|
deflate-miniz = ["flate2/default"]
|
||||||
deflate-zlib = ["flate2/zlib"]
|
deflate-zlib = ["flate2/zlib"]
|
||||||
|
|
86
src/aes.rs
Normal file
86
src/aes.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use crate::types::AesMode;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
|
use hmac::Hmac;
|
||||||
|
use sha1::Sha1;
|
||||||
|
|
||||||
|
/// The length of the password verifcation value in bytes
|
||||||
|
const PWD_VERIFY_LENGTH: u64 = 2;
|
||||||
|
/// The length of the authentication code in bytes
|
||||||
|
const AUTH_CODE_LENGTH: u64 = 10;
|
||||||
|
/// The number of iterations used with PBKDF2
|
||||||
|
const ITERATION_COUNT: u32 = 1000;
|
||||||
|
/// AES block size in bytes
|
||||||
|
const BLOCK_SIZE: usize = 16;
|
||||||
|
|
||||||
|
// an aes encrypted file starts with a salt, whose length depends on the used aes mode
|
||||||
|
// followed by a 2 byte password verification value
|
||||||
|
// then the variable length encrypted data
|
||||||
|
// and lastly a 10 byte authentication code
|
||||||
|
pub(crate) struct AesReader<R> {
|
||||||
|
reader: R,
|
||||||
|
aes_mode: AesMode,
|
||||||
|
salt_length: usize,
|
||||||
|
data_length: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: io::Read> AesReader<R> {
|
||||||
|
pub fn new(reader: R, aes_mode: AesMode, compressed_size: u64) -> AesReader<R> {
|
||||||
|
let salt_length = aes_mode.salt_length();
|
||||||
|
let data_length = compressed_size - (PWD_VERIFY_LENGTH + AUTH_CODE_LENGTH + salt_length);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
reader,
|
||||||
|
aes_mode,
|
||||||
|
salt_length: salt_length as usize,
|
||||||
|
data_length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate(mut self, password: &[u8]) -> Result<Option<AesReaderValid<R>>, io::Error> {
|
||||||
|
// the length of the salt depends on the used key size
|
||||||
|
let mut salt = vec![0; self.salt_length as usize];
|
||||||
|
self.reader.read_exact(&mut salt).unwrap();
|
||||||
|
|
||||||
|
// next are 2 bytes used for password verification
|
||||||
|
let mut pwd_verification_value = vec![0; PWD_VERIFY_LENGTH as usize];
|
||||||
|
self.reader.read_exact(&mut pwd_verification_value).unwrap();
|
||||||
|
|
||||||
|
// derive a key from the password and salt
|
||||||
|
// the length depends on the aes key length
|
||||||
|
let derived_key_len = (2 * self.aes_mode.key_length() + PWD_VERIFY_LENGTH) as usize;
|
||||||
|
let mut derived_key: Vec<u8> = vec![0; derived_key_len];
|
||||||
|
|
||||||
|
// use PBKDF2 with HMAC-Sha1 to derive the key
|
||||||
|
pbkdf2::pbkdf2::<Hmac<Sha1>>(password, &salt, ITERATION_COUNT, &mut derived_key);
|
||||||
|
|
||||||
|
// the last 2 bytes should equal the password verification value
|
||||||
|
if pwd_verification_value != &derived_key[derived_key_len - 2..] {
|
||||||
|
// wrong password
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the first key_length bytes are used as decryption key
|
||||||
|
let decrypt_key = &derived_key[0..self.aes_mode.key_length() as usize];
|
||||||
|
|
||||||
|
panic!("Validating AesReader");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct AesReaderValid<R> {
|
||||||
|
reader: AesReader<R>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: io::Read> io::Read for AesReaderValid<R> {
|
||||||
|
fn read(&mut self, mut buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
panic!("Reading from AesReaderValid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: io::Read> AesReaderValid<R> {
|
||||||
|
/// Consumes this decoder, returning the underlying reader.
|
||||||
|
pub fn into_inner(self) -> R {
|
||||||
|
self.reader.reader
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,10 @@ pub enum CompressionMethod {
|
||||||
/// Compress the file using BZIP2
|
/// Compress the file using BZIP2
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
Bzip2,
|
Bzip2,
|
||||||
|
/// Encrypted using AES.
|
||||||
|
/// The actual compression method has to be taken from the AES extra data field
|
||||||
|
/// or from `ZipFileData`.
|
||||||
|
AES,
|
||||||
/// Unsupported compression method
|
/// Unsupported compression method
|
||||||
#[deprecated(since = "0.5.7", note = "use the constants instead")]
|
#[deprecated(since = "0.5.7", note = "use the constants instead")]
|
||||||
Unsupported(u16),
|
Unsupported(u16),
|
||||||
|
@ -85,7 +89,7 @@ impl CompressionMethod {
|
||||||
8 => CompressionMethod::Deflated,
|
8 => CompressionMethod::Deflated,
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
12 => CompressionMethod::Bzip2,
|
12 => CompressionMethod::Bzip2,
|
||||||
|
99 => CompressionMethod::AES,
|
||||||
v => CompressionMethod::Unsupported(v),
|
v => CompressionMethod::Unsupported(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,6 +111,7 @@ impl CompressionMethod {
|
||||||
CompressionMethod::Deflated => 8,
|
CompressionMethod::Deflated => 8,
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
CompressionMethod::Bzip2 => 12,
|
CompressionMethod::Bzip2 => 12,
|
||||||
|
CompressionMethod::AES => 99,
|
||||||
CompressionMethod::Unsupported(v) => v,
|
CompressionMethod::Unsupported(v) => v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub use crate::read::ZipArchive;
|
||||||
pub use crate::types::DateTime;
|
pub use crate::types::DateTime;
|
||||||
pub use crate::write::ZipWriter;
|
pub use crate::write::ZipWriter;
|
||||||
|
|
||||||
#[cfg(feature = "aes-crypto")]
|
mod aes;
|
||||||
mod aes_ctr;
|
mod aes_ctr;
|
||||||
mod compression;
|
mod compression;
|
||||||
mod cp437;
|
mod cp437;
|
||||||
|
|
98
src/read.rs
98
src/read.rs
|
@ -1,19 +1,19 @@
|
||||||
//! Types for reading ZIP archives
|
//! Types for reading ZIP archives
|
||||||
|
|
||||||
|
use crate::aes::{AesReaderValid, AesReader};
|
||||||
use crate::compression::CompressionMethod;
|
use crate::compression::CompressionMethod;
|
||||||
|
use crate::cp437::FromCp437;
|
||||||
use crate::crc32::Crc32Reader;
|
use crate::crc32::Crc32Reader;
|
||||||
use crate::result::{InvalidPassword, ZipError, ZipResult};
|
use crate::result::{InvalidPassword, ZipError, ZipResult};
|
||||||
use crate::spec;
|
use crate::spec;
|
||||||
|
use crate::types::{AesMode, DateTime, System, ZipFileData};
|
||||||
use crate::zipcrypto::{ZipCryptoReader, ZipCryptoReaderValid, ZipCryptoValidator};
|
use crate::zipcrypto::{ZipCryptoReader, ZipCryptoReaderValid, ZipCryptoValidator};
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{self, prelude::*};
|
use std::io::{self, prelude::*};
|
||||||
use std::path::{Component, Path};
|
use std::path::{Component, Path};
|
||||||
|
|
||||||
use crate::cp437::FromCp437;
|
|
||||||
use crate::types::{DateTime, System, ZipFileData};
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "deflate",
|
feature = "deflate",
|
||||||
feature = "deflate-miniz",
|
feature = "deflate-miniz",
|
||||||
|
@ -57,6 +57,7 @@ pub struct ZipArchive<R> {
|
||||||
enum CryptoReader<'a> {
|
enum CryptoReader<'a> {
|
||||||
Plaintext(io::Take<&'a mut dyn Read>),
|
Plaintext(io::Take<&'a mut dyn Read>),
|
||||||
ZipCrypto(ZipCryptoReaderValid<io::Take<&'a mut dyn Read>>),
|
ZipCrypto(ZipCryptoReaderValid<io::Take<&'a mut dyn Read>>),
|
||||||
|
Aes(AesReaderValid<io::Take<&'a mut dyn Read>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Read for CryptoReader<'a> {
|
impl<'a> Read for CryptoReader<'a> {
|
||||||
|
@ -64,6 +65,7 @@ impl<'a> Read for CryptoReader<'a> {
|
||||||
match self {
|
match self {
|
||||||
CryptoReader::Plaintext(r) => r.read(buf),
|
CryptoReader::Plaintext(r) => r.read(buf),
|
||||||
CryptoReader::ZipCrypto(r) => r.read(buf),
|
CryptoReader::ZipCrypto(r) => r.read(buf),
|
||||||
|
CryptoReader::Aes(r) => r.read(buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +76,7 @@ impl<'a> CryptoReader<'a> {
|
||||||
match self {
|
match self {
|
||||||
CryptoReader::Plaintext(r) => r,
|
CryptoReader::Plaintext(r) => r,
|
||||||
CryptoReader::ZipCrypto(r) => r.into_inner(),
|
CryptoReader::ZipCrypto(r) => r.into_inner(),
|
||||||
|
CryptoReader::Aes(r) => r.into_inner(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,6 +167,8 @@ fn make_crypto_reader<'a>(
|
||||||
using_data_descriptor: bool,
|
using_data_descriptor: bool,
|
||||||
reader: io::Take<&'a mut dyn io::Read>,
|
reader: io::Take<&'a mut dyn io::Read>,
|
||||||
password: Option<&[u8]>,
|
password: Option<&[u8]>,
|
||||||
|
aes_mode: Option<AesMode>,
|
||||||
|
compressed_size: u64,
|
||||||
) -> ZipResult<Result<CryptoReader<'a>, InvalidPassword>> {
|
) -> ZipResult<Result<CryptoReader<'a>, InvalidPassword>> {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
{
|
{
|
||||||
|
@ -172,9 +177,9 @@ fn make_crypto_reader<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let reader = match password {
|
let reader = match (password, aes_mode) {
|
||||||
None => CryptoReader::Plaintext(reader),
|
(None, _) => CryptoReader::Plaintext(reader),
|
||||||
Some(password) => {
|
(Some(password), None) => {
|
||||||
let validator = if using_data_descriptor {
|
let validator = if using_data_descriptor {
|
||||||
ZipCryptoValidator::InfoZipMsdosTime(last_modified_time.timepart())
|
ZipCryptoValidator::InfoZipMsdosTime(last_modified_time.timepart())
|
||||||
} else {
|
} else {
|
||||||
|
@ -185,6 +190,12 @@ fn make_crypto_reader<'a>(
|
||||||
Some(r) => CryptoReader::ZipCrypto(r),
|
Some(r) => CryptoReader::ZipCrypto(r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(Some(password), Some(aes_mode)) => {
|
||||||
|
match AesReader::new(reader, aes_mode, compressed_size).validate(&password)? {
|
||||||
|
None => return Ok(Err(InvalidPassword)),
|
||||||
|
Some(r) => CryptoReader::Aes(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(Ok(reader))
|
Ok(Ok(reader))
|
||||||
}
|
}
|
||||||
|
@ -502,6 +513,8 @@ impl<R: Read + io::Seek> ZipArchive<R> {
|
||||||
data.using_data_descriptor,
|
data.using_data_descriptor,
|
||||||
limit_reader,
|
limit_reader,
|
||||||
password,
|
password,
|
||||||
|
data.aes_mode,
|
||||||
|
data.compressed_size
|
||||||
) {
|
) {
|
||||||
Ok(Ok(crypto_reader)) => Ok(Ok(ZipFile {
|
Ok(Ok(crypto_reader)) => Ok(Ok(ZipFile {
|
||||||
crypto_reader: Some(crypto_reader),
|
crypto_reader: Some(crypto_reader),
|
||||||
|
@ -595,6 +608,7 @@ pub(crate) fn central_header_to_zip_file<R: Read + io::Seek>(
|
||||||
data_start: 0,
|
data_start: 0,
|
||||||
external_attributes: external_file_attributes,
|
external_attributes: external_file_attributes,
|
||||||
large_file: false,
|
large_file: false,
|
||||||
|
aes_mode: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
match parse_extra_field(&mut result) {
|
match parse_extra_field(&mut result) {
|
||||||
|
@ -602,6 +616,14 @@ pub(crate) fn central_header_to_zip_file<R: Read + io::Seek>(
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let aes_enabled = result.compression_method == CompressionMethod::AES;
|
||||||
|
if aes_enabled && result.aes_mode.is_none() {
|
||||||
|
return Err(ZipError::InvalidArchive(
|
||||||
|
"AES encryption without AES extra data field",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Account for shifted zip offsets.
|
// Account for shifted zip offsets.
|
||||||
result.header_start += archive_offset;
|
result.header_start += archive_offset;
|
||||||
|
|
||||||
|
@ -615,24 +637,53 @@ fn parse_extra_field(file: &mut ZipFileData) -> ZipResult<()> {
|
||||||
let kind = reader.read_u16::<LittleEndian>()?;
|
let kind = reader.read_u16::<LittleEndian>()?;
|
||||||
let len = reader.read_u16::<LittleEndian>()?;
|
let len = reader.read_u16::<LittleEndian>()?;
|
||||||
let mut len_left = len as i64;
|
let mut len_left = len as i64;
|
||||||
// Zip64 extended information extra field
|
match kind {
|
||||||
if kind == 0x0001 {
|
// Zip64 extended information extra field
|
||||||
if file.uncompressed_size == 0xFFFFFFFF {
|
0x0001 => {
|
||||||
file.large_file = true;
|
if file.uncompressed_size == 0xFFFFFFFF {
|
||||||
file.uncompressed_size = reader.read_u64::<LittleEndian>()?;
|
file.large_file = true;
|
||||||
len_left -= 8;
|
file.uncompressed_size = reader.read_u64::<LittleEndian>()?;
|
||||||
|
len_left -= 8;
|
||||||
|
}
|
||||||
|
if file.compressed_size == 0xFFFFFFFF {
|
||||||
|
file.large_file = true;
|
||||||
|
file.compressed_size = reader.read_u64::<LittleEndian>()?;
|
||||||
|
len_left -= 8;
|
||||||
|
}
|
||||||
|
if file.header_start == 0xFFFFFFFF {
|
||||||
|
file.header_start = reader.read_u64::<LittleEndian>()?;
|
||||||
|
len_left -= 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if file.compressed_size == 0xFFFFFFFF {
|
0x9901 => {
|
||||||
file.large_file = true;
|
// AES
|
||||||
file.compressed_size = reader.read_u64::<LittleEndian>()?;
|
if len != 7 {
|
||||||
len_left -= 8;
|
return Err(ZipError::UnsupportedArchive(
|
||||||
|
"AES extra data field has an unsupported length",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let _vendor_version = reader.read_u16::<LittleEndian>()?; // TODO: CRC value handling changes
|
||||||
|
let vendor_id = reader.read_u16::<LittleEndian>()?;
|
||||||
|
let aes_mode = reader.read_u8()?;
|
||||||
|
let compression_method = reader.read_u16::<LittleEndian>()?;
|
||||||
|
|
||||||
|
if vendor_id != 0x4541 {
|
||||||
|
return Err(ZipError::InvalidArchive("Invalid AES vendor"));
|
||||||
|
}
|
||||||
|
match aes_mode {
|
||||||
|
0x01 => file.aes_mode = Some(AesMode::Aes128),
|
||||||
|
0x02 => file.aes_mode = Some(AesMode::Aes192),
|
||||||
|
0x03 => file.aes_mode = Some(AesMode::Aes256),
|
||||||
|
_ => return Err(ZipError::InvalidArchive("Invalid AES encryption strength")),
|
||||||
|
};
|
||||||
|
file.compression_method = {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
CompressionMethod::from_u16(compression_method)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if file.header_start == 0xFFFFFFFF {
|
_ => {
|
||||||
file.header_start = reader.read_u64::<LittleEndian>()?;
|
// Other fields are ignored
|
||||||
len_left -= 8;
|
|
||||||
}
|
}
|
||||||
// Unparsed fields:
|
|
||||||
// u32: disk start number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We could also check for < 0 to check for errors
|
// We could also check for < 0 to check for errors
|
||||||
|
@ -950,6 +1001,7 @@ pub fn read_zipfile_from_stream<'a, R: io::Read>(
|
||||||
// from standard input, this field is set to zero.'
|
// from standard input, this field is set to zero.'
|
||||||
external_attributes: 0,
|
external_attributes: 0,
|
||||||
large_file: false,
|
large_file: false,
|
||||||
|
aes_mode: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
match parse_extra_field(&mut result) {
|
match parse_extra_field(&mut result) {
|
||||||
|
@ -975,6 +1027,8 @@ pub fn read_zipfile_from_stream<'a, R: io::Read>(
|
||||||
result.using_data_descriptor,
|
result.using_data_descriptor,
|
||||||
limit_reader,
|
limit_reader,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
|
result.compressed_size,
|
||||||
)?
|
)?
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
25
src/types.rs
25
src/types.rs
|
@ -248,6 +248,8 @@ pub struct ZipFileData {
|
||||||
pub external_attributes: u32,
|
pub external_attributes: u32,
|
||||||
/// Reserve local ZIP64 extra field
|
/// Reserve local ZIP64 extra field
|
||||||
pub large_file: bool,
|
pub large_file: bool,
|
||||||
|
/// AES mode if applicable
|
||||||
|
pub aes_mode: Option<AesMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZipFileData {
|
impl ZipFileData {
|
||||||
|
@ -298,6 +300,28 @@ impl ZipFileData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AES variant used.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum AesMode {
|
||||||
|
Aes128,
|
||||||
|
Aes192,
|
||||||
|
Aes256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AesMode {
|
||||||
|
pub fn salt_length(&self) -> u64 {
|
||||||
|
self.key_length() / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key_length(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::Aes128 => 16,
|
||||||
|
Self::Aes192 => 24,
|
||||||
|
Self::Aes256 => 32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -332,6 +356,7 @@ mod test {
|
||||||
central_header_start: 0,
|
central_header_start: 0,
|
||||||
external_attributes: 0,
|
external_attributes: 0,
|
||||||
large_file: false,
|
large_file: false,
|
||||||
|
aes_mode: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
data.file_name_sanitized(),
|
data.file_name_sanitized(),
|
||||||
|
|
|
@ -334,6 +334,7 @@ impl<W: Write + io::Seek> ZipWriter<W> {
|
||||||
central_header_start: 0,
|
central_header_start: 0,
|
||||||
external_attributes: permissions << 16,
|
external_attributes: permissions << 16,
|
||||||
large_file: options.large_file,
|
large_file: options.large_file,
|
||||||
|
aes_mode: None,
|
||||||
};
|
};
|
||||||
write_local_file_header(writer, &file)?;
|
write_local_file_header(writer, &file)?;
|
||||||
|
|
||||||
|
@ -830,6 +831,11 @@ impl<W: Write + io::Seek> GenericZipWriter<W> {
|
||||||
CompressionMethod::Bzip2 => {
|
CompressionMethod::Bzip2 => {
|
||||||
GenericZipWriter::Bzip2(BzEncoder::new(bare, bzip2::Compression::default()))
|
GenericZipWriter::Bzip2(BzEncoder::new(bare, bzip2::Compression::default()))
|
||||||
}
|
}
|
||||||
|
CompressionMethod::AES => {
|
||||||
|
return Err(ZipError::UnsupportedArchive(
|
||||||
|
"AES compression is not supported for writing",
|
||||||
|
))
|
||||||
|
}
|
||||||
CompressionMethod::Unsupported(..) => {
|
CompressionMethod::Unsupported(..) => {
|
||||||
return Err(ZipError::UnsupportedArchive("Unsupported compression"))
|
return Err(ZipError::UnsupportedArchive("Unsupported compression"))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue