using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Security.Cryptography; using System.Text; using Microsoft.Win32; namespace Deploymentcenter.Client; public class HardwareIdResult { public string HardwareId { get; set; } = string.Empty; public int HwidVersion { get; set; } = 2; public string HwidSource { get; set; } = string.Empty; public string Platform { get; set; } = string.Empty; public string LegacyHardwareId { get; set; } = string.Empty; } public static class HardwareId { private static readonly string[] MacStopWords = new[] { "docker", "veth", "br-", "virbr", "cni", "flannel", "cali", "weave", "zt", "tailscale", "ipsec", "sit", "gre", "dummy", "bond", "macvlan", "ovs" }; public static HardwareIdResult GetHardwareId(string productSlug = "default_app") { string platform = OperatingSystemHelpers.GetPlatformCode(); string rawValue = string.Empty; string source = string.Empty; // 3.1 Override (all platforms, highest priority) var overrideValue = LicenseConfig.HardwareIdOverride; if (!string.IsNullOrWhiteSpace(overrideValue)) { rawValue = overrideValue!.Trim(); source = "override"; } else if (OperatingSystemHelpers.IsWindows()) { // 3.2 Windows Sources string? machineGuid = GetWindowsMachineGuid(); if (IsPlausibleMachineId(machineGuid)) { rawValue = machineGuid!; source = "machine-guid"; } else { string? macs = GetStablePhysicalMacs(); if (!string.IsNullOrWhiteSpace(macs)) { rawValue = macs!; source = "mac"; } else { rawValue = GetOrCreateKeyfile(productSlug); source = "keyfile"; } } } else if (OperatingSystemHelpers.IsLinux()) { // 3.3 Linux Sources string? systemdId = ReadTextFileFile("/etc/machine-id"); if (IsPlausibleMachineId(systemdId)) { rawValue = systemdId!; source = "machine-id"; } else { string? dbusId = ReadTextFileFile("/var/lib/dbus/machine-id"); if (IsPlausibleMachineId(dbusId)) { rawValue = dbusId!; source = "dbus-machine-id"; } else { string? dmiUuid = ReadTextFileFile("/sys/class/dmi/id/product_uuid"); if (IsPlausibleMachineId(dmiUuid)) { rawValue = dmiUuid!; source = "dmi-uuid"; } else { string? macs = GetStablePhysicalMacs(); if (!string.IsNullOrWhiteSpace(macs)) { rawValue = macs!; source = "mac"; } else { rawValue = GetOrCreateKeyfile(productSlug); source = "keyfile"; } } } } } else { // Generic fallback string? macs = GetStablePhysicalMacs(); if (!string.IsNullOrWhiteSpace(macs)) { rawValue = macs!; source = "mac"; } else { rawValue = GetOrCreateKeyfile(productSlug); source = "keyfile"; } } string v2Hash = ComputeV2Hash(platform, source, rawValue); string v2HardwareId = $"2:{platform}:{v2Hash}"; string legacyHardwareId = GetLegacyHardwareId(); return new HardwareIdResult { HardwareId = v2HardwareId, HwidVersion = 2, HwidSource = source, Platform = platform, LegacyHardwareId = legacyHardwareId }; } public static string ComputeV2Hash(string platform, string source, string rawValue) { string domainString = "LicenseLabrador-HWID-v2"; string payload = $"{domainString}\n{platform}\n{source}\n{rawValue}"; using var sha256 = SHA256.Create(); byte[] bytes = Encoding.UTF8.GetBytes(payload); byte[] hash = sha256.ComputeHash(bytes); return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } public static string GetLegacyHardwareId() { // Legacy v1 algorithm: SHA256 of MachineName + First MAC string firstMac = GetFirstPhysicalMacLegacy(); string raw = $"{Environment.MachineName}:{firstMac}"; using var sha256 = SHA256.Create(); byte[] bytes = Encoding.UTF8.GetBytes(raw); byte[] hash = sha256.ComputeHash(bytes); return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } public static bool IsPlausibleMachineId(string? v) { if (string.IsNullOrWhiteSpace(v)) return false; string trimmed = v!.Trim(); if (trimmed.Length < 16) return false; if (trimmed.Equals("uninitialized", StringComparison.OrdinalIgnoreCase)) return false; if (trimmed.Trim('0').Length == 0) return false; return true; } private static string? GetWindowsMachineGuid() { if (!OperatingSystemHelpers.IsWindows()) return null; try { using var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); using var subKey = baseKey.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography"); return subKey?.GetValue("MachineGuid")?.ToString(); } catch { return null; } } private static string? GetStablePhysicalMacs() { try { var validMacs = new List(); var interfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (var ni in interfaces) { if (ni.OperationalStatus != OperationalStatus.Up && ni.OperationalStatus != OperationalStatus.Unknown) continue; if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel) continue; string name = ni.Name.ToLowerInvariant(); string desc = ni.Description.ToLowerInvariant(); if (MacStopWords.Any(sw => name.Contains(sw) || desc.Contains(sw))) continue; var physAddr = ni.GetPhysicalAddress(); var bytes = physAddr.GetAddressBytes(); if (bytes.Length == 0) continue; // Check locally administered bit (mac[0] & 0x02) != 0 if ((bytes[0] & 0x02) != 0) continue; // On Linux, check /sys/class/net//device if (OperatingSystemHelpers.IsLinux()) { string sysDevicePath = $"/sys/class/net/{ni.Name}/device"; if (!Directory.Exists(sysDevicePath) && !File.Exists(sysDevicePath)) continue; } string macHex = BitConverter.ToString(bytes).Replace("-", "").ToUpperInvariant(); if (!string.IsNullOrWhiteSpace(macHex) && macHex.Trim('0').Length > 0) { validMacs.Add(macHex); } } if (validMacs.Count == 0) return null; validMacs.Sort(); return string.Join(":", validMacs); } catch { return null; } } private static string GetFirstPhysicalMacLegacy() { try { var interfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (var ni in interfaces) { if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue; var bytes = ni.GetPhysicalAddress().GetAddressBytes(); if (bytes.Length > 0) { return BitConverter.ToString(bytes).Replace("-", "").ToUpperInvariant(); } } } catch { } return "000000000000"; } private static string? ReadTextFileFile(string path) { try { if (!File.Exists(path)) return null; return File.ReadAllText(path).Trim(); } catch { return null; } } private static string GetOrCreateKeyfile(string productSlug) { string dir = LicenseConfig.GetStorageDirectory(productSlug); Directory.CreateDirectory(dir); string keyfilePath = Path.Combine(dir, "machine.key"); if (File.Exists(keyfilePath)) { try { string existing = File.ReadAllText(keyfilePath).Trim(); if (!string.IsNullOrWhiteSpace(existing)) return existing; } catch { } } byte[] randomBytes = new byte[32]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(randomBytes); } string keyBase64 = Convert.ToBase64String(randomBytes); File.WriteAllText(keyfilePath, keyBase64, Encoding.UTF8); // Set 0600 permissions on Linux/macOS SetUnixPermissions(keyfilePath, "600"); return keyBase64; } private static void SetUnixPermissions(string filePath, string mode) { if (OperatingSystemHelpers.IsWindows()) return; #if NET8_0_OR_GREATER try { File.SetUnixFileMode(filePath, UnixFileMode.UserRead | UnixFileMode.UserWrite); } catch { } #else try { var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = "chmod", Arguments = $"{mode} \"{filePath}\"", UseShellExecute = false, CreateNoWindow = true }); proc?.WaitForExit(); } catch { } #endif } }