systemid

package
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2025 License: Apache-2.0 Imports: 15 Imported by: 0

README

SystemID Package

A cross-platform Go package for generating unique device identifiers based on various system hardware and software characteristics.

Features

  • Cross-platform support: Works on Linux, macOS, and Windows
  • Architecture support: x86, x64, ARM32, ARM64/Apple Silicon, MIPS, PowerPC, RISC-V, and more
  • Multiple identifier sources: MAC addresses, CPU info, OS details, machine IDs, disk serials, and more
  • Configurable generation: Choose which identifiers to include in the device ID
  • Consistent output: Same system configuration always produces the same ID
  • Privacy-aware: User information is optional and disabled by default
  • Salt support: Add custom salt for application-specific IDs

Installation

import "github.com/maxthom/mir/internal/libs/systemid"

Usage

Generate a Device ID with Default Options
deviceID, err := systemid.GenerateDeviceID(systemid.DefaultOptions())
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Device ID: %s\n", deviceID)
Generate a Short Device ID
shortID, err := systemid.GetShortDeviceID(systemid.DefaultOptions())
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Short Device ID: %s\n", shortID) // 16 characters
Custom Options
opts := systemid.Options{
    IncludeMAC:       true,
    IncludeCPU:       true,
    IncludeOS:        false,
    IncludeDisks:     true,
    IncludeUser:      false,
    IncludeMachineID: true,
    Salt:             "my-app-v1.0",
}

deviceID, err := systemid.GenerateDeviceID(opts)
Get System Information
info, err := systemid.GetSystemInfo()
if err != nil {
    log.Fatal(err)
}

fmt.Printf("OS: %s %s\n", info.OSType, info.OSVersion)
fmt.Printf("Hostname: %s\n", info.Hostname)
fmt.Printf("MAC Addresses: %v\n", info.MACAddresses)

System Identifiers

The package collects the following system identifiers:

Hardware Identifiers
  • MAC Addresses: Network interface hardware addresses (excluding virtual interfaces)
  • CPU Information: Processor model, family, and other CPU-specific data
  • Disk Serials: Serial numbers of physical storage devices
System Identifiers
  • Machine ID: System-specific unique identifier
    • Linux: /etc/machine-id or /var/lib/dbus/machine-id
    • macOS: IOPlatformUUID from ioreg
    • Windows: Machine GUID from registry
  • Serial Numbers: Hardware serial numbers (system, board)
  • Product UUID: System product UUID
OS Information
  • OS Type: Operating system type (linux, darwin, windows)
  • OS Version: Operating system version string
  • OS Architecture: System architecture with normalized names:
    • amd64: 64-bit x86 (x86_64)
    • 386: 32-bit x86 (i386, i686)
    • arm64: 64-bit ARM (aarch64, Apple Silicon)
    • arm: 32-bit ARM (armv6, armv7)
    • mips64, mips64le: 64-bit MIPS
    • ppc64, ppc64le: 64-bit PowerPC
    • riscv64: 64-bit RISC-V
    • s390x: IBM System z
  • Hostname: System hostname
User Information (Optional)
  • Username: Current user's username
  • Home Directory: User's home directory path

Options

type Options struct {
    IncludeMAC       bool   // Include network MAC addresses
    IncludeCPU       bool   // Include CPU information
    IncludeOS        bool   // Include OS information
    IncludeDisks     bool   // Include disk serial numbers
    IncludeUser      bool   // Include user information (default: false)
    IncludeMachineID bool   // Include machine-specific IDs
    IncludeHostname  bool   // Include hostname
    Salt             string // Custom salt for ID generation
}

Security Considerations

  1. Privacy: User information is disabled by default to protect privacy
  2. Consistency: The same hardware configuration will always produce the same ID
  3. Salt Usage: Use application-specific salts to prevent ID correlation across applications
  4. Hashing: All identifiers are combined and hashed using SHA256
  5. Hash Irreversibility: SHA256 hashes cannot be reversed to obtain original system information
Hash Irreversibility

The device IDs generated by this package are one-way hashes, meaning:

  • Cannot be reversed: It's computationally infeasible to derive the original system information from the device ID
  • Information is lost: The 256-bit hash output contains less information than the combined input
  • Privacy protection: Even if someone has your device ID, they cannot determine your MAC address, CPU model, etc.
If You Need Retrievable Information

If your application needs to retrieve the original system information, consider these alternatives:

  1. Store Both ID and Info:
info, _ := systemid.GetSystemInfo()
deviceID, _ := systemid.GenerateDeviceID(systemid.DefaultOptions())
// Store both in your database
  1. Use Structured IDs (less secure):
// Create a structured ID that includes visible hostname
structured, _ := systemid.GenerateStructuredID(opts)
// Result: "mir-laptop-f282b622a980dca4"
fmt.Printf("Hostname: %s, Hash: %s\n", structured.Hostname, structured.UniqueHash)
  1. Separate Tracking:
// Use device ID for identification
// Store system info separately with encryption
encryptedInfo := encrypt(info)
store(deviceID, encryptedInfo)

Platform-Specific Behavior

Linux
  • Reads from /proc/cpuinfo for CPU information (x86 and ARM-specific fields)
  • Uses /etc/machine-id or /var/lib/dbus/machine-id for machine ID
  • Accesses /sys/class/dmi/id/ for hardware information
  • Falls back to lscpu for CPU info on systems without /proc/cpuinfo
  • Normalizes architecture names from uname -m to Go conventions
  • May require elevated permissions for some hardware identifiers
macOS
  • Uses ioreg for hardware information
  • Uses sysctl for CPU details (Intel and Apple Silicon)
  • Detects Apple Silicon (M1/M2/M3) with performance/efficiency core counts
  • Handles Rosetta 2 translation detection
  • Uses diskutil for disk information
  • All operations work with standard user permissions
Windows
  • Uses WMI (Windows Management Instrumentation) via wmic commands
  • Reads from Windows Registry for machine GUID
  • Detects architecture including ARM-based Windows devices
  • Handles WOW64 (32-bit apps on 64-bit Windows)
  • Supports Windows on ARM with x86/x64 emulation detection
  • All operations work with standard user permissions

Example Application

See mir/examples/device-id/main.go for a complete example application that demonstrates:

  • Generating device IDs with various options
  • Displaying system information
  • Using custom salts
  • JSON output support

Error Handling

The package gracefully handles missing identifiers. If a specific identifier cannot be retrieved:

  • The error is silently ignored
  • The identifier is excluded from the device ID generation
  • Other available identifiers are still used

If no identifiers can be collected, the generation will fail with an error.

Performance

  • Initial system information collection: ~50-200ms (varies by platform)
  • Subsequent ID generation from cached info: <1ms
  • The package does not cache system information between calls

License

This package is part of the mir project and follows the project's licensing terms.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateDeviceID

func GenerateDeviceID(opts Options) (string, error)

GenerateDeviceID generates a unique device ID based on system information

Example
// Generate a device ID with default options
opts := DefaultOptions()
deviceID, err := GenerateDeviceID(opts)
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Device ID: %s\n", deviceID[:16]+"...")

// Generate with custom options
customOpts := Options{
	IncludeMAC:       true,
	IncludeMachineID: true,
	Salt:             "my-app-v1.0",
}
customID, err := GenerateDeviceID(customOpts)
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}
fmt.Printf("Custom Device ID: %s\n", customID[:16]+"...")
Example (Irreversible)
// Generate an irreversible device ID
deviceID, _ := GenerateDeviceID(DefaultOptions())
fmt.Printf("SHA256 Device ID: %s\n", deviceID[:16]+"...")

// This ID cannot be reversed to get system info
// You can only verify by regenerating with same system

// For visible info, use structured ID
structured, _ := GenerateStructuredID(PrefixOptions{Hostname: true}, DefaultOptions())
fmt.Printf("Structured ID: %s\n", structured.ToString())
// Output might be: mir-laptop-f282b622a980dca4

func GenerateDeviceIDFromInfo

func GenerateDeviceIDFromInfo(info *SystemInfo, opts Options) (string, error)

GenerateDeviceIDFromInfo generates a device ID from provided system info

func GenerateReversibleID

func GenerateReversibleID(opts Options) (string, error)

GenerateReversibleID creates an encoded device ID that can be decoded WARNING: This reveals system information and should only be used when necessary

func GetPrefix

func GetPrefix(opts PrefixOptions) (string, error)

func GetPrefixedDeviceID

func GetPrefixedDeviceID(prefixOpts PrefixOptions, deviceOpts Options) (string, error)

GetPrefixedDeviceID generates a device ID with an optional prefix This is a convenience function that combines GetPrefix and device ID generation

func GetShortDeviceID

func GetShortDeviceID(opts Options) (string, error)

GetShortDeviceID returns a shorter version of the device ID (first 16 characters)

Types

type Collector

type Collector interface {
	GetMACAddresses() ([]string, error)
	GetCPUInfo() (string, error)
	GetOSInfo() (osType, osVersion, osArch string, err error)
	GetHostname() (string, error)
	GetMachineID() (string, error)
	GetSerialNumber() (string, error)
	GetProductUUID() (string, error)
	GetBoardSerial() (string, error)
	GetDiskSerials() ([]string, error)
	GetUsername() (string, error)
	GetHomeDir() (string, error)
}

Collector interface for platform-specific implementations

type IDComparison

type IDComparison struct {
	// What we can determine from hashed IDs
	Identical bool `json:"identical"`

	// What we can determine from structured IDs
	SameHostname *bool `json:"same_hostname,omitempty"`
}

CompareDeviceIDs compares two device IDs and returns what can be determined

func CompareIDs

func CompareIDs(id1, id2 string) IDComparison

CompareIDs compares two device IDs and returns what can be determined

type Options

type Options struct {
	// Include network interfaces (IncludeMAC addresses)
	IncludeMAC bool
	// Include CPU information
	IncludeCPU bool
	// Include OS information
	IncludeOS bool
	// Include disk serials
	IncludeDisk bool
	// Include user information
	IncludeUser bool
	// Include machine-specific IDs
	IncludeMachineID bool
	// Include hostname
	IncludeHostname bool
	// Custom salt for ID generation
	Salt string
	// Leave the ID in plain text
	Plain bool
}

Options for device ID generation

func DefaultOptions

func DefaultOptions() Options

DefaultOptions returns options with all identifiers enabled

func DefaultUniqueOptions

func DefaultUniqueOptions() Options

DefaultUniqueOptions returns options with MAC, MachineID, and Hostname enabled

type PrefixOptions

type PrefixOptions struct {
	Prefix   string
	Hostname bool
	Username bool
}

type ReversibleDeviceInfo

type ReversibleDeviceInfo struct {
	Version      int      `json:"v"`
	MACAddresses []string `json:"mac,omitempty"`
	CPUInfo      string   `json:"cpu,omitempty"`
	OSInfo       struct {
		Type    string `json:"type"`
		Version string `json:"ver"`
		Arch    string `json:"arch"`
	} `json:"os"`
	Hostname string `json:"host,omitempty"`
}

ReversibleDeviceInfo contains system information that can be encoded/decoded

func DecodeReversibleID

func DecodeReversibleID(encodedID string) (*ReversibleDeviceInfo, error)

DecodeReversibleID decodes a reversible device ID back to system information

type StructuredDeviceID

type StructuredDeviceID struct {
	// Visible components
	Prefix string `json:"prefix"`

	// Hashed unique identifier (irreversible)
	UniqueHash string `json:"hash"`
}

StructuredDeviceID represents a device ID that contains visible, parseable information

func GenerateStructuredID

func GenerateStructuredID(prefixOpts PrefixOptions, opts Options) (*StructuredDeviceID, error)

GenerateStructuredID creates a device ID with both visible and hashed components

func ParseStructuredID

func ParseStructuredID(id string) (*StructuredDeviceID, error)

ParseStructuredID parses a structured ID string

func (*StructuredDeviceID) ToString

func (s *StructuredDeviceID) ToString() string

ToString converts the structured ID to a string format

type SystemInfo

type SystemInfo struct {
	// Hardware identifiers
	MACAddresses []string `json:"mac_addresses"`
	CPUInfo      string   `json:"cpu_info"`

	// OS identifiers
	OSType         string `json:"os_type"`
	OSVersion      string `json:"os_version"`
	OSArchitecture string `json:"os_architecture"`
	Hostname       string `json:"hostname"`

	// System identifiers
	MachineID    string `json:"machine_id"`
	SerialNumber string `json:"serial_number"`
	ProductUUID  string `json:"product_uuid"`
	BoardSerial  string `json:"board_serial"`

	// Disk identifiers
	DiskSerials []string `json:"disk_serials"`

	// Additional identifiers
	Username string `json:"username"`
	HomeDir  string `json:"home_dir"`
}

SystemInfo contains various system identifiers

func GetSystemInfo

func GetSystemInfo() (*SystemInfo, error)

GetSystemInfo collects all available system information

Example
info, err := GetSystemInfo()
if err != nil {
	fmt.Printf("Error: %v\n", err)
	return
}

fmt.Printf("OS: %s %s (%s)\n", info.OSType, info.OSVersion, info.OSArchitecture)
fmt.Printf("Hostname: %s\n", info.Hostname)
fmt.Printf("MAC Addresses: %d found\n", len(info.MACAddresses))

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL