use stuffr::infozip::ByteSource;
use stuffr::infozip::crypt::{self, RAND_HEAD_LEN};
use super::super::fileio::{self, Pw};
use super::super::ttyio::Password;
use super::super::{PK_COOL, PK_WARN, Uz, flavour};
use super::output::{self, EntrySrc};
use super::{Ex, PK_ERR, PK_MEM2};
fn testkey(uz: &mut Uz, ex: &mut Ex, h: &[u8; RAND_HEAD_LEN], key: &[u8]) -> bool {
let check = crypt::check_byte(
ex.pinfo.ext_loc_hdr,
uz.lrec.crc32,
uz.lrec.last_mod_dos_datetime,
);
let Some(mut keys) = crypt::test_key(key, h, check) else {
return false; };
let n = uz.zin.incnt.min(uz.csize).max(0) as usize;
let a = uz.zin.inptr.min(uz.zin.inbuf.len());
let b = (a + n).min(uz.zin.inbuf.len());
keys.decrypt(&mut uz.zin.inbuf[a..b]);
ex.keys = Some(keys);
true }
pub(crate) fn decrypt_entry(uz: &mut Uz, ex: &mut Ex, passwrd: Option<&[u8]>) -> i32 {
let mut src = EntrySrc {
zin: std::mem::take(&mut uz.zin),
csize: uz.csize,
keys: None,
read_error: false,
};
output::defer_leftover_input(&mut src.zin, &mut src.csize);
let mut h = [0u8; RAND_HEAD_LEN];
for b in h.iter_mut() {
*b = match src.next_byte() {
Some(c) => c,
None if flavour::cve_2015_7696_short_header(uz.flavour) => {
uz.zin = std::mem::take(&mut src.zin);
uz.csize = src.csize;
output::finish_read(uz, src.read_error);
if uz.abort.is_some() {
return super::super::PK_BADERR;
}
return PK_ERR;
}
None => 0xff,
};
}
output::undefer_input(uz, &mut src.zin, &mut src.csize);
uz.zin = std::mem::take(&mut src.zin);
uz.csize = src.csize;
output::finish_read(uz, src.read_error);
if uz.abort.is_some() {
return super::super::PK_BADERR;
}
if uz.newzip {
uz.newzip = false;
if let Some(p) = passwrd {
if uz.key.is_none() {
uz.key = Some(Password::new(p));
uz.nopwd = true; }
} else if uz.key.is_some() {
uz.key = None;
}
}
if uz.key.is_some() {
if testp(uz, ex, &h) {
return PK_COOL; } else if uz.nopwd {
return PK_WARN; }
}
let mut n = 0;
loop {
let r = fileio::uzp_password(uz, &mut n);
let cancel_all = matches!(r, Pw::CancelAll);
uz.key = match r {
Pw::Error => {
uz.key = None;
return PK_MEM2;
}
Pw::Entered(k) => Some(k),
Pw::CancelAll => {
n = 0;
Some(Password::new(b""))
}
};
if testp(uz, ex, &h) {
return PK_COOL;
}
if cancel_all {
uz.nopwd = true; }
if n <= 0 {
break;
}
}
PK_WARN
}
fn testp(uz: &mut Uz, ex: &mut Ex, h: &[u8; RAND_HEAD_LEN]) -> bool {
let Some(key) = uz.key.take() else {
return false;
};
let ok = testkey(uz, ex, h, key.key());
uz.key = Some(key);
ok
}