Handle AES key parsing outside of pak

This commit is contained in:
Truman Kilen 2023-01-16 23:27:36 -06:00
parent 6c2fe4dfb7
commit d62915ec16
8 changed files with 29 additions and 20 deletions

View file

@ -18,3 +18,4 @@ aes = "0.8"
flate2 = "1.0" flate2 = "1.0"
hashbrown = "0.13" hashbrown = "0.13"
thiserror = "1.0" thiserror = "1.0"
base64 = "0.21.0"

View file

@ -1,4 +1,4 @@
pub fn list(path: String, key: String) -> Result<(), unpak::Error> { pub fn list(path: String, key: Option<String>) -> Result<(), unpak::Error> {
for file in super::load_pak(path, key)?.files() { for file in super::load_pak(path, key)?.files() {
println!("{file}"); println!("{file}");
} }

View file

@ -5,16 +5,27 @@ pub use {list::list, unpack::unpack, version::version};
fn load_pak( fn load_pak(
path: String, path: String,
key: String, key: Option<String>,
) -> Result<unpak::Pak<std::io::BufReader<std::fs::File>>, unpak::Error> { ) -> Result<unpak::Pak<std::io::BufReader<std::fs::File>>, unpak::Error> {
use aes::cipher::KeyInit;
use base64::{engine::general_purpose, Engine as _};
let key = key
.map(|k| {
general_purpose::STANDARD
.decode(k)
.as_ref()
.map_err(|_| unpak::Error::Base64)
.and_then(|bytes| {
aes::Aes256Dec::new_from_slice(bytes).map_err(|_| unpak::Error::Aes)
})
})
.transpose()?;
for ver in unpak::Version::iter() { for ver in unpak::Version::iter() {
match unpak::Pak::new( match unpak::Pak::new(
std::io::BufReader::new(std::fs::OpenOptions::new().read(true).open(&path)?), std::io::BufReader::new(std::fs::OpenOptions::new().read(true).open(&path)?),
ver, ver,
match key.as_bytes() { key.clone(),
&[] => None,
key => Some(key),
},
) { ) {
Ok(pak) => { Ok(pak) => {
return Ok(pak); return Ok(pak);

View file

@ -1,4 +1,4 @@
pub fn unpack(path: String, key: String) -> Result<(), unpak::Error> { pub fn unpack(path: String, key: Option<String>) -> Result<(), unpak::Error> {
let folder = std::path::Path::new( let folder = std::path::Path::new(
std::path::Path::new(&path) std::path::Path::new(&path)
.file_stem() .file_stem()

View file

@ -1,4 +1,4 @@
pub fn version(path: String, key: String) -> Result<(), unpak::Error> { pub fn version(path: String, key: Option<String>) -> Result<(), unpak::Error> {
println!("{}", super::load_pak(path, key)?.version()); println!("{}", super::load_pak(path, key)?.version());
Ok(()) Ok(())
} }

View file

@ -8,9 +8,9 @@ fn main() {
}; };
// can't map key to &[u8] because refers to owned data // can't map key to &[u8] because refers to owned data
if let Err(e) = match args.next().unwrap_or_default().as_str() { if let Err(e) = match args.next().unwrap_or_default().as_str() {
"version" => subcommands::version(path, args.next().unwrap_or_default()), "version" => subcommands::version(path, args.next()),
"list" => subcommands::list(path, args.next().unwrap_or_default()), "list" => subcommands::list(path, args.next()),
"unpack" | "" => subcommands::unpack(path, args.next().unwrap_or_default()), "unpack" | "" => subcommands::unpack(path, args.next()),
"help" | _ => help(), "help" | _ => help(),
} { } {
eprintln!("{e}") eprintln!("{e}")

View file

@ -5,6 +5,8 @@ pub enum Error {
Strum(#[from] strum::ParseError), Strum(#[from] strum::ParseError),
#[error("key hash is an incorrect length")] #[error("key hash is an incorrect length")]
Aes, Aes,
#[error("malformed base64")]
Base64,
// std errors // std errors
#[error("io error: {0}")] #[error("io error: {0}")]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),

View file

@ -14,7 +14,7 @@ impl<R: io::Read + io::Seek> Pak<R> {
pub fn new( pub fn new(
mut reader: R, mut reader: R,
version: super::Version, version: super::Version,
key_hash: Option<&[u8]>, key: Option<aes::Aes256Dec>,
) -> Result<Self, super::Error> { ) -> Result<Self, super::Error> {
use super::ext::ReadExt; use super::ext::ReadExt;
use byteorder::{ReadBytesExt, LE}; use byteorder::{ReadBytesExt, LE};
@ -24,20 +24,15 @@ impl<R: io::Read + io::Seek> Pak<R> {
// read index to get all the entry info // read index to get all the entry info
reader.seek(io::SeekFrom::Start(footer.index_offset))?; reader.seek(io::SeekFrom::Start(footer.index_offset))?;
let mut index = reader.read_len(footer.index_size as usize)?; let mut index = reader.read_len(footer.index_size as usize)?;
let mut key = None;
// decrypt index if needed // decrypt index if needed
if footer.encrypted { if footer.encrypted {
let Some(hash) = key_hash else { let Some(key) = &key else {
return Err(super::Error::Encrypted); return Err(super::Error::Encrypted);
}; };
use aes::cipher::{BlockDecrypt, KeyInit}; use aes::cipher::BlockDecrypt;
let Ok(decrypter)= aes::Aes256Dec::new_from_slice(hash) else {
return Err(super::Error::Aes)
};
for chunk in index.chunks_mut(16) { for chunk in index.chunks_mut(16) {
decrypter.decrypt_block(aes::Block::from_mut_slice(chunk)) key.decrypt_block(aes::Block::from_mut_slice(chunk))
} }
key = Some(decrypter);
} }
let mut index = io::Cursor::new(index); let mut index = io::Cursor::new(index);
let mount_point = index.read_string()?; let mount_point = index.read_string()?;