DeviceId

repository·main·Indexed 21 days ago

https://github.com/matthewking/deviceid

A C# library for generating unique device identifiers to identify computers. It features a modular architecture with platform-specific packages for Windows, Linux, and Mac, as well as support for SQL Server. The library provides a DeviceIdBuilder for chaining hardware and software components, support for ARM64/ARM fallbacks, and a DeviceIdManager for validating identifiers across multiple versions.

Tokens
3.1K
Snippets
9
Records
10
Agent score
25%

What's inside DeviceId

  1. Use platform-specific methods in DeviceId v6

    main

    In version 6, instead of calling general methods directly on the builder, use platform-specific wrappers to ensure compatibility and access relevant components. This prevents runtime errors on unsupported platforms.

    Common mapping examples:

    v5 Methodv6 Windowsv6 Linuxv6 Mac
    AddOSInstallationID().OnWindows(x => x.AddMachineGuid()).OnLinux(x => x.AddMachineId()).OnMac(x => x.AddPlatformSerialNumber())
    AddMotherboardSerialNumber().OnWindows(x => x.AddMotherboardSerialNumber()).OnLinux(x => x.AddMotherboardSerialNumber())Not available
    AddSystemUUID().OnWindows(x => x.AddSystemUuid()).OnLinux(x => x.AddProductUuid())Not available
    AddSystemUUID() (as Processor ID).OnWindows(x => x.AddProcessorId()).OnLinux(x => x.AddCpuInfo())Not available
    // Example of platform-specific component selection in v6
    builder.OnWindows(x => x.AddMachineGuid())
           .OnLinux(x => x.AddMachineId())
           .OnMac(x => x.AddPlatformSerialNumber());
  2. Handle MAC address randomization and virtual adapters

    main

    To avoid issues with MAC address randomization (common in wireless adapters) or non-physical adapters (like VPNs), you can use the excludeWireless parameter.

    Cross-platform usage:

    string deviceId = new DeviceIdBuilder()
        .AddMacAddress(excludeWireless: true)
        .ToString();

    Windows-specific (WMI/MMI) usage: To exclude both wireless and non-physical adapters on Windows:

    string deviceId = new DeviceIdBuilder()
        .AddMacAddress(excludeWireless: true)
        .OnWindows(windows => windows
            .AddMacAddressFromWmi(excludeWireless: true, excludeNonPhysical: true)
        .ToString();
  3. Install the DeviceId packages

    main

    As of version 6, DeviceId is modular. You can pick and choose packages based on your target platform to avoid unnecessary dependencies.

    Core Packages:

    • DeviceId: Core functionality and cross-platform components.
    • DeviceId.Windows: Windows-specific components.
    • DeviceId.Windows.Wmi: Advanced Windows components using WMI.
    • DeviceId.Windows.WmiLight: Advanced Windows components using WmiLight.
    • DeviceId.Windows.Mmi: Advanced Windows components using MMI (useful where .NET Framework is absent).
    • DeviceId.Linux: Linux-specific components.
    • DeviceId.Mac: Mac-specific components.
    • DeviceId.SqlServer: Support for generating database IDs for SQL Server.

    Recommended for standard Windows apps: Install DeviceId and DeviceId.Windows.

    PM> Install-Package DeviceId
    PM> Install-Package DeviceId.Windows
  4. Migrate from DeviceId v5 to v6

    main

    Upgrading from version 5 to version 6 involves several breaking changes:

    1. Package Splitting: DeviceId is now split into platform-specific packages. You must add references to the packages required for your target platforms (e.g., DeviceId.Windows, DeviceId.Windows.Wmi, DeviceId.Linux).
    2. Formatter Changes: The default formatter has changed. To revert to the version 5 formatting style, use .UseFormatter(DeviceIdFormatters.DefaultV5).
    3. Method Renaming and Platform Restrictions: Many methods have been moved into platform-specific extension methods (e.g., .OnWindows(), .OnLinux(), .OnMac()). Some components are no longer available on certain platforms.

    If a specific component from v5 is missing in v6, you can re-implement it using a custom component.

    // Reverting to v5 formatter in v6
    string deviceId = new DeviceIdBuilder()
        .UseFormatter(DeviceIdFormatters.DefaultV5)
        .ToString();
  5. Validate multiple device ID formats with DeviceIdManager

    main

    If you have updated your device ID generation logic but need to remain backwards compatible with older identifiers (e.g., stored in license files), use DeviceIdManager. It allows you to register multiple builders, each associated with a version number. The manager will attempt to validate the input against the builders.

    Example: Validating against multiple versions

    var deviceIdManager = new DeviceIdManager()
        .AddBuilder(1, builder => builder
            .AddMachineName()
            .AddUserName()
            .AddMacAddress())
        .AddBuilder(2, builder => builder
            .AddMacAddress()
            .AddFileToken(TokenFilePath));
    
    var savedDeviceIdFromLicenseFile = ReadDeviceIdFromLicenseFile();
    
    // Validates the saved ID against the registered builders
    var isLicenseValid = deviceIdManager.Validate(savedDeviceIdFromLicenseFile);
    var deviceIdManager = new DeviceIdManager()
        .AddBuilder(1, builder => builder.AddMachineName())
        .AddBuilder(2, builder => builder.AddMacAddress());
    
    bool isValid = deviceIdManager.Validate(savedId);
  6. Handle ARM64 on Windows

    main

    On Windows ARM64 systems, the AddProcessorId method is compatible with both x86/x64 and ARM64. When running on ARM64, it automatically falls back to a combination of processor attributes (Manufacturer, Name, and NumberOfCores) because the standard x86-specific ProcessorId is unavailable.

    string deviceId = new DeviceIdBuilder()
        .OnWindows(windows => windows.AddProcessorId()) // Works on both x86/x64 and ARM64
        .ToString();
  7. Handle ARM on Linux

    main

    On ARM-based Linux systems (like Raspberry Pi), DMI (Desktop Management Interface) is often unavailable. DeviceId provides automatic fallbacks to Device Tree information:

    • AddProductUuid falls back to /sys/firmware/devicetree/base/serial-number.
    • AddMotherboardSerialNumber falls back to /sys/firmware/devicetree/base/model.

    You can also explicitly use ARM-specific Device Tree methods for more control.

    // Using automatic fallbacks
    builder.OnLinux(linux => linux.AddProductUuid());
    
    // Using explicit ARM Device Tree methods
    string deviceId = new DeviceIdBuilder()
        .OnLinux(linux => linux
            .AddDeviceTreeSerialNumber()  // ARM Device Tree serial number
            .AddDeviceTreeModel())        // ARM Device Tree model
        .ToString();
  8. Configure device identifier formatting

    main

    Use UseFormatter to control how the collected components are represented in the final string.

    Custom Hash Formatter:

    string deviceId = new DeviceIdBuilder()
        .AddMachineName()
        .AddOsVersion()
        .UseFormatter(new HashDeviceIdFormatter(() => SHA256.Create(), new Base32ByteArrayEncoder()))
        .ToString();

    Reverting to Version 5 format in Version 6:

    string deviceId = new DeviceIdBuilder()
        .AddMachineName()
        .AddOsVersion()
        .UseFormatter(DeviceIdFormatters.DefaultV5)
        .ToString();

    Available Formatters (IDeviceIdFormatter)

    • StringDeviceIdFormatter: Formats as a string containing each component ID using a specified encoding.
    • HashDeviceIdFormatter: Formats as a hash string using a specified hash algorithm and byte array encoding.
    • XmlDeviceIdFormatter: Formats as an XML document.

    Available Encoders (IDeviceIdComponentEncoder / IByteArrayEncoder)

    • PlainTextDeviceIdComponentEncoder: Plain text encoding.
    • HashDeviceIdComponentEncoder: Hash string encoding.
    • HexByteArrayEncoder: Hex string encoding.
    • Base32UrlByteArrayEncoder: Base64 URL-encoded string.
    • Base64ByteArrayEncoder: Base64 string.
    • Base64UrlByteArrayEncoder: Base64 URL-encoded string.
    string deviceId = new DeviceIdBuilder()
        .AddMachineName()
        .UseFormatter(DeviceIdFormatters.DefaultV5)
        .ToString();
  9. Build a device identifier using DeviceIdBuilder

    main

    Use the DeviceIdBuilder class to construct a device identifier by chaining component methods. The builder supports cross-platform components and platform-specific extensions (Windows, Linux, Mac) via lambda expressions.

    Example: Windows-specific ID

    string deviceId = new DeviceIdBuilder()
        .OnWindows(windows => windows.AddWindowsDeviceId())
        .ToString();

    Example: Cross-platform ID

    string deviceId = new DeviceIdBuilder()
        .AddMachineName()
        .AddOsVersion()
        .AddFileToken("example-device-token.txt")
        .ToString();

    Example: Complex multi-platform ID

    string deviceId = new DeviceIdBuilder()
        .AddMachineName()
        .AddOsVersion()
        .OnWindows(windows => windows
            .AddProcessorId()
            .AddMotherboardSerialNumber()
            .AddSystemDriveSerialNumber())
        .OnLinux(linux => linux
            .AddMotherboardSerialNumber()
            .AddSystemDriveSerialNumber())
        .OnMac(mac => mac
            .AddSystemDriveVolumeUUID()
            .AddPlatformSerialNumber())
        .ToString();

    Example: SQL Server Database ID

    using SqlConnection connection = new SqlConnection(connectionString);
    connection.Open();
    string databaseId = new DeviceIdBuilder()
        .AddSqlServer(connection, sql => sql
            .AddServerName()
            .AddDatabaseName()
            .AddDatabaseId())
        .ToString();
    string deviceId = new DeviceIdBuilder()
        .AddMachineName()
        .AddOsVersion()
        .ToString();
  10. Available device identifier components

    main

    The following extension methods can be used with DeviceIdBuilder to include specific hardware or software attributes in the identifier. Availability depends on the installed package.

    From DeviceId (Cross-platform)

    • AddUserName(): Current user's username.
    • AddMachineName(): Machine name.
    • AddOsVersion(): Current OS version (uses Environment.OSVersion).
    • AddMacAddress(): MAC address.
    • AddFileToken(string path): A unique token stored in a file. Fails silently if no permissions.

    From DeviceId.Windows

    • AddWindowsDeviceId(): Windows Device ID (Machine ID/Advertising ID).
    • AddWindowsProductId(): Windows Product ID.
    • AddRegistryValue(string key): A specified registry value.
    • AddMachineGuid(): Machine GUID from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography.

    From DeviceId.Windows.Wmi / WmiLight / Mmi

    • AddMacAddressFromWmi() / AddMacAddressFromMmi(): MAC address using improved query functionality.
    • AddProcessorId(): Processor ID (falls back to Manufacturer/Name/Cores on ARM64).
    • AddSystemDriveSerialNumber(): System drive's serial number.
    • AddMotherboardSerialNumber(): Motherboard serial number.
    • AddSystemUuid(): System UUID.

    From DeviceId.Linux

    • AddSystemDriveSerialNumber(): System drive's serial number.
    • AddMotherboardSerialNumber(): Motherboard serial number (falls back to Device Tree on ARM).
    • AddMachineId(): Machine ID from /var/lib/dbus/machine-id or /etc/machine-id.
    • AddProductUuid(): Product UUID from /sys/class/dmi/id/product_uuid (falls back to Device Tree on ARM).
    • AddCpuInfo(): CPU info from /proc/cpuinfo.
    • AddDockerContainerId(): Docker container ID from /proc/1/cgroup.
    • AddDeviceTreeSerialNumber(): Device Tree serial number (common on ARM).
    • AddDeviceTreeModel(): Device Tree model (common on ARM).

    From DeviceId.Mac

    • AddSystemDriveVolumeUUID(): System drive's Volume UUID.
    • AddPlatformSerialNumber(): IOPlatformSerialNumber.

    From DeviceId.SqlServer

    • AddServerName(): SQL Server name.
    • AddDatabaseName(): Database name.
    • AddDatabaseId(): Database ID.
    • AddServerProperty(string name): Specified server property.
    • AddServerProperty(string name, string value): Specified extended property.