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 { 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 = 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::().unwrap_or(0) } fn pin_airocon(&self, mac: &NetworkAddress) -> u64 { let mac_str = mac.string(); let b: Vec = 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 { 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 { let list = self.get_suggested(mac_str); list.first().map(|item| item.pin.clone()) } pub fn prompt_wpspin(&self, bssid: &str) -> Option { 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::() { 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 } } }