2026-02-17 17:28:59 +01:00
|
|
|
use crate::utils::common;
|
2026-06-04 19:46:23 +08:00
|
|
|
use crate::utils::aes::decrypt_aes256_ecb;
|
2026-02-17 17:28:59 +01:00
|
|
|
use binrw::BinRead;
|
2026-05-05 19:27:51 +02:00
|
|
|
use crate::keys;
|
|
|
|
|
use rsa::{RsaPublicKey, BigUint};
|
2026-02-17 17:28:59 +01:00
|
|
|
|
2026-05-05 19:27:51 +02:00
|
|
|
pub fn try_find_key(sig: &[u8], ciphertext: &[u8]) -> Result<Option<(String, [u8; 32])>, Box<dyn std::error::Error>> {
|
|
|
|
|
let mut result: Option<(String, [u8; 32])> = None;
|
2026-02-17 17:28:59 +01:00
|
|
|
|
2026-06-04 19:46:23 +08:00
|
|
|
for (name, n_hex) in keys::PFLUPG {
|
2026-05-05 19:27:51 +02:00
|
|
|
let n = BigUint::from_bytes_be(&hex::decode(n_hex)?);
|
|
|
|
|
let e = BigUint::from_bytes_be(&hex::decode("010001")?);
|
|
|
|
|
let pubkey = RsaPublicKey::new(n, e)?;
|
|
|
|
|
|
|
|
|
|
let sig_int = BigUint::from_bytes_le(&sig);
|
|
|
|
|
let dec_int = rsa::hazmat::rsa_encrypt(&pubkey, &sig_int)?;
|
|
|
|
|
let dec_sig = dec_int.to_bytes_le();
|
2026-02-17 17:28:59 +01:00
|
|
|
|
2026-06-04 19:46:23 +08:00
|
|
|
let aes_key: [u8; 32] = dec_sig[20..52].try_into().unwrap();
|
|
|
|
|
let dec_ciphertext = decrypt_aes256_ecb(&aes_key, &ciphertext)?;
|
|
|
|
|
|
|
|
|
|
// Needs to start with null-terminated filename string
|
2026-05-05 19:27:51 +02:00
|
|
|
let end = match dec_ciphertext.iter().position(|&b| b == 0) {
|
|
|
|
|
Some(pos) => pos,
|
2026-06-04 19:46:23 +08:00
|
|
|
None => continue, // There is no 0, continue
|
2026-05-05 19:27:51 +02:00
|
|
|
};
|
|
|
|
|
let fname = &dec_ciphertext[..end];
|
2026-06-04 19:46:23 +08:00
|
|
|
if fname.len() > 1 && fname.is_ascii() { // Is ASCII filename
|
|
|
|
|
result = Some((name.to_string(), aes_key));
|
2026-05-05 19:27:51 +02:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
|
}
|
2026-02-17 17:28:59 +01:00
|
|
|
|
|
|
|
|
#[derive(BinRead)]
|
|
|
|
|
pub struct Header {
|
|
|
|
|
_magic_bytes: [u8; 8],
|
2026-05-05 19:27:51 +02:00
|
|
|
pub header_size: u32, //data start
|
2026-02-17 17:28:59 +01:00
|
|
|
pub data_size: u32,
|
2026-06-04 19:46:23 +08:00
|
|
|
_crc32: u32,
|
|
|
|
|
pub mask: u32,
|
|
|
|
|
_data_size_decompressed: u32,
|
|
|
|
|
_padding2: u32,
|
|
|
|
|
description_bytes: [u8; 512],
|
2026-02-17 17:28:59 +01:00
|
|
|
}
|
|
|
|
|
impl Header {
|
|
|
|
|
pub fn description(&self) -> String {
|
2026-02-18 22:54:03 +01:00
|
|
|
common::string_from_bytes(&self.description_bytes).replace('\r', "\n")
|
|
|
|
|
}
|
|
|
|
|
pub fn is_encrypted(&self) -> bool {
|
|
|
|
|
(self.mask & 0x2000_0000) != 0
|
2026-02-17 17:28:59 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(BinRead)]
|
|
|
|
|
pub struct FileHeader {
|
|
|
|
|
file_name_bytes: [u8; 60],
|
|
|
|
|
pub real_size: u32,
|
2026-06-04 19:46:23 +08:00
|
|
|
pub stored_size: u32,
|
|
|
|
|
pub header_size: u32,
|
2026-02-17 17:28:59 +01:00
|
|
|
pub attributes: [u8; 4],
|
|
|
|
|
}
|
|
|
|
|
impl FileHeader {
|
|
|
|
|
pub fn file_name(&self) -> String {
|
|
|
|
|
common::string_from_bytes(&self.file_name_bytes)
|
|
|
|
|
}
|
2026-02-18 22:54:03 +01:00
|
|
|
pub fn is_folder(&self) -> bool {
|
|
|
|
|
(self.attributes[3] & (1 << 1)) != 0
|
|
|
|
|
}
|
|
|
|
|
pub fn has_extended_name(&self) -> bool {
|
|
|
|
|
(self.attributes[2] & (1 << 7)) != 0
|
|
|
|
|
}
|
2026-05-05 19:27:51 +02:00
|
|
|
pub fn is_package(&self) -> bool {
|
|
|
|
|
(self.attributes[3] & (1 << 2)) != 0
|
2026-02-18 22:54:03 +01:00
|
|
|
}
|
2026-06-04 19:46:23 +08:00
|
|
|
}
|