use crc16::{State, ARC};
use std::sync::Arc;
use super::local_file_header::CompressionMethod;
use super::{crushed, distilled, lzw, rle, unsqueeze};
use crate::error::Result;
fn decrypt_into(data: &[u8], password: &[u8], output: &mut Vec<u8>) {
output.clear();
output.reserve(data.len());
if password.is_empty() {
output.extend_from_slice(data);
return;
}
let mut key_pos = 0;
for &byte in data {
output.push(byte ^ password[key_pos]);
key_pos += 1;
if key_pos >= password.len() {
key_pos = 0;
}
}
}
#[derive(Clone)]
pub struct ArcPasswordVerifier {
compressed_data: Arc<[u8]>,
compression_method: CompressionMethod,
expected_crc: u16,
original_size: u32,
entry_name: String,
}
unsafe impl Send for ArcPasswordVerifier {}
unsafe impl Sync for ArcPasswordVerifier {}
impl ArcPasswordVerifier {
pub fn new(compressed_data: Vec<u8>, compression_method: CompressionMethod, expected_crc: u16, original_size: u32, entry_name: String) -> Self {
Self {
compressed_data: Arc::from(compressed_data.into_boxed_slice()),
compression_method,
expected_crc,
original_size,
entry_name,
}
}
pub fn entry_name(&self) -> &str {
&self.entry_name
}
pub fn compressed_size(&self) -> usize {
self.compressed_data.len()
}
pub fn original_size(&self) -> u32 {
self.original_size
}
pub fn verify(&self, password: &str) -> bool {
thread_local! {
static DECRYPT_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(64 * 1024));
static LZW_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(64 * 1024));
static RLE_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(64 * 1024));
static LZW: std::cell::RefCell<lzw::Lzw> = std::cell::RefCell::new(lzw::Lzw::new());
}
DECRYPT_BUF.with(|decrypt_buf| {
LZW_BUF.with(|lzw_buf| {
RLE_BUF.with(|rle_buf| {
LZW.with(|lzw_cell| {
let mut decrypt_buf = decrypt_buf.borrow_mut();
let mut lzw_buf = lzw_buf.borrow_mut();
let mut rle_buf = rle_buf.borrow_mut();
let mut lzw = lzw_cell.borrow_mut();
decrypt_into(&self.compressed_data, password.as_bytes(), &mut decrypt_buf);
self.decompress_and_verify_reuse(&decrypt_buf, &mut lzw, &mut lzw_buf, &mut rle_buf)
.unwrap_or(false)
})
})
})
})
}
fn decompress_and_verify_reuse(&self, decrypted: &[u8], lzw: &mut lzw::Lzw, lzw_buf: &mut Vec<u8>, rle_buf: &mut Vec<u8>) -> Result<bool> {
match self.compression_method {
CompressionMethod::Unpacked(_) => {
Ok(self.check_crc_and_size(decrypted))
}
CompressionMethod::RLE90 => {
rle::unpack_rle_into(decrypted, rle_buf);
Ok(self.check_crc_and_size(rle_buf))
}
CompressionMethod::Squeezed => {
let uncompressed = unsqueeze::unsqueeze(decrypted)?;
Ok(self.check_crc_and_size(&uncompressed))
}
CompressionMethod::Crunched(_) => {
lzw.decomp_into(decrypted, true, lzw_buf)?;
rle::unpack_rle_into(lzw_buf, rle_buf);
Ok(self.check_crc_and_size(rle_buf))
}
CompressionMethod::Squashed => {
lzw.decomp_into(decrypted, false, lzw_buf)?;
Ok(self.check_crc_and_size(lzw_buf))
}
CompressionMethod::Crushed => {
let decompressed = crushed::decompress(decrypted)?;
rle::unpack_rle_into(&decompressed, rle_buf);
Ok(self.check_crc_and_size(rle_buf))
}
CompressionMethod::Distilled => {
let uncompressed = distilled::decompress(decrypted)?;
Ok(self.check_crc_and_size(&uncompressed))
}
CompressionMethod::Unknown(_) => Ok(false),
}
}
#[allow(dead_code)]
fn decompress_and_verify_with_lzw(&self, decrypted: &[u8], lzw: &mut lzw::Lzw) -> Result<bool> {
let uncompressed = match self.compression_method {
CompressionMethod::Unpacked(_) => {
return Ok(self.check_crc(decrypted));
}
CompressionMethod::RLE90 => rle::unpack_rle(decrypted),
CompressionMethod::Squeezed => unsqueeze::unsqueeze(decrypted)?,
CompressionMethod::Crunched(_) => {
let decompressed = lzw.decomp(decrypted, true)?;
rle::unpack_rle(&decompressed)
}
CompressionMethod::Squashed => lzw.decomp(decrypted, false)?,
CompressionMethod::Crushed => {
let decompressed = crushed::decompress(decrypted)?;
rle::unpack_rle(&decompressed)
}
CompressionMethod::Distilled => distilled::decompress(decrypted)?,
CompressionMethod::Unknown(_) => return Ok(false),
};
Ok(self.check_crc(&uncompressed))
}
#[allow(dead_code)]
fn decompress_and_verify(&self, decrypted: &[u8]) -> Result<bool> {
let uncompressed = match self.compression_method {
CompressionMethod::Unpacked(_) => {
return Ok(self.check_crc(decrypted));
}
CompressionMethod::RLE90 => rle::unpack_rle(decrypted),
CompressionMethod::Squeezed => unsqueeze::unsqueeze(decrypted)?,
CompressionMethod::Crunched(_) => {
let decompressed = lzw::Lzw::new().decomp(decrypted, true)?;
rle::unpack_rle(&decompressed)
}
CompressionMethod::Squashed => lzw::Lzw::new().decomp(decrypted, false)?,
CompressionMethod::Crushed => {
let decompressed = crushed::decompress(decrypted)?;
rle::unpack_rle(&decompressed)
}
CompressionMethod::Distilled => distilled::decompress(decrypted)?,
CompressionMethod::Unknown(_) => return Ok(false),
};
Ok(self.check_crc(&uncompressed))
}
#[inline]
fn check_crc_and_size(&self, data: &[u8]) -> bool {
if data.len() != self.original_size as usize {
return false;
}
let mut state = State::<ARC>::new();
state.update(data);
state.get() == self.expected_crc
}
#[inline]
fn check_crc(&self, data: &[u8]) -> bool {
let mut state = State::<ARC>::new();
state.update(data);
state.get() == self.expected_crc
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decrypt_into() {
let data = b"Hello";
let password = b"key";
let mut output = Vec::new();
decrypt_into(data, password, &mut output);
let mut decrypted = Vec::new();
decrypt_into(&output, password, &mut decrypted);
assert_eq!(&decrypted, data);
}
#[test]
fn test_verifier_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ArcPasswordVerifier>();
}
}