mirror of
https://github.com/xavo95/repak.git
synced 2025-01-18 10:54:38 +00:00
Improve AES key handling and accept both hex string and base64 formats
This commit is contained in:
parent
0853cf7875
commit
688f0aa46d
5 changed files with 56 additions and 70 deletions
|
@ -6,12 +6,9 @@ pub enum Error {
|
|||
#[error("enum conversion: {0}")]
|
||||
Strum(#[from] strum::ParseError),
|
||||
|
||||
#[error("key hash is an incorrect length")]
|
||||
#[error("expect 256 bit AES key as base64 or hex string")]
|
||||
Aes,
|
||||
|
||||
#[error("malformed base64")]
|
||||
Base64,
|
||||
|
||||
// std errors
|
||||
#[error("io error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
@ -68,8 +65,8 @@ pub enum Error {
|
|||
#[error("error with OsString")]
|
||||
OsString(std::ffi::OsString),
|
||||
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
#[error("version unsupported or is encrypted (possibly missing --aes-key?)")]
|
||||
UnsuportedOrEncrypted,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Error {
|
||||
|
|
|
@ -91,7 +91,7 @@ impl PakReader {
|
|||
_ => continue,
|
||||
}
|
||||
}
|
||||
Err(super::Error::Other("version unsupported".to_owned()))
|
||||
Err(super::Error::UnsuportedOrEncrypted)
|
||||
}
|
||||
|
||||
pub fn version(&self) -> super::Version {
|
||||
|
|
|
@ -88,7 +88,7 @@ fn test_read(version: repak::Version, _file_name: &str, bytes: &[u8]) {
|
|||
let key = general_purpose::STANDARD
|
||||
.decode(AES_KEY)
|
||||
.as_ref()
|
||||
.map_err(|_| repak::Error::Base64)
|
||||
.map_err(|_| repak::Error::Aes)
|
||||
.and_then(|bytes| aes::Aes256::new_from_slice(bytes).map_err(|_| repak::Error::Aes))
|
||||
.unwrap();
|
||||
|
||||
|
@ -154,7 +154,7 @@ fn test_write(_version: repak::Version, _file_name: &str, bytes: &[u8]) {
|
|||
let key = general_purpose::STANDARD
|
||||
.decode(AES_KEY)
|
||||
.as_ref()
|
||||
.map_err(|_| repak::Error::Base64)
|
||||
.map_err(|_| repak::Error::Aes)
|
||||
.and_then(|bytes| aes::Aes256::new_from_slice(bytes).map_err(|_| repak::Error::Aes))
|
||||
.unwrap();
|
||||
|
||||
|
@ -186,7 +186,7 @@ fn test_rewrite_index(_version: repak::Version, _file_name: &str, bytes: &[u8])
|
|||
let key = general_purpose::STANDARD
|
||||
.decode(AES_KEY)
|
||||
.as_ref()
|
||||
.map_err(|_| repak::Error::Base64)
|
||||
.map_err(|_| repak::Error::Aes)
|
||||
.and_then(|bytes| aes::Aes256::new_from_slice(bytes).map_err(|_| repak::Error::Aes))
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ path = "src/main.rs"
|
|||
aes = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
clap = { version = "4.1.4", features = ["derive"] }
|
||||
hex = "0.4.3"
|
||||
indicatif = { version = "0.17.3", features = ["rayon"] }
|
||||
path-clean = "0.1.0"
|
||||
path-slash = "0.2.1"
|
||||
|
|
|
@ -14,10 +14,6 @@ struct ActionInfo {
|
|||
/// Input .pak path
|
||||
#[arg(index = 1)]
|
||||
input: String,
|
||||
|
||||
/// Base64 encoded AES encryption key if the pak is encrypted
|
||||
#[arg(short, long)]
|
||||
aes_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -25,10 +21,6 @@ struct ActionList {
|
|||
/// Input .pak path
|
||||
#[arg(index = 1)]
|
||||
input: String,
|
||||
|
||||
/// Base64 encoded AES encryption key if the pak is encrypted
|
||||
#[arg(short, long)]
|
||||
aes_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -45,10 +37,6 @@ struct ActionUnpack {
|
|||
#[arg(short, long, default_value = "../../../")]
|
||||
strip_prefix: String,
|
||||
|
||||
/// Base64 encoded AES encryption key if the pak is encrypted
|
||||
#[arg(short, long)]
|
||||
aes_key: Option<String>,
|
||||
|
||||
/// Verbose
|
||||
#[arg(short, long, default_value = "false")]
|
||||
verbose: bool,
|
||||
|
@ -106,10 +94,6 @@ struct ActionGet {
|
|||
/// Prefix to strip from entry path
|
||||
#[arg(short, long, default_value = "../../../")]
|
||||
strip_prefix: String,
|
||||
|
||||
/// Base64 encoded AES encryption key if the pak is encrypted
|
||||
#[arg(short, long)]
|
||||
aes_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
|
@ -129,37 +113,50 @@ enum Action {
|
|||
#[derive(Parser, Debug)]
|
||||
#[command(author, version)]
|
||||
struct Args {
|
||||
/// 256 bit AES encryption key as base64 or hex string if the pak is encrypted
|
||||
#[arg(short, long)]
|
||||
aes_key: Option<AesKey>,
|
||||
|
||||
#[command(subcommand)]
|
||||
action: Action,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), repak::Error> {
|
||||
let args = Args::parse();
|
||||
|
||||
match args.action {
|
||||
Action::Info(args) => info(args),
|
||||
Action::List(args) => list(args),
|
||||
Action::Unpack(args) => unpack(args),
|
||||
Action::Pack(args) => pack(args),
|
||||
Action::Get(args) => get(args),
|
||||
#[derive(Debug, Clone)]
|
||||
struct AesKey(aes::Aes256);
|
||||
impl std::str::FromStr for AesKey {
|
||||
type Err = repak::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
use aes::cipher::KeyInit;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
let try_parse = |bytes: Vec<_>| aes::Aes256::new_from_slice(&bytes).ok().map(AesKey);
|
||||
hex::decode(s.strip_prefix("0x").unwrap_or(s))
|
||||
.ok()
|
||||
.and_then(try_parse)
|
||||
.or_else(|| {
|
||||
general_purpose::STANDARD_NO_PAD
|
||||
.decode(s.trim_end_matches('='))
|
||||
.ok()
|
||||
.and_then(try_parse)
|
||||
})
|
||||
.ok_or(repak::Error::Aes)
|
||||
}
|
||||
}
|
||||
|
||||
fn aes_key(key: &str) -> Result<aes::Aes256, repak::Error> {
|
||||
use aes::cipher::KeyInit;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
general_purpose::STANDARD
|
||||
.decode(key)
|
||||
.as_ref()
|
||||
.map_err(|_| repak::Error::Base64)
|
||||
.and_then(|bytes| aes::Aes256::new_from_slice(bytes).map_err(|_| repak::Error::Aes))
|
||||
fn main() -> Result<(), repak::Error> {
|
||||
let args = Args::parse();
|
||||
let aes_key = args.aes_key.map(|k| k.0);
|
||||
|
||||
match args.action {
|
||||
Action::Info(action) => info(aes_key, action),
|
||||
Action::List(action) => list(aes_key, action),
|
||||
Action::Unpack(action) => unpack(aes_key, action),
|
||||
Action::Pack(action) => pack(action),
|
||||
Action::Get(action) => get(aes_key, action),
|
||||
}
|
||||
}
|
||||
|
||||
fn info(args: ActionInfo) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(
|
||||
BufReader::new(File::open(&args.input)?),
|
||||
args.aes_key.map(|k| aes_key(k.as_str())).transpose()?,
|
||||
)?;
|
||||
fn info(aes_key: Option<aes::Aes256>, action: ActionInfo) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(BufReader::new(File::open(action.input)?), aes_key)?;
|
||||
println!("mount point: {}", pak.mount_point());
|
||||
println!("version: {}", pak.version());
|
||||
println!("version major: {}", pak.version().version_major());
|
||||
|
@ -167,11 +164,8 @@ fn info(args: ActionInfo) -> Result<(), repak::Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn list(args: ActionInfo) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(
|
||||
BufReader::new(File::open(&args.input)?),
|
||||
args.aes_key.map(|k| aes_key(k.as_str())).transpose()?,
|
||||
)?;
|
||||
fn list(aes_key: Option<aes::Aes256>, action: ActionInfo) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(BufReader::new(File::open(action.input)?), aes_key)?;
|
||||
for f in pak.files() {
|
||||
println!("{f}");
|
||||
}
|
||||
|
@ -180,29 +174,26 @@ fn list(args: ActionInfo) -> Result<(), repak::Error> {
|
|||
|
||||
const STYLE: &str = "[{elapsed_precise}] [{wide_bar}] {pos}/{len} ({eta})";
|
||||
|
||||
fn unpack(args: ActionUnpack) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(
|
||||
BufReader::new(File::open(&args.input)?),
|
||||
args.aes_key.map(|k| aes_key(k.as_str())).transpose()?,
|
||||
)?;
|
||||
let output = args
|
||||
fn unpack(aes_key: Option<aes::Aes256>, action: ActionUnpack) -> Result<(), repak::Error> {
|
||||
let pak = repak::PakReader::new_any(BufReader::new(File::open(&action.input)?), aes_key)?;
|
||||
let output = action
|
||||
.output
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| Path::new(&args.input).with_extension(""));
|
||||
.unwrap_or_else(|| Path::new(&action.input).with_extension(""));
|
||||
match fs::create_dir(&output) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
if !args.force && output.read_dir()?.next().is_some() {
|
||||
if !action.force && output.read_dir()?.next().is_some() {
|
||||
return Err(repak::Error::OutputNotEmpty(
|
||||
output.to_string_lossy().to_string(),
|
||||
));
|
||||
}
|
||||
let mount_point = PathBuf::from(pak.mount_point());
|
||||
let prefix = Path::new(&args.strip_prefix);
|
||||
let prefix = Path::new(&action.strip_prefix);
|
||||
|
||||
let includes = args
|
||||
let includes = action
|
||||
.include
|
||||
.iter()
|
||||
.map(|i| prefix.join(Path::new(i)))
|
||||
|
@ -255,9 +246,9 @@ fn unpack(args: ActionUnpack) -> Result<(), repak::Error> {
|
|||
.progress_with_style(indicatif::ProgressStyle::with_template(STYLE).unwrap());
|
||||
let progress = iter.progress.clone();
|
||||
iter.try_for_each_init(
|
||||
|| (progress.clone(), File::open(&args.input)),
|
||||
|| (progress.clone(), File::open(&action.input)),
|
||||
|(progress, file), entry| -> Result<(), repak::Error> {
|
||||
if args.verbose {
|
||||
if action.verbose {
|
||||
progress.println(format!("unpacking {}", entry.entry_path));
|
||||
}
|
||||
fs::create_dir_all(&entry.out_dir)?;
|
||||
|
@ -335,12 +326,9 @@ fn pack(args: ActionPack) -> Result<(), repak::Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get(args: ActionGet) -> Result<(), repak::Error> {
|
||||
fn get(aes_key: Option<aes::Aes256>, args: ActionGet) -> Result<(), repak::Error> {
|
||||
let mut reader = BufReader::new(File::open(&args.input)?);
|
||||
let pak = repak::PakReader::new_any(
|
||||
&mut reader,
|
||||
args.aes_key.map(|k| aes_key(k.as_str())).transpose()?,
|
||||
)?;
|
||||
let pak = repak::PakReader::new_any(&mut reader, aes_key)?;
|
||||
let mount_point = PathBuf::from(pak.mount_point());
|
||||
let prefix = Path::new(&args.strip_prefix);
|
||||
|
||||
|
|
Loading…
Reference in a new issue