machid

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 14, 2025 License: GPL-3.0 Imports: 13 Imported by: 0

README

MachID

MachID (Machine Identity, pronounced "Mock Eye Dee") is a Go library for generating unique machine identifiers on Linux systems.

Features

  • eMachID (Ephemeral Machine Identifier): A unique, one-time identifier that can never be regenerated
  • reMachID (Reconstructable Machine Identifier): A reproducible identifier based on hardware that remains constant for a given machine
  • Secure SHA-256 hashing
  • Privacy-focused: No data is stored, only returned to the caller
  • Automatic fallback from sysfs to dmidecode
  • Filesystem fallback when hardware IDs are unavailable (with warning)
  • Strict mode to disable filesystem fallback
  • Configurable logging for warnings
  • Memory clearing of sensitive data after use

Installation

go get github.com/LyrinoxTechnologies/machID

Requirements

  • Linux operating system (uses /sys/class/dmi/id/ and dmidecode)
  • Root privileges (required to read hardware identifiers)
  • dmidecode (optional, used as fallback if sysfs is unavailable)
# Install dmidecode on Debian/Ubuntu
sudo apt install dmidecode

# Install dmidecode on RHEL/CentOS/Fedora
sudo dnf install dmidecode

Usage

Basic Usage
package main

import (
    "fmt"
    "log"

    "github.com/LyrinoxTechnologies/machID"
)

func main() {
    salt := "your-application-secret"

    // Generate an ephemeral (one-time) machine ID
    emachid, err := machid.GenerateEMachID(salt)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("eMachID: %s\n", emachid)

    // Generate a reconstructable (reproducible) machine ID
    remachid, err := machid.GenerateReMachID(salt)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("reMachID: %s\n", remachid)
}
Generate Both IDs
info, err := machid.GenerateBoth(salt)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("eMachID: %s\n", info.EMachID)
fmt.Printf("reMachID: %s\n", info.ReMachID)
fmt.Printf("Used Fallback: %v\n", info.UsedFallback)
Check If Fallback Was Used
remachid, usedFallback, err := machid.GenerateReMachIDWithInfo(salt)
if err != nil {
    log.Fatal(err)
}
if usedFallback {
    fmt.Println("Warning: Using filesystem-based machine ID (no hardware IDs available)")
}
fmt.Printf("reMachID: %s\n", remachid)
Strict Mode (Disable Filesystem Fallback)
// Enable strict mode - will return an error if hardware IDs are unavailable
machid.SetStrictMode(true)

remachid, err := machid.GenerateReMachID(salt)
if err == machid.ErrStrictModeNoHardwareID {
    log.Fatal("Hardware IDs required but not available")
}
Custom Logger
// Integrate with your logging framework
machid.SetLogger(func(msg string) {
    log.Println(msg)
})

// Or disable logging entirely
machid.SetLogger(nil)
Running Your Application

Since MachID requires root privileges, run your application with sudo:

sudo go run main.go
# or
sudo ./your-compiled-binary

API Reference

Functions
GenerateEMachID(salt string) (string, error)

Generates an Ephemeral Machine Identifier. This ID is unique and can only be generated once, based on the current Unix nanosecond timestamp combined with the provided salt.

Parameters:

  • salt: A non-empty string used to add entropy to the hash

Returns:

  • A 64-character hex-encoded SHA-256 hash
  • An error if root privileges are missing or salt is empty
GenerateReMachID(salt string) (string, error)

Generates a Reconstructable Machine Identifier. This ID is reproducible - the same hardware will always generate the same ID when using the same salt.

Parameters:

  • salt: An optional string to add to the hash (can be empty for salt-less operation)

Returns:

  • A 64-character hex-encoded SHA-256 hash
  • An error if root privileges are missing or hardware IDs cannot be retrieved
GenerateReMachIDWithInfo(salt string) (string, bool, error)

Same as GenerateReMachID but also returns whether the filesystem fallback was used.

Returns:

  • The reMachID
  • usedFallback: true if filesystem fallback was used
  • An error if generation fails
GenerateBoth(salt string) (*MachIDInfo, error)

Generates both eMachID and reMachID in a single call.

Parameters:

  • salt: A non-empty string used for both identifiers

Returns:

  • A MachIDInfo struct containing both identifiers and fallback status
  • An error if generation fails for either identifier
SetStrictMode(enabled bool)

Enables or disables strict mode. When enabled, the library will NOT fall back to filesystem-based machine IDs and will return ErrStrictModeNoHardwareID instead.

IsStrictMode() bool

Returns whether strict mode is currently enabled.

SetLogger(logger func(msg string))

Sets a custom logger function for warning messages. Pass nil to disable logging.

ClearFallbackFiles() error

Removes the filesystem fallback files. Useful for regenerating new fallback IDs.

HasFallbackFiles() bool

Returns true if filesystem fallback files exist.

Types
MachIDInfo
type MachIDInfo struct {
    EMachID      string // Ephemeral Machine Identifier
    ReMachID     string // Reconstructable Machine Identifier
    UsedFallback bool   // True if filesystem fallback was used for reMachID
}
Errors
Error Description
ErrNotRoot Library called without root privileges
ErrEmptySalt Empty salt provided for eMachID generation
ErrNoHardwareID Unable to retrieve hardware identifiers
ErrDmidecodeNotFound dmidecode needed but not installed
ErrStrictModeNoHardwareID Strict mode enabled and hardware IDs unavailable
ErrFallbackFileCreation Failed to create filesystem fallback files

How It Works

eMachID (Ephemeral Machine Identifier)
  1. Captures the current Unix time in nanoseconds
  2. Combines with the provided salt
  3. Generates a SHA-256 hash
  4. Returns the hex-encoded result

Because it uses nanosecond precision, each call produces a unique identifier that can never be regenerated.

reMachID (Reconstructable Machine Identifier)
  1. Reads hardware identifiers from sysfs:
    • /sys/class/dmi/id/product_serial
    • /sys/class/dmi/id/product_uuid
    • Fallbacks: chassis_serial, board_serial
  2. If sysfs fails, falls back to dmidecode:
    • system-serial-number
    • system-uuid
    • Fallbacks: chassis-serial-number, baseboard-serial-number
  3. If no hardware identifiers are available (and strict mode is disabled):
    • Logs a warning to stdout
    • Creates hidden files in /etc/.machid/ with random data
    • Uses these files as the source for the machine ID
  4. Combines the identifiers with optional salt
  5. Generates a SHA-256 hash
  6. Returns the hex-encoded result

The same hardware with the same salt will always produce the same reMachID.

Filesystem Fallback

When the BIOS doesn't provide proper system variables (serial number/UUID), the library will:

  1. Log a warning to stdout (or your custom logger):

    WARNING: machid - BIOS is not providing the system variables (serial/UUID) needed to generate hardware-based machine IDs.
    WARNING: machid - Falling back to filesystem-based machine IDs stored in /etc/.machid/
    WARNING: machid - These IDs will persist across reboots but are NOT tied to hardware.
    
  2. Create hidden files in /etc/.machid/:

    • .mserial - Random 128-character hex string for serial
    • .muuid - Random 128-character hex string for UUID
    • Files have 0600 permissions (owner read/write only)
    • Directory has 0700 permissions
  3. Use these files to generate consistent machine IDs that persist across reboots.

Disabling Fallback (Strict Mode)

If you require hardware-based IDs only:

machid.SetStrictMode(true)

_, err := machid.GenerateReMachID(salt)
if err == machid.ErrStrictModeNoHardwareID {
    // Handle the case where hardware IDs are required but unavailable
}

Security Considerations

  • Root Required: The library refuses to run without root privileges to prevent unauthorized access to hardware identifiers
  • No Storage: No data is written to disk (except fallback files when hardware IDs unavailable)
  • Memory Clearing: Sensitive data (hardware IDs, salt copies) are cleared from memory after hashing
  • SHA-256: Cryptographically secure hashing prevents reverse-engineering of hardware identifiers
  • Restrictive Permissions: Fallback files are created with 0600 permissions

Use Cases

  • License Validation: Use reMachID to bind software licenses to specific machines
  • Device Fingerprinting: Identify returning devices in a fleet
  • One-Time Tokens: Use eMachID for unique, non-reproducible tokens
  • Audit Logging: Generate unique identifiers for audit trails

Example

Run the included example:

cd example
sudo go run main.go

License

See LICENSE for details.

Documentation

Overview

Package machid provides machine identification generation for Linux systems. It generates two types of machine IDs: - eMachID (Ephemeral Machine Identifier): A unique, one-time ID based on current time and salt - reMachID (Reconstructable Machine Identifier): A reproducible ID based on hardware identifiers

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotRoot is returned when the library is called without root privileges
	ErrNotRoot = errors.New("machid: root privileges required (run with sudo)")

	// ErrEmptySalt is returned when an empty salt is provided for eMachID generation
	ErrEmptySalt = errors.New("machid: salt cannot be empty")

	// ErrNoHardwareID is returned when no hardware identifiers can be found
	ErrNoHardwareID = errors.New("machid: unable to retrieve hardware identifiers from sysfs or dmidecode")

	// ErrDmidecodeNotFound is returned when dmidecode is needed but not installed
	ErrDmidecodeNotFound = errors.New("machid: dmidecode not found, please install it (e.g., apt install dmidecode)")

	// ErrStrictModeNoHardwareID is returned in strict mode when hardware IDs are unavailable
	ErrStrictModeNoHardwareID = errors.New("machid: strict mode enabled - hardware identifiers unavailable and filesystem fallback is disabled")

	// ErrFallbackFileCreation is returned when fallback files cannot be created
	ErrFallbackFileCreation = errors.New("machid: failed to create filesystem fallback files")
)

Error definitions

Functions

func ClearCache

func ClearCache() error

ClearCache removes the cached machine IDs.

func ClearFallbackFiles

func ClearFallbackFiles() error

ClearFallbackFiles removes the filesystem fallback files if they exist. This can be used to regenerate new filesystem-based IDs.

Returns an error if the files exist but cannot be removed.

func GenerateEMachID

func GenerateEMachID(salt string) (string, error)

GenerateEMachID generates an Ephemeral Machine Identifier. This ID is unique and can only be generated once (based on current Unix nanosecond time and salt).

Unlike GenerateReMachID, this function does NOT require root privileges since it only uses the current timestamp and salt, not hardware identifiers.

Parameters:

  • salt: A non-empty string used to add entropy to the hash

Returns:

  • The eMachID as a hex-encoded SHA-256 hash
  • An error if salt is empty

Security: The salt and time values are cleared from memory after hashing.

func GenerateReMachID

func GenerateReMachID(salt string) (string, error)

GenerateReMachID generates a Reconstructable Machine Identifier. This ID is reproducible - the same hardware will always generate the same ID.

The ID is generated from hardware identifiers found in:

  • /sys/class/dmi/id/product_serial (or chassis_serial, board_serial)
  • /sys/class/dmi/id/product_uuid

If sysfs is not available, it falls back to dmidecode. If no hardware identifiers are available and strict mode is disabled (default), it falls back to filesystem-based identifiers stored in /etc/.machid/

Parameters:

  • salt: An optional string to add to the hash for additional uniqueness per application

Returns:

  • The reMachID as a hex-encoded SHA-256 hash
  • An error if root privileges are missing or hardware IDs cannot be retrieved

Security: All hardware identifiers are cleared from memory after hashing.

Note: If filesystem fallback is used, a warning will be logged to stdout. Use SetStrictMode(true) to disable the filesystem fallback.

func GenerateReMachIDWithInfo

func GenerateReMachIDWithInfo(salt string) (remachid string, usedFallback bool, err error)

GenerateReMachIDWithInfo generates a Reconstructable Machine Identifier and returns additional information about how it was generated.

This is the same as GenerateReMachID but also returns whether the filesystem fallback was used.

Parameters:

  • salt: An optional string to add to the hash for additional uniqueness per application

Returns:

  • The reMachID as a hex-encoded SHA-256 hash
  • usedFallback: true if filesystem fallback was used instead of hardware IDs
  • An error if generation fails

func GetOrGenerateEMachID

func GetOrGenerateEMachID(salt string) (emachid string, fromCache bool, err error)

GetOrGenerateEMachID attempts to load the cached eMachID, or generates a new one. This function does NOT require sudo since eMachID generation uses timestamps only.

Parameters:

  • salt: Salt for the machine ID

Returns:

  • The eMachID
  • Whether the ID was loaded from cache (true) or freshly generated (false)
  • An error if generation fails

func GetOrGenerateReMachID

func GetOrGenerateReMachID(salt string) (remachid string, fromCache bool, err error)

GetOrGenerateReMachID attempts to load the cached reMachID, or generates a new one. If generation is needed, sudo is required. The generated ID is cached for future use.

Parameters:

  • salt: Salt for the machine ID (must match previous runs for consistent IDs)

Returns:

  • The reMachID
  • Whether the ID was loaded from cache (true) or freshly generated (false)
  • An error if generation fails (including if sudo is required but not available)

func HasFallbackFiles

func HasFallbackFiles() bool

HasFallbackFiles returns true if the filesystem fallback files exist.

func IncrementActionCount

func IncrementActionCount() (int, error)

IncrementActionCount increments the action counter in the cache. Returns the new action count.

func IsStrictMode

func IsStrictMode() bool

IsStrictMode returns whether strict mode is currently enabled.

func RotateEMachID

func RotateEMachID(salt string) (string, error)

RotateEMachID generates a new eMachID and updates the cache. This should be called when you need to refresh the ephemeral ID. Does NOT require sudo.

Parameters:

  • salt: Salt for the new eMachID

Returns:

  • The new eMachID
  • An error if generation or caching fails

func SaveCachedIDs

func SaveCachedIDs(cache *CachedMachineIDs) error

SaveCachedIDs saves machine IDs to the cache file. When running with sudo, it fixes ownership so the real user can read the file.

func SetLogger

func SetLogger(logger func(msg string))

SetLogger sets a custom logger function for warning messages. This is useful for integrating with existing logging frameworks.

Parameters:

  • logger: A function that accepts a string message. Pass nil to disable logging.

func SetStrictMode

func SetStrictMode(enabled bool)

SetStrictMode enables or disables strict mode. When strict mode is enabled, the library will NOT fall back to filesystem-based machine IDs when hardware identifiers are unavailable. Instead, it will return an error.

Parameters:

  • enabled: true to enable strict mode, false to allow filesystem fallback

Types

type CachedMachineIDs

type CachedMachineIDs struct {
	ReMachID    string `json:"remach_id"`
	EMachID     string `json:"emach_id,omitempty"`
	Salt        string `json:"salt,omitempty"`
	ActionCount int    `json:"action_count"`
	CreatedAt   int64  `json:"created_at,omitempty"`
}

CachedMachineIDs holds cached machine identifiers

func GetOrGenerateBoth

func GetOrGenerateBoth(salt string) (*CachedMachineIDs, error)

GetOrGenerateBoth loads or generates both machine IDs. For reMachID, sudo is required if not cached. For eMachID, sudo is never required.

Parameters:

  • salt: Salt for both machine IDs

Returns:

  • CachedMachineIDs containing both IDs
  • An error if reMachID generation fails (typically if sudo is needed but not available)

func LoadCachedIDs

func LoadCachedIDs() (*CachedMachineIDs, error)

LoadCachedIDs loads cached machine IDs from disk. Returns nil if no cache exists or cache is invalid.

type MachIDInfo

type MachIDInfo struct {
	EMachID      string // Ephemeral Machine Identifier
	ReMachID     string // Reconstructable Machine Identifier
	UsedFallback bool   // True if filesystem fallback was used for reMachID
}

MachIDInfo contains both types of machine identifiers.

func GenerateBoth

func GenerateBoth(salt string) (*MachIDInfo, error)

GenerateBoth generates both eMachID and reMachID in a single call.

Parameters:

  • salt: A non-empty string used for both identifiers

Returns:

  • A MachIDInfo struct containing both identifiers and fallback status
  • An error if generation fails for either identifier

Directories

Path Synopsis
Example program demonstrating the machID library usage.
Example program demonstrating the machID library usage.

Jump to

Keyboard shortcuts

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