Merge upstream version 0.6.5
This commit is contained in:
commit
67bfe53d65
8 changed files with 138 additions and 47 deletions
|
@ -111,3 +111,10 @@
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed a bug that occurs when a filename in a ZIP32 file includes the ZIP64 magic bytes.
|
- Fixed a bug that occurs when a filename in a ZIP32 file includes the ZIP64 magic bytes.
|
||||||
|
|
||||||
|
## [0.7.4]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Added experimental [`zip::unstable::write::FileOptions::with_deprecated_encryption`] API to enable encrypting files
|
||||||
|
with PKWARE encryption.
|
||||||
|
|
|
@ -7,6 +7,7 @@ license = "MIT"
|
||||||
repository = "https://github.com/Pr0methean/zip-next.git"
|
repository = "https://github.com/Pr0methean/zip-next.git"
|
||||||
keywords = ["zip", "archive"]
|
keywords = ["zip", "archive"]
|
||||||
description = """
|
description = """
|
||||||
|
rust-version = "1.66.0"
|
||||||
Library to support the reading and writing of zip files.
|
Library to support the reading and writing of zip files.
|
||||||
"""
|
"""
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
|
@ -49,6 +49,6 @@ mod zipcrypto;
|
||||||
///
|
///
|
||||||
/// ```toml
|
/// ```toml
|
||||||
/// [dependencies]
|
/// [dependencies]
|
||||||
/// zip = "=0.6.4"
|
/// zip = "=0.6.5"
|
||||||
/// ```
|
/// ```
|
||||||
pub mod unstable;
|
pub mod unstable;
|
||||||
|
|
21
src/types.rs
21
src/types.rs
|
@ -567,27 +567,6 @@ mod test {
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
|
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
|
||||||
#[test]
|
|
||||||
fn datetime_from_time_bounds() {
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
use super::DateTime;
|
|
||||||
use time::macros::datetime;
|
|
||||||
|
|
||||||
// 1979-12-31 23:59:59
|
|
||||||
assert!(DateTime::try_from(datetime!(1979-12-31 23:59:59 UTC)).is_err());
|
|
||||||
|
|
||||||
// 1980-01-01 00:00:00
|
|
||||||
assert!(DateTime::try_from(datetime!(1980-01-01 00:00:00 UTC)).is_ok());
|
|
||||||
|
|
||||||
// 2107-12-31 23:59:59
|
|
||||||
assert!(DateTime::try_from(datetime!(2107-12-31 23:59:59 UTC)).is_ok());
|
|
||||||
|
|
||||||
// 2108-01-01 00:00:00
|
|
||||||
assert!(DateTime::try_from(datetime!(2108-01-01 00:00:00 UTC)).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
#[test]
|
#[test]
|
||||||
fn datetime_try_from_bounds() {
|
fn datetime_try_from_bounds() {
|
||||||
|
|
|
@ -2,3 +2,19 @@
|
||||||
pub mod stream {
|
pub mod stream {
|
||||||
pub use crate::read::stream::*;
|
pub use crate::read::stream::*;
|
||||||
}
|
}
|
||||||
|
/// Types for creating ZIP archives.
|
||||||
|
pub mod write {
|
||||||
|
use crate::write::FileOptions;
|
||||||
|
/// Unstable methods for [`FileOptions`].
|
||||||
|
pub trait FileOptionsExt {
|
||||||
|
/// Write the file with the given password using the deprecated ZipCrypto algorithm.
|
||||||
|
///
|
||||||
|
/// This is not recommended for new archives, as ZipCrypto is not secure.
|
||||||
|
fn with_deprecated_encryption(self, password: &[u8]) -> Self;
|
||||||
|
}
|
||||||
|
impl FileOptionsExt for FileOptions {
|
||||||
|
fn with_deprecated_encryption(self, password: &[u8]) -> Self {
|
||||||
|
self.with_deprecated_encryption(password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
src/write.rs
63
src/write.rs
|
@ -31,19 +31,37 @@ use time::OffsetDateTime;
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
use zstd::stream::write::Encoder as ZstdEncoder;
|
use zstd::stream::write::Encoder as ZstdEncoder;
|
||||||
|
|
||||||
|
enum MaybeEncrypted<W> {
|
||||||
|
Unencrypted(W),
|
||||||
|
Encrypted(crate::zipcrypto::ZipCryptoWriter<W>),
|
||||||
|
}
|
||||||
|
impl<W: Write> Write for MaybeEncrypted<W> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
match self {
|
||||||
|
MaybeEncrypted::Unencrypted(w) => w.write(buf),
|
||||||
|
MaybeEncrypted::Encrypted(w) => w.write(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
MaybeEncrypted::Unencrypted(w) => w.flush(),
|
||||||
|
MaybeEncrypted::Encrypted(w) => w.flush(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
enum GenericZipWriter<W: Write + Seek> {
|
enum GenericZipWriter<W: Write + Seek> {
|
||||||
Closed,
|
Closed,
|
||||||
Storer(W),
|
Storer(MaybeEncrypted<W>),
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "deflate",
|
feature = "deflate",
|
||||||
feature = "deflate-miniz",
|
feature = "deflate-miniz",
|
||||||
feature = "deflate-zlib"
|
feature = "deflate-zlib"
|
||||||
))]
|
))]
|
||||||
Deflater(DeflateEncoder<W>),
|
Deflater(DeflateEncoder<MaybeEncrypted<W>>),
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
Bzip2(BzEncoder<W>),
|
Bzip2(BzEncoder<MaybeEncrypted<W>>),
|
||||||
#[cfg(feature = "zstd")]
|
#[cfg(feature = "zstd")]
|
||||||
Zstd(ZstdEncoder<'static, W>),
|
Zstd(ZstdEncoder<'static, MaybeEncrypted<W>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put the struct declaration in a private module to convince rustdoc to display ZipWriter nicely
|
// Put the struct declaration in a private module to convince rustdoc to display ZipWriter nicely
|
||||||
|
@ -115,6 +133,7 @@ pub struct FileOptions {
|
||||||
pub(crate) last_modified_time: DateTime,
|
pub(crate) last_modified_time: DateTime,
|
||||||
pub(crate) permissions: Option<u32>,
|
pub(crate) permissions: Option<u32>,
|
||||||
pub(crate) large_file: bool,
|
pub(crate) large_file: bool,
|
||||||
|
encrypt_with: Option<crate::zipcrypto::ZipCryptoKeys>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileOptions {
|
impl FileOptions {
|
||||||
|
@ -178,6 +197,10 @@ impl FileOptions {
|
||||||
self.large_file = large;
|
self.large_file = large;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
pub(crate) fn with_deprecated_encryption(mut self, password: &[u8]) -> FileOptions {
|
||||||
|
self.encrypt_with = Some(crate::zipcrypto::ZipCryptoKeys::derive(password));
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FileOptions {
|
impl Default for FileOptions {
|
||||||
|
@ -203,6 +226,7 @@ impl Default for FileOptions {
|
||||||
last_modified_time: DateTime::default(),
|
last_modified_time: DateTime::default(),
|
||||||
permissions: None,
|
permissions: None,
|
||||||
large_file: false,
|
large_file: false,
|
||||||
|
encrypt_with: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,7 +317,7 @@ impl<A: Read + Write + Seek> ZipWriter<A> {
|
||||||
let _ = readwriter.seek(SeekFrom::Start(directory_start)); // seek directory_start to overwrite it
|
let _ = readwriter.seek(SeekFrom::Start(directory_start)); // seek directory_start to overwrite it
|
||||||
|
|
||||||
Ok(ZipWriter {
|
Ok(ZipWriter {
|
||||||
inner: Storer(readwriter),
|
inner: Storer(MaybeEncrypted::Unencrypted(readwriter)),
|
||||||
files,
|
files,
|
||||||
files_by_name,
|
files_by_name,
|
||||||
stats: Default::default(),
|
stats: Default::default(),
|
||||||
|
@ -360,7 +384,7 @@ impl<W: Write + Seek> ZipWriter<W> {
|
||||||
/// [`ZipWriter::is_writing_file`] to determine if the file remains open.
|
/// [`ZipWriter::is_writing_file`] to determine if the file remains open.
|
||||||
pub fn new(inner: W) -> ZipWriter<W> {
|
pub fn new(inner: W) -> ZipWriter<W> {
|
||||||
ZipWriter {
|
ZipWriter {
|
||||||
inner: Storer(inner),
|
inner: Storer(MaybeEncrypted::Unencrypted(inner)),
|
||||||
files: Vec::new(),
|
files: Vec::new(),
|
||||||
files_by_name: HashMap::new(),
|
files_by_name: HashMap::new(),
|
||||||
stats: Default::default(),
|
stats: Default::default(),
|
||||||
|
@ -419,7 +443,7 @@ impl<W: Write + Seek> ZipWriter<W> {
|
||||||
let file = ZipFileData {
|
let file = ZipFileData {
|
||||||
system: System::Unix,
|
system: System::Unix,
|
||||||
version_made_by: DEFAULT_VERSION,
|
version_made_by: DEFAULT_VERSION,
|
||||||
encrypted: false,
|
encrypted: options.encrypt_with.is_some(),
|
||||||
using_data_descriptor: false,
|
using_data_descriptor: false,
|
||||||
compression_method: options.compression_method,
|
compression_method: options.compression_method,
|
||||||
compression_level: options.compression_level,
|
compression_level: options.compression_level,
|
||||||
|
@ -450,7 +474,13 @@ impl<W: Write + Seek> ZipWriter<W> {
|
||||||
self.stats.bytes_written = 0;
|
self.stats.bytes_written = 0;
|
||||||
self.stats.hasher = Hasher::new();
|
self.stats.hasher = Hasher::new();
|
||||||
}
|
}
|
||||||
|
if let Some(keys) = options.encrypt_with {
|
||||||
|
let mut zipwriter = crate::zipcrypto::ZipCryptoWriter { writer: mem::replace(&mut self.inner, Closed).unwrap(), buffer: vec![], keys };
|
||||||
|
let crypto_header = [0u8; 12];
|
||||||
|
|
||||||
|
zipwriter.write_all(&crypto_header)?;
|
||||||
|
self.inner = Storer(MaybeEncrypted::Encrypted(zipwriter));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1005,7 +1035,7 @@ impl<W: Write + Seek> GenericZipWriter<W> {
|
||||||
&self,
|
&self,
|
||||||
compression: CompressionMethod,
|
compression: CompressionMethod,
|
||||||
compression_level: Option<i32>,
|
compression_level: Option<i32>,
|
||||||
) -> ZipResult<Box<dyn FnOnce(W) -> GenericZipWriter<W>>> {
|
) -> ZipResult<Box<dyn FnOnce(MaybeEncrypted<W>) -> GenericZipWriter<W>>> {
|
||||||
if let Closed = self {
|
if let Closed = self {
|
||||||
return Err(
|
return Err(
|
||||||
io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed").into(),
|
io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed").into(),
|
||||||
|
@ -1085,7 +1115,7 @@ impl<W: Write + Seek> GenericZipWriter<W> {
|
||||||
|
|
||||||
fn switch_to(
|
fn switch_to(
|
||||||
&mut self,
|
&mut self,
|
||||||
make_new_self: Box<dyn FnOnce(W) -> GenericZipWriter<W>>,
|
make_new_self: Box<dyn FnOnce(MaybeEncrypted<W>) -> GenericZipWriter<W>>,
|
||||||
) -> ZipResult<()> {
|
) -> ZipResult<()> {
|
||||||
let bare = match mem::replace(self, Closed) {
|
let bare = match mem::replace(self, Closed) {
|
||||||
Storer(w) => w,
|
Storer(w) => w,
|
||||||
|
@ -1134,15 +1164,15 @@ impl<W: Write + Seek> GenericZipWriter<W> {
|
||||||
|
|
||||||
fn get_plain(&mut self) -> &mut W {
|
fn get_plain(&mut self) -> &mut W {
|
||||||
match *self {
|
match *self {
|
||||||
Storer(ref mut w) => w,
|
Storer(MaybeEncrypted::Unencrypted(ref mut w)) => w,
|
||||||
_ => panic!("Should have switched to stored beforehand"),
|
_ => panic!("Should have switched to stored and unencrypted beforehand"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap(self) -> W {
|
fn unwrap(self) -> W {
|
||||||
match self {
|
match self {
|
||||||
Storer(w) => w,
|
Storer(MaybeEncrypted::Unencrypted(w)) => w,
|
||||||
_ => panic!("Should have switched to stored beforehand"),
|
_ => panic!("Should have switched to stored and unencrypted beforehand"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1190,7 +1220,7 @@ fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipR
|
||||||
1u16 << 11
|
1u16 << 11
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
} | if file.encrypted { 1u16 << 0 } else { 0 };
|
||||||
writer.write_u16::<LittleEndian>(flag)?;
|
writer.write_u16::<LittleEndian>(flag)?;
|
||||||
// Compression method
|
// Compression method
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
|
@ -1262,7 +1292,7 @@ fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData)
|
||||||
1u16 << 11
|
1u16 << 11
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
} | if file.encrypted { 1u16 << 0 } else { 0 };
|
||||||
writer.write_u16::<LittleEndian>(flag)?;
|
writer.write_u16::<LittleEndian>(flag)?;
|
||||||
// compression method
|
// compression method
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
|
@ -1558,6 +1588,7 @@ mod test {
|
||||||
last_modified_time: DateTime::default(),
|
last_modified_time: DateTime::default(),
|
||||||
permissions: Some(33188),
|
permissions: Some(33188),
|
||||||
large_file: false,
|
large_file: false,
|
||||||
|
encrypt_with: None,
|
||||||
};
|
};
|
||||||
writer.start_file("mimetype", options).unwrap();
|
writer.start_file("mimetype", options).unwrap();
|
||||||
writer
|
writer
|
||||||
|
@ -1594,6 +1625,7 @@ mod test {
|
||||||
last_modified_time: DateTime::default(),
|
last_modified_time: DateTime::default(),
|
||||||
permissions: Some(33188),
|
permissions: Some(33188),
|
||||||
large_file: false,
|
large_file: false,
|
||||||
|
encrypt_with: None,
|
||||||
};
|
};
|
||||||
writer.start_file(RT_TEST_FILENAME, options).unwrap();
|
writer.start_file(RT_TEST_FILENAME, options).unwrap();
|
||||||
writer.write_all(RT_TEST_TEXT.as_ref()).unwrap();
|
writer.write_all(RT_TEST_TEXT.as_ref()).unwrap();
|
||||||
|
@ -1640,6 +1672,7 @@ mod test {
|
||||||
last_modified_time: DateTime::default(),
|
last_modified_time: DateTime::default(),
|
||||||
permissions: Some(33188),
|
permissions: Some(33188),
|
||||||
large_file: false,
|
large_file: false,
|
||||||
|
encrypt_with: None,
|
||||||
};
|
};
|
||||||
writer.start_file(RT_TEST_FILENAME, options).unwrap();
|
writer.start_file(RT_TEST_FILENAME, options).unwrap();
|
||||||
writer.write_all(RT_TEST_TEXT.as_ref()).unwrap();
|
writer.write_all(RT_TEST_TEXT.as_ref()).unwrap();
|
||||||
|
|
|
@ -3,15 +3,28 @@
|
||||||
//! The following paper was used to implement the ZipCrypto algorithm:
|
//! The following paper was used to implement the ZipCrypto algorithm:
|
||||||
//! [https://courses.cs.ut.ee/MTAT.07.022/2015_fall/uploads/Main/dmitri-report-f15-16.pdf](https://courses.cs.ut.ee/MTAT.07.022/2015_fall/uploads/Main/dmitri-report-f15-16.pdf)
|
//! [https://courses.cs.ut.ee/MTAT.07.022/2015_fall/uploads/Main/dmitri-report-f15-16.pdf](https://courses.cs.ut.ee/MTAT.07.022/2015_fall/uploads/Main/dmitri-report-f15-16.pdf)
|
||||||
|
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::num::Wrapping;
|
use std::num::Wrapping;
|
||||||
|
|
||||||
|
|
||||||
/// A container to hold the current key state
|
/// A container to hold the current key state
|
||||||
struct ZipCryptoKeys {
|
#[derive(Clone, Copy, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub(crate) struct ZipCryptoKeys {
|
||||||
key_0: Wrapping<u32>,
|
key_0: Wrapping<u32>,
|
||||||
key_1: Wrapping<u32>,
|
key_1: Wrapping<u32>,
|
||||||
key_2: Wrapping<u32>,
|
key_2: Wrapping<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for ZipCryptoKeys {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut t = DefaultHasher::new();
|
||||||
|
self.hash(&mut t);
|
||||||
|
f.write_fmt(format_args!("ZipCryptoKeys(hash {})", t.finish()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ZipCryptoKeys {
|
impl ZipCryptoKeys {
|
||||||
fn new() -> ZipCryptoKeys {
|
fn new() -> ZipCryptoKeys {
|
||||||
ZipCryptoKeys {
|
ZipCryptoKeys {
|
||||||
|
@ -49,6 +62,13 @@ impl ZipCryptoKeys {
|
||||||
fn crc32(crc: Wrapping<u32>, input: u8) -> Wrapping<u32> {
|
fn crc32(crc: Wrapping<u32>, input: u8) -> Wrapping<u32> {
|
||||||
(crc >> 8) ^ Wrapping(CRCTABLE[((crc & Wrapping(0xff)).0 as u8 ^ input) as usize])
|
(crc >> 8) ^ Wrapping(CRCTABLE[((crc & Wrapping(0xff)).0 as u8 ^ input) as usize])
|
||||||
}
|
}
|
||||||
|
pub(crate) fn derive(password: &[u8]) -> ZipCryptoKeys {
|
||||||
|
let mut keys = ZipCryptoKeys::new();
|
||||||
|
for byte in password.iter() {
|
||||||
|
keys.update(*byte);
|
||||||
|
}
|
||||||
|
keys
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A ZipCrypto reader with unverified password
|
/// A ZipCrypto reader with unverified password
|
||||||
|
@ -70,17 +90,10 @@ impl<R: std::io::Read> ZipCryptoReader<R> {
|
||||||
/// would be impossible to decrypt files that were encrypted with a
|
/// would be impossible to decrypt files that were encrypted with a
|
||||||
/// password byte sequence that is unrepresentable in UTF-8.
|
/// password byte sequence that is unrepresentable in UTF-8.
|
||||||
pub fn new(file: R, password: &[u8]) -> ZipCryptoReader<R> {
|
pub fn new(file: R, password: &[u8]) -> ZipCryptoReader<R> {
|
||||||
let mut result = ZipCryptoReader {
|
ZipCryptoReader {
|
||||||
file,
|
file,
|
||||||
keys: ZipCryptoKeys::new(),
|
keys: ZipCryptoKeys::derive(password),
|
||||||
};
|
|
||||||
|
|
||||||
// Key the cipher by updating the keys with the password.
|
|
||||||
for byte in password.iter() {
|
|
||||||
result.keys.update(*byte);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the ZipCrypto header bytes and validate the password.
|
/// Read the ZipCrypto header bytes and validate the password.
|
||||||
|
@ -122,6 +135,31 @@ impl<R: std::io::Read> ZipCryptoReader<R> {
|
||||||
Ok(Some(ZipCryptoReaderValid { reader: self }))
|
Ok(Some(ZipCryptoReaderValid { reader: self }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub(crate) struct ZipCryptoWriter<W> {
|
||||||
|
pub(crate) writer: W,
|
||||||
|
pub(crate) buffer: Vec<u8>,
|
||||||
|
pub(crate) keys: ZipCryptoKeys,
|
||||||
|
}
|
||||||
|
impl<W: std::io::Write> ZipCryptoWriter<W> {
|
||||||
|
pub(crate) fn finish(mut self, crc32: u32) -> std::io::Result<W> {
|
||||||
|
self.buffer[11] = (crc32 >> 24) as u8;
|
||||||
|
for byte in self.buffer.iter_mut() {
|
||||||
|
*byte = self.keys.encrypt_byte(*byte);
|
||||||
|
}
|
||||||
|
self.writer.write_all(&self.buffer)?;
|
||||||
|
self.writer.flush()?;
|
||||||
|
Ok(self.writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<W: std::io::Write> std::io::Write for ZipCryptoWriter<W> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
self.buffer.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A ZipCrypto reader with verified password
|
/// A ZipCrypto reader with verified password
|
||||||
pub struct ZipCryptoReaderValid<R> {
|
pub struct ZipCryptoReaderValid<R> {
|
||||||
|
|
|
@ -20,6 +20,23 @@
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encrypting_file() {
|
||||||
|
use zip::unstable::write::FileOptionsExt;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
let mut buf = vec![0; 2048];
|
||||||
|
let mut archive = zip::write::ZipWriter::new(Cursor::new(&mut buf));
|
||||||
|
archive.start_file("name", zip::write::FileOptions::default().with_deprecated_encryption(b"password")).unwrap();
|
||||||
|
archive.write_all(b"test").unwrap();
|
||||||
|
archive.finish().unwrap();
|
||||||
|
drop(archive);
|
||||||
|
let mut archive = zip::ZipArchive::new(Cursor::new(&mut buf)).unwrap();
|
||||||
|
let mut file = archive.by_index_decrypt(0, b"password").unwrap().unwrap();
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
file.read_to_end(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf, b"test");
|
||||||
|
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn encrypted_file() {
|
fn encrypted_file() {
|
||||||
let zip_file_bytes = &mut Cursor::new(vec![
|
let zip_file_bytes = &mut Cursor::new(vec![
|
||||||
|
|
Loading…
Add table
Reference in a new issue