CI / Format and clippy (push) Successful in 1m13s
CI / Dependency audit (push) Successful in 13s
CI / Tests (Linux) (push) Successful in 1m35s
Release / Create release (push) Successful in 7s
CI / Check desktop app (Linux) (push) Successful in 3m19s
Release / CLI (linux-aarch64) (push) Successful in 1m3s
Release / CLI (linux-x86_64) (push) Successful in 1m28s
Release / Desktop app (linux-aarch64) (push) Successful in 7m16s
Release / Desktop app (linux-x86_64) (push) Successful in 10m17s
Release / Desktop app (windows) (push) Successful in 4m54s
Completes the English-only standardization for code, comments, scripts and CI templates (UI/CLI-facing strings stay bilingual pt-BR/en, untouched). Covers the remaining crates/, apps/gui/ui/ and apps/gui/src-tauri/ comments, .gitea/ workflow and template comments, .githooks/ hooks, and the scripts/ source comments for the files already renamed in a prior commit (check.sh, generate-logo.py, install-hooks.sh). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
87 lines
2.4 KiB
Rust
87 lines
2.4 KiB
Rust
//! Exports captured WPA/WPA2 material in hashcat `.hc22000` format (`-m 22000`).
|
|
//!
|
|
//! Emits both record types supported by `hcxtools` / `hashcat`:
|
|
//!
|
|
//! * `WPA*01*PMKID*AP*STA*SSID***`
|
|
//! * `WPA*02*MIC*AP*STA*SSID*ANONCE*EAPOL*MESSAGEPAIR`
|
|
//!
|
|
//! The exported EAPOL field contains the full message 2 frame (802.1X header +
|
|
//! key descriptor) expected by hashcat's `EAPOL_CLIENT` field.
|
|
|
|
use crate::eapol::MIC_OFFSET;
|
|
use crate::handshake::Handshake;
|
|
|
|
fn mac_hex(mac: &str) -> Option<String> {
|
|
let bytes: Vec<String> = mac
|
|
.split(':')
|
|
.map(|part| part.trim().to_ascii_lowercase())
|
|
.collect();
|
|
if bytes.len() != 6
|
|
|| !bytes
|
|
.iter()
|
|
.all(|b| b.len() == 2 && b.chars().all(|c| c.is_ascii_hexdigit()))
|
|
{
|
|
return None;
|
|
}
|
|
Some(bytes.concat())
|
|
}
|
|
|
|
fn ssid_hex(ssid: &str) -> String {
|
|
ssid.as_bytes().iter().map(|b| format!("{b:02x}")).collect()
|
|
}
|
|
|
|
fn hex(bytes: &[u8]) -> String {
|
|
bytes.iter().map(|b| format!("{b:02x}")).collect()
|
|
}
|
|
|
|
pub fn hashcat_line(hs: &Handshake) -> Option<String> {
|
|
let ssid = hs.ssid.as_deref().filter(|s| !s.is_empty())?;
|
|
let ap = mac_hex(&hs.bssid)?;
|
|
let sta = mac_hex(&hs.station)?;
|
|
let ssid = ssid_hex(ssid);
|
|
|
|
if let Some(pmkid) = hs.pmkid {
|
|
return Some(format!("WPA*01*{}*{ap}*{sta}*{ssid}***", hex(&pmkid)));
|
|
}
|
|
|
|
let eapol = hs.eapol_msg2.as_deref()?;
|
|
let anonce = hs.anonce?;
|
|
let mic = eapol.get(MIC_OFFSET..MIC_OFFSET + 16)?;
|
|
Some(format!(
|
|
"WPA*02*{}*{ap}*{sta}*{ssid}*{}*{}*00",
|
|
hex(mic),
|
|
hex(&anonce),
|
|
hex(eapol),
|
|
))
|
|
}
|
|
|
|
/// Formats deduplicated `.hc22000` lines for all crackable handshakes.
|
|
pub fn export<'a>(handshakes: impl IntoIterator<Item = &'a Handshake>) -> Vec<String> {
|
|
let mut lines: Vec<String> = handshakes.into_iter().filter_map(hashcat_line).collect();
|
|
lines.sort();
|
|
lines.dedup();
|
|
lines
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::handshake::Handshake;
|
|
|
|
#[test]
|
|
fn exports_pmkid_line() {
|
|
let hs = Handshake {
|
|
bssid: "00:11:22:33:44:55".into(),
|
|
station: "66:77:88:99:aa:bb".into(),
|
|
ssid: Some("Casa".into()),
|
|
pmkid: Some([0xcd; 16]),
|
|
..Default::default()
|
|
};
|
|
let line = hashcat_line(&hs).expect("linha");
|
|
assert_eq!(
|
|
line,
|
|
"WPA*01*cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd*001122334455*66778899aabb*43617361***"
|
|
);
|
|
}
|
|
}
|