Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 58 additions & 64 deletions src/cmd/crack.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use colored::Colorize;
use indicatif::{HumanDuration, MultiProgress, ProgressBar, ProgressStyle};
use colored::{Color, Colorize};
use indicatif::{HumanDuration, MultiProgress, ProgressBar};
use log::{error, info};
use rayon::prelude::*;
use serde::Serialize;
Expand All @@ -14,6 +14,7 @@ use zeroize::Zeroize;

use crate::crack;
use crate::jwt;
use crate::printing::theme;
use crate::utils;

/// Maps preset names to their corresponding character sets
Expand Down Expand Up @@ -273,13 +274,7 @@ fn create_crack_progress_bar(
if verbose {
return None;
}
let progress = multi.add(ProgressBar::new(total));
progress.set_style(
ProgressStyle::default_bar()
.template("{spinner:.red} [{elapsed_precise}] Cracking.. [{bar:40.cyan/blue}] {pos}/{len} ({percent}%) {msg}")
.expect("valid progress bar template")
.progress_chars("#>-")
);
let progress = multi.add(theme::progress_bar(total, "Cracking"));
Some(progress)
}

Expand Down Expand Up @@ -457,29 +452,39 @@ fn report_crack_results(
};
let secret_label = if is_jwe { "Key" } else { "Secret" };

eprintln!("\n {} {}", "✓".green(), label.bold());
eprintln!(
"\n{}",
theme::status_line(theme::G_OK, Color::Green, label.bold())
);
println!();
println!(" {:<14}{}", secret_label.bold(), secret.bold());
println!("{}", theme::kv(secret_label, secret.bold()));
println!(
" {:<14}{} ({:.2} keys/sec)",
"Time".bold(),
HumanDuration(elapsed),
rate
"{}",
theme::kv(
"Time",
format!("{} ({:.2} keys/sec)", HumanDuration(elapsed), rate)
)
);
println!(" {:<14}{}", "Token".bold(), utils::format_jwt_token(token));
println!("{}", theme::kv("Token", utils::format_jwt_token(token)));
} else {
let label = if is_jwe {
"Key not found"
} else {
"Secret not found"
};
eprintln!(
"\n {} {} ({} keys in {}, {:.2} keys/sec)",
"✗".red(),
label.bold(),
attempts_total,
HumanDuration(elapsed),
rate
"\n{}",
theme::status_line(
theme::G_ERR,
Color::Red,
format!(
"{} ({} keys in {}, {:.2} keys/sec)",
label.bold(),
attempts_total,
HumanDuration(elapsed),
rate
)
)
);
}
}
Expand Down Expand Up @@ -542,20 +547,9 @@ fn crack_dictionary(
None
};
// Show a spinner while loading/processing batches
let loading_pb = if let Some(ref multi) = multi {
let pb = multi.add(ProgressBar::new_spinner());
pb.set_style(
ProgressStyle::default_spinner()
.template("{spinner:.blue} {msg}")
.expect("valid spinner template")
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
);
pb.set_message("Processing wordlist...");
pb.enable_steady_tick(Duration::from_millis(100));
Some(pb)
} else {
None
};
let loading_pb = multi
.as_ref()
.map(|multi| multi.add(theme::spinner("Processing wordlist...")));

let found = Arc::new(Mutex::new(None::<String>));
let found_flag = Arc::new(AtomicBool::new(false));
Expand Down Expand Up @@ -1017,19 +1011,9 @@ fn crack_target_field(
let mut bytes_read: u64 = 0;

let loading_pb = if emit_output {
let pb = multi.as_ref().map(|m| {
let pb = m.add(ProgressBar::new_spinner());
pb.set_style(
ProgressStyle::default_spinner()
.template("{spinner:.blue} {msg}")
.expect("valid spinner template")
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
);
pb.set_message("Processing wordlist (targeted field)...");
pb.enable_steady_tick(Duration::from_millis(100));
pb
});
pb
multi
.as_ref()
.map(|m| m.add(theme::spinner("Processing wordlist (targeted field)...")))
} else {
None
};
Expand Down Expand Up @@ -1116,27 +1100,37 @@ fn crack_target_field(
if emit_output {
if let Some(value) = found.lock().unwrap_or_else(|e| e.into_inner()).clone() {
eprintln!(
"\n {} {}",
"✓".green(),
"Matching field value found".bold()
"\n{}",
theme::status_line(
theme::G_OK,
Color::Green,
"Matching field value found".bold()
)
);
println!();
println!(" {:<14}{}", "Field".bold(), target_field.bold());
println!(" {:<14}{}", "Value".bold(), value.bold());
println!("{}", theme::kv("Field", target_field.bold()));
println!("{}", theme::kv("Value", value.bold()));
println!(
" {:<14}{} ({:.2} attempts/sec)",
"Time".bold(),
HumanDuration(elapsed),
rate
"{}",
theme::kv(
"Time",
format!("{} ({:.2} attempts/sec)", HumanDuration(elapsed), rate)
)
);
} else {
eprintln!(
"\n {} {} ({} attempts in {}, {:.2} attempts/sec)",
"✗".red(),
"No matching field value found".bold(),
attempts_total,
HumanDuration(elapsed),
rate
"\n{}",
theme::status_line(
theme::G_ERR,
Color::Red,
format!(
"{} ({} attempts in {}, {:.2} attempts/sec)",
"No matching field value found".bold(),
attempts_total,
HumanDuration(elapsed),
rate
)
)
);
}
}
Expand Down
93 changes: 56 additions & 37 deletions src/cmd/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use colored::Colorize;
use serde_json::Value;

use crate::jwt;
use crate::printing::theme;
use crate::utils;

/// Helper function to format a Unix timestamp to a human-readable string.
Expand Down Expand Up @@ -112,20 +113,22 @@ fn decode_jwt_token(token: &str) -> Result<()> {
.and_then(|v| v.as_str())
.unwrap_or("JWT");

println!(" {:<14}{}", "Algorithm".bold(), alg_str.cyan());
println!(" {:<14}{}", "Type".bold(), typ);
println!("{}", theme::section_line("Decode"));
println!();
println!("{}", theme::kv("Algorithm", alg_str.cyan()));
println!("{}", theme::kv("Type", typ));

println!("\n {}", "Header".bold());
println!("\n{}", theme::subsection_line("Header"));
let header_json = serde_json::to_string_pretty(&decoded.header)?;
println!(" {}", header_json.replace('\n', "\n "));
println!("{}{}", theme::INDENT, header_json.replace('\n', "\n "));

let mut claims_map: Value = decoded.claims.clone();
process_issued_at_claim(&decoded.claims, &mut claims_map);
process_expiration_claim(&decoded.claims, &mut claims_map);

println!("\n {}", "Payload".bold());
println!("\n{}", theme::subsection_line("Payload"));
let payload_json = serde_json::to_string_pretty(&claims_map)?;
println!(" {}", payload_json.replace('\n', "\n "));
println!("{}{}", theme::INDENT, payload_json.replace('\n', "\n "));

Ok(())
}
Expand Down Expand Up @@ -157,57 +160,73 @@ fn decode_jwt_token_json(token: &str) -> Result<Value> {
fn decode_jwe_token(token: &str) -> Result<()> {
let decoded = jwt::decode_jwe(token)?;

println!(" {:<14}{}", "Key Mgmt".bold(), decoded.algorithm.cyan());
println!(" {:<14}{}", "Encryption".bold(), decoded.encryption.cyan());
println!("{}", theme::section_line("Decode · JWE"));
println!();
println!("{}", theme::kv("Key Mgmt", decoded.algorithm.cyan()));
println!("{}", theme::kv("Encryption", decoded.encryption.cyan()));

println!("\n {}", "Header".bold());
println!("\n{}", theme::subsection_line("Header"));
let header_json = serde_json::to_string_pretty(&decoded.header)?;
println!(" {}", header_json.replace('\n', "\n "));
println!("{}{}", theme::INDENT, header_json.replace('\n', "\n "));

println!("\n{}", theme::subsection_line("Components"));
println!(
"\n {:<18}{}",
"Encrypted Key".bold(),
if decoded.encrypted_key.is_empty() {
"(empty)".dimmed().to_string()
} else {
utils::format_base64_preview(&decoded.encrypted_key)
}
"{}",
theme::kv_line(
"Encrypted Key",
if decoded.encrypted_key.is_empty() {
"(empty)".dimmed().to_string()
} else {
utils::format_base64_preview(&decoded.encrypted_key)
},
18
)
);
println!(
" {:<18}{}",
"IV".bold(),
if decoded.iv.is_empty() {
"(empty)".dimmed().to_string()
} else {
utils::format_base64_preview(&decoded.iv)
}
"{}",
theme::kv_line(
"IV",
if decoded.iv.is_empty() {
"(empty)".dimmed().to_string()
} else {
utils::format_base64_preview(&decoded.iv)
},
18
)
);
println!(
" {:<18}{}",
"Ciphertext".bold(),
utils::format_base64_preview(&decoded.ciphertext)
"{}",
theme::kv_line(
"Ciphertext",
utils::format_base64_preview(&decoded.ciphertext),
18
)
);
println!(
" {:<18}{}",
"Auth Tag".bold(),
if decoded.tag.is_empty() {
"(empty)".dimmed().to_string()
} else {
utils::format_base64_preview(&decoded.tag)
}
"{}",
theme::kv_line(
"Auth Tag",
if decoded.tag.is_empty() {
"(empty)".dimmed().to_string()
} else {
utils::format_base64_preview(&decoded.tag)
},
18
)
);

eprintln!(
"\n {}",
"\n{}{}",
theme::INDENT,
"JWE payload is encrypted and cannot be decoded without the appropriate key".dimmed()
);

// Check for misconfigurations
let misconfigs = jwt::detect_jwe_misconfigurations(&decoded);
if !misconfigs.is_empty() {
eprintln!("\n {}", "Security Issues Detected:".yellow().bold());
eprintln!("\n{}", theme::subsection_line("Security Issues"));
for issue in misconfigs {
eprintln!(" {}", issue);
eprintln!("{}{}", theme::INDENT, issue.yellow());
}
}

Expand Down
25 changes: 15 additions & 10 deletions src/cmd/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fs;
use std::path::PathBuf;

use crate::jwt;
use crate::printing::theme;
use crate::utils;

/// Options for encoding operations
Expand Down Expand Up @@ -155,18 +156,20 @@ fn display_encoding_result(
key_info: &str,
headers: &[(String, String)],
) {
println!(" {:<14}{}", "Algorithm".bold(), algorithm.cyan());
println!(" {:<14}{}", "Key".bold(), key_info);
println!("{}", theme::section_line("Encode"));
println!();
println!("{}", theme::kv("Algorithm", algorithm.cyan()));
println!("{}", theme::kv("Key", key_info));

if !headers.is_empty() {
println!("\n {}", "Headers".bold());
println!("\n{}", theme::subsection_line("Headers"));
for (key, value) in headers {
println!(" {:<14}{}", key.to_string().dimmed(), value);
println!("{}", theme::kv(key, value));
}
}

println!("\n {}", "Token".bold());
println!(" {}", utils::format_jwt_token(token));
println!("\n{}", theme::subsection_line("Token"));
println!("{}{}", theme::INDENT, utils::format_jwt_token(token));
}

fn encode_json(
Expand Down Expand Up @@ -250,11 +253,13 @@ fn encode_jwe(json_str: &str, secret: Option<&str>) -> Result<()> {
let key = secret.unwrap_or("default_jwe_key");
let token = jwt::encode_jwe_demo(json_str, key)?;

println!(" {:<14}{}", "Key Mgmt".bold(), "dir".cyan());
println!(" {:<14}{}", "Encryption".bold(), "A256GCM".cyan());
println!("{}", theme::section_line("Encode · JWE"));
println!();
println!("{}", theme::kv("Key Mgmt", "dir".cyan()));
println!("{}", theme::kv("Encryption", "A256GCM".cyan()));

println!("\n {}", "Token".bold());
println!(" {}", token);
println!("\n{}", theme::subsection_line("Token"));
println!("{}{}", theme::INDENT, token);

Ok(())
}
Expand Down
Loading
Loading