#include "nm_connection.h" #include #include #include #include #include namespace radiantwave::network { // ─── UUID Generation (standard RFC 4122 v4) ───────────────────────────── namespace { std::string generateUuid() { static std::random_device rd; static std::mt19937_64 gen(rd()); static std::uniform_int_distribution dis; uint8_t bytes[16]; uint64_t v1 = dis(gen); uint64_t v2 = dis(gen); std::memcpy(bytes, &v1, 8); std::memcpy(bytes + 8, &v2, 8); bytes[6] = (bytes[6] & 0x0F) | 0x40; // Version 4 bytes[8] = (bytes[8] & 0x3F) | 0x80; // Variant RFC 4122 static const char hex[] = "0123456789abcdef"; char buf[37]; int p = 0; for (int i = 0; i < 16; ++i) { if (i == 4 || i == 6 || i == 8 || i == 10) { buf[p++] = '-'; } buf[p++] = hex[(bytes[i] >> 4) & 0x0F]; buf[p++] = hex[bytes[i] & 0x0F]; } buf[36] = '\0'; return std::string(buf, 36); } } // anonymous namespace // ─── Connection Builder ──────────────────────────────────────────────── ConnectionSettings buildConnectionSettings( const std::string& ssid, const std::string& password, SecurityType security, const std::string& interface_name ) { // Validate SSID if (!isValidSsid(ssid)) { throw std::invalid_argument("Invalid SSID: must be 1-32 bytes with no null characters"); } // Validate password if required if (requiresPassword(security)) { std::string error = validatePassword(password, security); if (!error.empty()) { throw std::invalid_argument(error); } } ConnectionSettings settings; // ── connection section ───────────────────────────────────────────── settings["connection"] = { {"id", ssid}, {"type", std::string("802-11-wireless")}, {"uuid", generateUuid()}, {"interface-name", interface_name}, {"autoconnect", true}, {"permissions", std::vector()} }; // ── 802-11-wireless section ──────────────────────────────────────── settings["802-11-wireless"] = { {"ssid", ssidToBytes(ssid)}, {"mode", std::string("infrastructure")} }; // ── 802-11-wireless-security section ─────────────────────────────── if (security != SecurityType::Open) { SettingDict security_settings; switch (security) { case SecurityType::WEP: security_settings["key-mgmt"] = std::string("none"); security_settings["wep-key0"] = password; security_settings["wep-key-type"] = static_cast(1); // WEP_KEY_TYPE_PASSPHRASE break; case SecurityType::WPA: case SecurityType::WPA2: security_settings["key-mgmt"] = std::string("wpa-psk"); security_settings["auth-alg"] = std::string("open"); security_settings["psk"] = password; security_settings["psk-flags"] = static_cast(0); break; case SecurityType::WPA3: security_settings["key-mgmt"] = std::string("sae"); security_settings["psk"] = password; break; default: break; } settings["802-11-wireless-security"] = security_settings; } // ── ipv4 section ─────────────────────────────────────────────────── settings["ipv4"] = { {"method", std::string("auto")} }; // ── ipv6 section ─────────────────────────────────────────────────── settings["ipv6"] = { {"method", std::string("auto")} }; return settings; } // ─── Security Detection ──────────────────────────────────────────────── SecurityType detectSecurityType( uint32_t ap_flags, uint32_t wpa_flags, uint32_t rsn_flags ) { // WPA3 uses SAE (flag 0x0400) if (rsn_flags & nm_ap_wpa_flags::RsnSae) { return SecurityType::WPA3; } // WPA2 uses RSN PSK (flag 0x0100) if (rsn_flags & nm_ap_wpa_flags::RsnPsk) { return SecurityType::WPA2; } // WPA uses WPA flags (not RSN) if (wpa_flags != 0) { return SecurityType::WPA; } // WEP uses privacy flag if (ap_flags & nm_ap_flags::Privacy) { return SecurityType::WEP; } return SecurityType::Open; } bool requiresPassword(SecurityType type) { return type != SecurityType::Open; } // ─── SSID Utilities ──────────────────────────────────────────────────── std::vector ssidToBytes(const std::string& ssid) { return std::vector(ssid.begin(), ssid.end()); } std::string bytesToSsid(const std::vector& bytes) { return std::string(bytes.begin(), bytes.end()); } bool isValidSsid(const std::string& ssid) { // SSID must be 1-32 bytes if (ssid.empty() || ssid.size() > 32) { return false; } // SSID must not contain null bytes for (char c : ssid) { if (c == '\0') { return false; } } return true; } // ─── Password Validation ─────────────────────────────────────────────── std::string validatePassword(const std::string& password, SecurityType type) { switch (type) { case SecurityType::WEP: // WEP keys can be 5 or 13 ASCII characters, or 10/26 hex digits // For simplicity, we accept any non-empty string if (password.empty()) { return "WEP network requires a password"; } break; case SecurityType::WPA: case SecurityType::WPA2: // WPA/WPA2 PSK requires 8-63 characters if (password.empty()) { return "WPA/WPA2 network requires a password"; } if (password.size() < 8) { return "WPA/WPA2 password must be at least 8 characters"; } if (password.size() > 63) { return "WPA/WPA2 password must be at most 63 characters"; } break; case SecurityType::WPA3: // WPA3 SAE requires 8-63 characters if (password.empty()) { return "WPA3 network requires a password"; } if (password.size() < 8) { return "WPA3 password must be at least 8 characters"; } if (password.size() > 63) { return "WPA3 password must be at most 63 characters"; } break; case SecurityType::Open: // No password required break; } return ""; // Valid } } // namespace radiantwave::network