#![allow(clippy::unwrap_used, clippy::expect_used, clippy::doc_markdown)]
use std::io::{Cursor, Read};
use std::path::PathBuf;
use zip_core::ZipArchive;
const PW: &[u8] = b"Infected123";
fn payload() -> Vec<u8> {
(0..20_000u32).map(|i| (i / 64) as u8).collect()
}
fn fixture(name: &str) -> Vec<u8> {
let path = PathBuf::from(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../tests/data/encrypted"
))
.join(name);
std::fs::read(&path).unwrap_or_else(|e| panic!("read fixture {}: {e}", path.display()))
}
fn zip_core_decrypt(bytes: &[u8], name: &str, pw: &[u8]) -> Vec<u8> {
let mut ar = ZipArchive::new(Cursor::new(bytes.to_vec())).unwrap();
let mut e = ar.by_name_decrypt(name, pw).unwrap();
let mut out = Vec::new();
e.read_to_end(&mut out).unwrap();
out
}
#[test]
fn zipcrypto_decrypts_to_payload() {
assert_eq!(
zip_core_decrypt(&fixture("zipcrypto.zip"), "file.bin", PW),
payload()
);
}
#[test]
fn aes256_decrypts_to_payload() {
assert_eq!(
zip_core_decrypt(&fixture("aes256.zip"), "file.bin", PW),
payload()
);
}
#[test]
fn encrypted_entry_without_password_errors() {
let mut ar = ZipArchive::new(Cursor::new(fixture("zipcrypto.zip"))).unwrap();
assert!(ar.by_name("file.bin").is_err());
let mut ar = ZipArchive::new(Cursor::new(fixture("aes256.zip"))).unwrap();
assert!(ar.by_name("file.bin").is_err());
}
#[test]
fn wrong_password_errors() {
let mut ar = ZipArchive::new(Cursor::new(fixture("zipcrypto.zip"))).unwrap();
assert!(ar.by_name_decrypt("file.bin", b"wrong-password").is_err());
let mut ar = ZipArchive::new(Cursor::new(fixture("aes256.zip"))).unwrap();
assert!(ar.by_name_decrypt("file.bin", b"wrong-password").is_err());
}
#[test]
fn zipcrypto_real_xloader_matches_oracle() {
let Ok(path) = std::env::var("ZIP_CORE_ZIPCRYPTO_ZIP") else {
eprintln!("skipping: ZIP_CORE_ZIPCRYPTO_ZIP not set");
return;
};
let pw = b"infected";
let entry = "XLoader/Statement SKBMT 09818/resources/NVFFY";
let bytes = std::fs::read(&path).unwrap();
let got = zip_core_decrypt(&bytes, entry, pw);
let mut oar = zip::ZipArchive::new(Cursor::new(bytes)).unwrap();
let mut oe = oar.by_name_decrypt(entry, pw).unwrap();
let mut want = Vec::new();
oe.read_to_end(&mut want).unwrap();
assert_eq!(got, want, "zip-core ZipCrypto decrypt vs zip-rs oracle");
}
#[test]
fn by_index_decrypt_works() {
let mut ar = ZipArchive::new(Cursor::new(fixture("zipcrypto.zip"))).unwrap();
let mut e = ar.by_index_decrypt(0, PW).unwrap();
let mut out = Vec::new();
e.read_to_end(&mut out).unwrap();
assert_eq!(out, payload());
}
#[test]
fn decrypt_on_unencrypted_entry_just_reads() {
use zip::write::SimpleFileOptions;
use zip::ZipWriter;
let mut zw = ZipWriter::new(Cursor::new(Vec::new()));
zw.start_file("plain.bin", SimpleFileOptions::default())
.unwrap();
std::io::Write::write_all(&mut zw, &payload()).unwrap();
let bytes = zw.finish().unwrap().into_inner();
let mut ar = ZipArchive::new(Cursor::new(bytes)).unwrap();
let mut e = ar.by_name_decrypt("plain.bin", b"ignored").unwrap();
let mut out = Vec::new();
e.read_to_end(&mut out).unwrap();
assert_eq!(out, payload());
}