343 lines
17 KiB
Rust
343 lines
17 KiB
Rust
use crate::mac::NetworkAddress;
|
|
use std::collections::HashMap;
|
|
use std::io::{self, Write};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum AlgoMode {
|
|
Mac,
|
|
Empty,
|
|
Static,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct AlgoInfo {
|
|
pub id: &'static str,
|
|
pub name: &'static str,
|
|
pub mode: AlgoMode,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, Clone)]
|
|
pub struct PinItem {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub pin: String,
|
|
}
|
|
|
|
pub struct WPSpin {
|
|
algos: HashMap<&'static str, AlgoInfo>,
|
|
suggestions: HashMap<&'static str, Vec<&'static str>>,
|
|
}
|
|
|
|
impl WPSpin {
|
|
pub fn new() -> Self {
|
|
let mut algos = HashMap::new();
|
|
let list = vec![
|
|
AlgoInfo { id: "pin24", name: "24-bit PIN", mode: AlgoMode::Mac },
|
|
AlgoInfo { id: "pin28", name: "28-bit PIN", mode: AlgoMode::Mac },
|
|
AlgoInfo { id: "pin32", name: "32-bit PIN", mode: AlgoMode::Mac },
|
|
AlgoInfo { id: "pinDLink", name: "D-Link PIN", mode: AlgoMode::Mac },
|
|
AlgoInfo { id: "pinDLink1", name: "D-Link PIN +1", mode: AlgoMode::Mac },
|
|
AlgoInfo { id: "pinASUS", name: "ASUS PIN", mode: AlgoMode::Mac },
|
|
AlgoInfo { id: "pinAirocon", name: "Airocon Realtek", mode: AlgoMode::Mac },
|
|
// Static / empty pin algos
|
|
AlgoInfo { id: "pinEmpty", name: "Empty PIN", mode: AlgoMode::Empty },
|
|
AlgoInfo { id: "pinCisco", name: "Cisco", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinBrcm1", name: "Broadcom 1", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinBrcm2", name: "Broadcom 2", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinBrcm3", name: "Broadcom 3", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinBrcm4", name: "Broadcom 4", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinBrcm5", name: "Broadcom 5", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinBrcm6", name: "Broadcom 6", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinAirc1", name: "Airocon 1", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinAirc2", name: "Airocon 2", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinDSL2740R", name: "DSL-2740R", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinRealtek1", name: "Realtek 1", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinRealtek2", name: "Realtek 2", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinRealtek3", name: "Realtek 3", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinUpvel", name: "Upvel", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinUR814AC", name: "UR-814AC", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinUR825AC", name: "UR-825AC", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinOnlime", name: "Onlime", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinEdimax", name: "Edimax", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinThomson", name: "Thomson", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinHG532x", name: "HG532x", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinH108L", name: "H108L", mode: AlgoMode::Static },
|
|
AlgoInfo { id: "pinONO", name: "CBN ONO", mode: AlgoMode::Static },
|
|
];
|
|
for item in list {
|
|
algos.insert(item.id, item);
|
|
}
|
|
|
|
let mut suggestions: HashMap<&'static str, Vec<&'static str>> = HashMap::new();
|
|
suggestions.insert("pin24", vec![
|
|
"04BF6D", "0E5D4E", "107BEF", "14A9E3", "28285D", "2A285D", "32B2DC", "381766", "404A03", "4E5D4E",
|
|
"5067F0", "5CF4AB", "6A285D", "8E5D4E", "AA285D", "B0B2DC", "C86C87", "CC5D4E", "CE5D4E", "EA285D",
|
|
"E243F6", "EC43F6", "EE43F6", "F2B2DC", "FCF528", "FEF528", "4C9EFF", "0014D1", "D8EB97", "1C7EE5",
|
|
"84C9B2", "FC7516", "14D64D", "9094E4", "BCF685", "C4A81D", "00664B", "087A4C", "14B968", "2008ED",
|
|
"346BD3", "4CEDDE", "786A89", "88E3AB", "D46E5C", "E8CD2D", "EC233D", "ECCB30", "F49FF3", "20CF30",
|
|
"90E6BA", "E0CB4E", "D4BF7F4", "F8C091", "001CDF", "002275", "08863B", "00B00C", "081075", "C83A35",
|
|
"0022F7", "001F1F", "00265B", "68B6CF", "788DF7", "BC1401", "202BC1", "308730", "5C4CA9", "62233D",
|
|
"623CE4", "623DFF", "6253D4", "62559C", "626BD3", "627D5E", "6296BF", "62A8E4", "62B686", "62C06F",
|
|
"62C61F", "62C714", "62CBA8", "62CDBE", "62E87B", "6416F0", "6A1D67", "6A233D", "6A3DFF", "6A53D4",
|
|
"6A559C", "6A6BD3", "6A96BF", "6A7D5E", "6AA8E4", "6AC06F", "6AC61F", "6AC714", "6ACBA8", "6ACDBE",
|
|
"6AD15E", "6AD167", "721D67", "72233D", "723CE4", "723DFF", "7253D4", "72559C", "726BD3", "727D5E",
|
|
"7296BF", "72A8E4", "72C06F", "72C61F", "72C714", "72CBA8", "72CDBE", "72D15E", "72E87B", "0026CE",
|
|
"9897D1", "E04136", "B246FC", "E24136", "00E020", "5CA39D", "D86CE9", "DC7144", "801F02", "E47CF9",
|
|
"000CF6", "00A026", "A0F3C1", "647002", "B0487A", "F81A67", "F8D111", "34BA9A", "B4944E",
|
|
]);
|
|
suggestions.insert("pin28", vec!["200BC7", "4846FB", "D46AA8", "F84ABF"]);
|
|
suggestions.insert("pin32", vec![
|
|
"000726", "D8FEE3", "FC8B97", "1062EB", "1C5F2B", "48EE0C", "802689", "908D78", "E8CC18", "2CAB25",
|
|
"10BF48", "14DAE9", "3085A9", "50465D", "5404A6", "C86000", "F46D04", "3085A9", "801F02",
|
|
]);
|
|
suggestions.insert("pinDLink", vec![
|
|
"14D64D", "1C7EE5", "28107B", "84C9B2", "A0AB1B", "B8A386", "C0A0BB", "CCB255", "FC7516", "0014D1", "D8EB97",
|
|
]);
|
|
suggestions.insert("pinDLink1", vec![
|
|
"0018E7", "00195B", "001CF0", "001E58", "002191", "0022B0", "002401", "00265A", "14D64D", "1C7EE5",
|
|
"340804", "5CD998", "84C9B2", "B8A386", "C8BE19", "C8D3A3", "CCB255", "0014D1",
|
|
]);
|
|
suggestions.insert("pinASUS", vec![
|
|
"049226", "04D9F5", "08606E", "0862669", "107B44", "10BF48", "10C37B", "14DDA9", "1C872C", "1CB72C",
|
|
"2C56DC", "2CFDA1", "305A3A", "382C4A", "38D547", "40167E", "50465D", "54A050", "6045CB", "60A44C",
|
|
"704D7B", "74D02B", "7824AF", "88D7F6", "9C5C8E", "AC220B", "AC9E17", "B06EBF", "BCEE7B", "C860007",
|
|
"D017C2", "D850E6", "E03F49", "F0795978", "F832E4", "00072624", "0008A1D3", "00177C", "001EA6", "00304FB",
|
|
"00E04C0", "048D38", "081077", "081078", "081079", "083E5D", "10FEED3C", "181E78", "1C4419", "2420C7",
|
|
"247F20", "2CAB25", "3085A98C", "3C1E04", "40F201", "44E9DD", "48EE0C", "5464D9", "54B80A", "587BE906",
|
|
"60D1AA21", "64517E", "64D954", "6C198F", "6C7220", "6CFDB9", "78D99FD", "7C2664", "803F5DF6", "84A423",
|
|
"88A6C6", "8C10D4", "8C882B00", "904D4A", "907282", "90F65290", "94FBB2", "A01B29", "A0F3C1E", "A8F7E00",
|
|
"ACA213", "B85510", "B8EE0E", "BC3400", "BC9680", "C891F9", "D00ED90", "D084B0", "D8FEE3", "E4BEED",
|
|
"E894F6F6", "EC1A5971", "EC4C4D", "F42853", "F43E61", "F46BEF", "F8AB05", "FC8B97", "7062B8", "78542E",
|
|
"C0A0BB8C", "C412F5", "C4A81D", "E8CC18", "EC2280", "F8E903F4",
|
|
]);
|
|
suggestions.insert("pinAirocon", vec![
|
|
"0007262F", "000B2B4A", "000EF4E7", "001333B", "00177C", "001AEF", "00E04BB3", "02101801", "0810734",
|
|
"08107710", "1013EE0", "2CAB25C7", "788C54", "803F5DF6", "94FBB2", "BC9680", "F43E61", "FC8B97",
|
|
]);
|
|
suggestions.insert("pinEmpty", vec![
|
|
"E46F13", "EC2280", "58D56E", "1062EB", "10BEF5", "1C5F2B", "802689", "A0AB1B", "74DADA", "9CD643",
|
|
"68A0F6", "0C96BF", "20F3A3", "ACE215", "C8D15E", "000E8F", "D42122", "3C9872", "788102", "7894B4",
|
|
"D460E3", "E06066", "004A77", "2C957F", "64136C", "74A78E", "88D274", "702E22", "74B57E", "789682",
|
|
"7C3953", "8C68C8", "D476EA", "344DEA", "38D82F", "54BE53", "709F2D", "94A7B7", "981333", "CAA366", "D0608C",
|
|
]);
|
|
suggestions.insert("pinCisco", vec!["001A2B", "00248C", "002618", "344DEB", "7071BC", "E06995", "E0CB4E", "7054F5"]);
|
|
suggestions.insert("pinBrcm1", vec!["ACF1DF", "BCF685", "C8D3A3", "988B5D", "001AA9", "14144B", "EC6264"]);
|
|
suggestions.insert("pinBrcm2", vec!["14D64D", "1C7EE5", "28107B", "84C9B2", "B8A386", "BCF685", "C8BE19"]);
|
|
suggestions.insert("pinBrcm3", vec!["14D64D", "1C7EE5", "28107B", "B8A386", "BCF685", "C8BE19", "7C034C"]);
|
|
suggestions.insert("pinBrcm4", vec!["14D64D", "1C7EE5", "28107B", "84C9B2", "B8A386", "BCF685", "C8BE19", "C8D3A3", "CCB255", "FC7516", "204E7F", "4C17EB", "18622C", "7C03D8", "D86CE9"]);
|
|
suggestions.insert("pinBrcm5", vec!["14D64D", "1C7EE5", "28107B", "84C9B2", "B8A386", "BCF685", "C8BE19", "C8D3A3", "CCB255", "FC7516", "204E7F", "4C17EB", "18622C", "7C03D8", "D86CE9"]);
|
|
suggestions.insert("pinBrcm6", vec!["14D64D", "1C7EE5", "28107B", "84C9B2", "B8A386", "BCF685", "C8BE19", "C8D3A3", "CCB255", "FC7516", "204E7F", "4C17EB", "18622C", "7C03D8", "D86CE9"]);
|
|
suggestions.insert("pinAirc1", vec!["181E78", "40F201", "44E9DD", "D084B0"]);
|
|
suggestions.insert("pinAirc2", vec!["84A423", "8C10D4", "88A6C6"]);
|
|
suggestions.insert("pinDSL2740R", vec!["00265A", "1CBDB9", "340804", "5CD998", "84C9B2", "FC7516"]);
|
|
suggestions.insert("pinRealtek1", vec!["0014D1", "000C42", "000EE8"]);
|
|
suggestions.insert("pinRealtek2", vec!["007263", "E4BEED"]);
|
|
suggestions.insert("pinRealtek3", vec!["08C6B3"]);
|
|
suggestions.insert("pinUpvel", vec!["784476", "D4BF7F0", "F8C091"]);
|
|
suggestions.insert("pinUR814AC", vec!["D4BF7F60"]);
|
|
suggestions.insert("pinUR825AC", vec!["D4BF7F5"]);
|
|
suggestions.insert("pinOnlime", vec!["D4BF7F", "F8C091", "144D67", "784476", "0014D1"]);
|
|
suggestions.insert("pinEdimax", vec!["801F02", "00E04C"]);
|
|
suggestions.insert("pinThomson", vec!["002624", "4432C8", "88F7C7", "CC03FA"]);
|
|
suggestions.insert("pinHG532x", vec!["00664B", "086361", "087A4C", "0C96BF", "14B968", "2008ED", "2469A5", "346BD3", "786A89", "88E3AB", "9CC172", "ACE215", "D07AB5", "CCA223", "E8CD2D", "F80113", "F83DFF"]);
|
|
suggestions.insert("pinH108L", vec!["4C09B4", "4CAC0A", "84742A4", "9CD24B", "B075D5", "C864C7", "DC028E", "FCC897"]);
|
|
suggestions.insert("pinONO", vec!["5C353B", "DC537C"]);
|
|
|
|
Self { algos, suggestions }
|
|
}
|
|
|
|
pub fn checksum(mut pin: u32) -> u32 {
|
|
let mut accum = 0;
|
|
while pin > 0 {
|
|
accum += 3 * (pin % 10);
|
|
pin /= 10;
|
|
accum += pin % 10;
|
|
pin /= 10;
|
|
}
|
|
(10 - (accum % 10)) % 10
|
|
}
|
|
|
|
pub fn generate(&self, algo_id: &str, mac: &NetworkAddress) -> Result<String, String> {
|
|
let _algo = self.algos.get(algo_id).ok_or("Invalid WPS pin algorithm")?;
|
|
if algo_id == "pinEmpty" {
|
|
return Ok(String::new());
|
|
}
|
|
|
|
let raw_pin: u64 = match algo_id {
|
|
"pin24" => mac.integer() & 0xFFFFFF,
|
|
"pin28" => mac.integer() & 0xFFFFFFF,
|
|
"pin32" => mac.integer() % 0x100000000,
|
|
"pinDLink" => self.pin_dlink(mac),
|
|
"pinDLink1" => {
|
|
let mut m = mac.clone();
|
|
m.set_integer(m.integer() + 1);
|
|
self.pin_dlink(&m)
|
|
}
|
|
"pinASUS" => self.pin_asus(mac),
|
|
"pinAirocon" => self.pin_airocon(mac),
|
|
"pinCisco" => 1234567,
|
|
"pinBrcm1" => 2017252,
|
|
"pinBrcm2" => 4626484,
|
|
"pinBrcm3" => 7622990,
|
|
"pinBrcm4" => 6232714,
|
|
"pinBrcm5" => 1086411,
|
|
"pinBrcm6" => 3195719,
|
|
"pinAirc1" => 3043203,
|
|
"pinAirc2" => 7141225,
|
|
"pinDSL2740R" => 6817554,
|
|
"pinRealtek1" => 9566146,
|
|
"pinRealtek2" => 9571911,
|
|
"pinRealtek3" => 4856371,
|
|
"pinUpvel" => 2085483,
|
|
"pinUR814AC" => 4397768,
|
|
"pinUR825AC" => 529417,
|
|
"pinOnlime" => 9995604,
|
|
"pinEdimax" => 3561153,
|
|
"pinThomson" => 6795814,
|
|
"pinHG532x" => 3425928,
|
|
"pinH108L" => 9422988,
|
|
"pinONO" => 9575521,
|
|
_ => return Err("Unknown WPS pin algorithm".to_string()),
|
|
};
|
|
|
|
let pin7 = (raw_pin % 10_000_000) as u32;
|
|
let csum = Self::checksum(pin7);
|
|
Ok(format!("{:07}{}", pin7, csum))
|
|
}
|
|
|
|
fn pin_dlink(&self, mac: &NetworkAddress) -> u64 {
|
|
let nic = mac.integer() & 0xFFFFFF;
|
|
let mut pin = nic ^ 0x55AA55;
|
|
pin ^= ((pin & 0xF) << 4)
|
|
+ ((pin & 0xF) << 8)
|
|
+ ((pin & 0xF) << 12)
|
|
+ ((pin & 0xF) << 16)
|
|
+ ((pin & 0xF) << 20);
|
|
pin %= 10_000_000;
|
|
if pin < 1_000_000 {
|
|
pin += ((pin % 9) * 1_000_000) + 1_000_000;
|
|
}
|
|
pin
|
|
}
|
|
|
|
fn pin_asus(&self, mac: &NetworkAddress) -> u64 {
|
|
let mac_str = mac.string();
|
|
let bytes: Vec<u32> = mac_str
|
|
.split(':')
|
|
.map(|s| u32::from_str_radix(s, 16).unwrap_or(0))
|
|
.collect();
|
|
if bytes.len() < 6 {
|
|
return 0;
|
|
}
|
|
let sum_b15 = bytes[1] + bytes[2] + bytes[3] + bytes[4] + bytes[5];
|
|
let mut pin_str = String::new();
|
|
for i in 0..7 {
|
|
let denom = 10 - ((i as u32 + sum_b15) % 7);
|
|
let digit = (bytes[i % 6] + bytes[5]) % denom;
|
|
pin_str.push_str(&digit.to_string());
|
|
}
|
|
pin_str.parse::<u64>().unwrap_or(0)
|
|
}
|
|
|
|
fn pin_airocon(&self, mac: &NetworkAddress) -> u64 {
|
|
let mac_str = mac.string();
|
|
let b: Vec<u64> = mac_str
|
|
.split(':')
|
|
.map(|s| u64::from_str_radix(s, 16).unwrap_or(0))
|
|
.collect();
|
|
if b.len() < 6 {
|
|
return 0;
|
|
}
|
|
((b[0] + b[1]) % 10)
|
|
+ (((b[5] + b[0]) % 10) * 10)
|
|
+ (((b[4] + b[5]) % 10) * 100)
|
|
+ (((b[3] + b[4]) % 10) * 1000)
|
|
+ (((b[2] + b[3]) % 10) * 10000)
|
|
+ (((b[1] + b[2]) % 10) * 100000)
|
|
+ (((b[0] + b[1]) % 10) * 1000000)
|
|
}
|
|
|
|
fn suggest_algos(&self, mac: &str) -> Vec<&'static str> {
|
|
let clean_mac = mac.replace([':', '-'], "").to_uppercase();
|
|
let mut res = Vec::new();
|
|
// Preserving insertion order of algos
|
|
let algo_ids = vec![
|
|
"pin24", "pin28", "pin32", "pinDLink", "pinDLink1", "pinASUS", "pinAirocon", "pinEmpty",
|
|
"pinCisco", "pinBrcm1", "pinBrcm2", "pinBrcm3", "pinBrcm4", "pinBrcm5", "pinBrcm6",
|
|
"pinAirc1", "pinAirc2", "pinDSL2740R", "pinRealtek1", "pinRealtek2", "pinRealtek3",
|
|
"pinUpvel", "pinUR814AC", "pinUR825AC", "pinOnlime", "pinEdimax", "pinThomson",
|
|
"pinHG532x", "pinH108L", "pinONO",
|
|
];
|
|
|
|
for algo_id in algo_ids {
|
|
if let Some(masks) = self.suggestions.get(algo_id) {
|
|
if masks.iter().any(|mask| clean_mac.starts_with(mask)) {
|
|
res.push(algo_id);
|
|
}
|
|
}
|
|
}
|
|
res
|
|
}
|
|
|
|
pub fn get_suggested(&self, mac_str: &str) -> Vec<PinItem> {
|
|
let Ok(mac) = NetworkAddress::from_str(mac_str) else {
|
|
return Vec::new();
|
|
};
|
|
let algos = self.suggest_algos(mac_str);
|
|
let mut res = Vec::new();
|
|
for id in algos {
|
|
if let Some(algo) = self.algos.get(id) {
|
|
let name = if algo.mode == AlgoMode::Static {
|
|
format!("Static PIN — {}", algo.name)
|
|
} else {
|
|
algo.name.to_string()
|
|
};
|
|
if let Ok(pin) = self.generate(id, &mac) {
|
|
res.push(PinItem {
|
|
id: id.to_string(),
|
|
name,
|
|
pin,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
res
|
|
}
|
|
|
|
pub fn get_likely(&self, mac_str: &str) -> Option<String> {
|
|
let list = self.get_suggested(mac_str);
|
|
list.first().map(|item| item.pin.clone())
|
|
}
|
|
|
|
pub fn prompt_wpspin(&self, bssid: &str) -> Option<String> {
|
|
let pins = self.get_suggested(bssid);
|
|
if pins.len() > 1 {
|
|
println!("PINs generated for {}:", bssid);
|
|
println!("{:<3} {:<10} {:<}", "#", "PIN", "Name");
|
|
for (i, pin_item) in pins.iter().enumerate() {
|
|
let number = format!("{})", i + 1);
|
|
println!("{:<3} {:<10} {:<}", number, pin_item.pin, pin_item.name);
|
|
}
|
|
loop {
|
|
print!("Select the PIN: ");
|
|
io::stdout().flush().ok();
|
|
let mut input = String::new();
|
|
if io::stdin().read_line(&mut input).is_err() {
|
|
break None;
|
|
}
|
|
if let Ok(num) = input.trim().parse::<usize>() {
|
|
if num >= 1 && num <= pins.len() {
|
|
return Some(pins[num - 1].pin.clone());
|
|
}
|
|
}
|
|
println!("Invalid number");
|
|
}
|
|
} else if pins.len() == 1 {
|
|
println!("[i] The only probable PIN is selected: {}", pins[0].name);
|
|
Some(pins[0].pin.clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|