libhid is a small C++ library for generating a stable hardware-based
identifier for the current machine.
Despite the name, libhid is not a USB HID library. In this project, HID means
hardware identifier.
Project naming policy:
- The repository and package name remain
libhidfor the0.xseries. - A rename to something like
libhwidis not planned during0.x, because it would create avoidable churn in package names, CMake targets, and include paths while the API is still stabilizing. - If a rename is ever chosen for
1.x, the transition should keep compatibility aliases and clear migration notes for at least one documented release cycle.
The library collects several OS-specific hardware properties, combines them, calculates an MD5 hash, and formats the result as a UUID-like string. This can be useful when an application developer needs a repeatable local machine identifier without implementing separate Windows, Linux, and macOS hardware probes.
- Binding application settings or local licenses to a specific machine.
- Generating a stable client or workstation identifier.
- Distinguishing machines in internal tools, diagnostics, or telemetry.
- Avoiding direct hardware probing code in the main application.
libhid does not provide cryptographic identity proof and should not be used as
the only security layer for licensing or access control. Hardware values can
change after system reinstall, disk replacement, motherboard replacement,
virtual machine changes, or restricted OS permissions.
- Windows: uses WMI and system APIs.
- Linux: reads DMI, disk, and network information from system paths.
- macOS: uses IOKit and CoreFoundation.
libhid combines several platform-specific values before hashing them. The
exact source values are intentionally not exposed as public API.
Windows sources:
Win32_ComputerSystemProduct.UUIDWin32_OperatingSystem.SerialNumber- System drive
Win32_LogicalDisk.VolumeSerialNumber Win32_ComputerSystemProduct.IdentifyingNumberWin32_BaseBoard.SerialNumber- Non-Microsoft network adapter MAC address
Linux sources:
/sys/class/dmi/id/product_uuid/sys/class/dmi/id/product_serial/sys/class/dmi/id/board_serial- First matching disk UUID from
/dev/disk/by-uuid/for common device names:sd*,hd*,vd*,xvd*, ornvme* - First usable MAC address from likely physical
eth*,en*,wl*, orww*network interfaces
macOS sources:
IOPlatformSerialNumberIOPlatformUUIDIOMACAddressfromIOEthernetInterfaceorIO80211Interface- Storage device
Serial Numberfrom common block-storage service families such as AHCI, NVMe, or generic block-storage devices
Known limitations:
- Virtual machines, containers, and CI runners may expose incomplete or synthetic hardware values.
- Linux DMI files may require elevated permissions or may be unavailable.
- Linux network interface naming can vary. The library skips common virtual or service interfaces such as loopback, Docker bridges, veth, tun/tap, and similar names where practical.
- Storage identifiers can change after disk replacement, repartitioning, or OS reinstall.
- macOS IOKit service names may vary across Intel and Apple Silicon hardware.
#include "libhid/libhid.h"
#include <iostream>
#include <string>
int main()
{
const std::string hardwareId = libhid::GetHardwareId();
const std::string appHardwareId = libhid::GetHardwareId("my-product");
if (hardwareId.empty()) {
std::cerr << "Hardware ID is not available\n";
return 1;
}
std::cout << hardwareId << '\n';
std::cout << appHardwareId << '\n';
return 0;
}The returned value is a UUID-like string, for example:
01234567-89ab-cdef-0123-456789abcdef
libhid::GetHardwareId() may return an empty string if the operating system does not
expose the required hardware properties or if access to those properties is
restricted. Callers should treat an empty value as "hardware ID unavailable" and
decide on an application-specific fallback.
Use libhid::GetHardwareId("your-product-or-namespace") when different applications
should receive different stable IDs on the same machine. Passing an empty string
keeps the same behavior as libhid::GetHardwareId().
The older LibHid::GetHardwareId() class API remains available as a
compatibility wrapper.
There is also a small C API for bindings and non-C++ consumers:
#include "libhid/libhid_c.h"
char hardware_id[37];
const libhid_status status = libhid_get_hardware_id(hardware_id, sizeof(hardware_id));
if (status == LIBHID_STATUS_OK) {
/* hardware_id now contains a NUL-terminated UUID-like string */
}The C API uses caller-provided buffers to avoid cross-module allocation issues.
Use libhid_hardware_id_buffer_size() to get the recommended minimum buffer
size for the current public format.
Compatibility note:
LibHidand the rootlibhid.hwrapper are kept for existing users during the0.xseries.SHAREDTESTLIB_EXPORTremains as a compatibility alias for older code that referenced the previous export macro name.- These compatibility names are not deprecated yet, so existing users do not get warning noise during the initial CMake migration period.
- The compatibility
LibHidclass remains supported throughout0.x. New code should preferlibhid::GetHardwareId(). - Before
1.0.0, the project should make an explicit decision whetherLibHid, the rootlibhid.h, andSHAREDTESTLIB_EXPORTbecome long-term aliases or receive a documented deprecation period.
The generated ID is intended to be stable for the same machine while the underlying hardware and OS-reported values remain stable. It can change after:
- Reinstalling or significantly reconfiguring the operating system.
- Replacing disks, network adapters, motherboard, or other hardware.
- Running inside a different virtual machine configuration.
- Losing permissions to read platform-specific hardware properties.
The value is useful as a practical machine fingerprint, but it is not a permanent or tamper-proof device identity.
libhid uses MD5 only to normalize collected hardware properties into a compact
UUID-like string. It is not used as a cryptographic security primitive.
Do not rely on this ID as the only proof for licensing, authentication, or access control. For security-sensitive systems, combine it with server-side validation, signed license data, user accounts, or another trusted mechanism.
An application namespace is not a secret and should not be treated as a password or cryptographic salt. It is only a practical way to separate IDs between products or internal tools.
Current library version: 0.1.0.
Requirements:
- CMake 3.16 or newer.
- A C++ compiler with C++11 support.
Configure and build:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config ReleaseBuild a static library instead of the default shared library:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF
cmake --build build --config ReleaseOr use CMake presets if your CMake version supports them. Presets use the
Ninja generator, so make sure ninja is available in PATH.
cmake --preset release
cmake --build --preset release
ctest --preset releaseThere is also a static-release preset for a static-library build:
cmake --preset static-release
cmake --build --preset static-release
ctest --preset static-releaseRun tests:
ctest --test-dir build --output-on-failureDeveloper warnings are enabled by default. They can be disabled with:
cmake -S . -B build -DLIBHID_ENABLE_WARNINGS=OFFExample applications are enabled by default. They can be disabled with:
cmake -S . -B build -DLIBHID_BUILD_EXAMPLES=OFFTests are enabled by default. They can be disabled with:
cmake -S . -B build -DLIBHID_BUILD_TESTS=OFFInstall rules are enabled by default. They can be disabled with:
cmake -S . -B build -DLIBHID_INSTALL=OFFInstall from source:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
cmake --install build --config Release --prefix installThe built library is written to ../libs relative to the project directory.
On Windows with MinGW, the output files are named:
libhid.dll
libhid.dll.a
libhid_print_hardware_id.exe
Run the example:
../libs/libhid_print_hardware_idWhen running a MinGW build manually on Windows, make sure the compiler runtime
DLLs are available in PATH. Qt Creator usually configures this environment
automatically.
After installing libhid, consume it with:
find_package(libhid CONFIG REQUIRED)
target_link_libraries(your_app PRIVATE libhid::libhid)If libhid was installed to a custom prefix, point CMake at it:
cmake -S your-app -B build -DCMAKE_PREFIX_PATH=/path/to/libhid/installWhen using the repository directly as a subproject:
add_subdirectory(path/to/libhid)
target_link_libraries(your_app PRIVATE libhid::libhid)The repository includes a Conan 2 recipe and a test_package consumer.
Create a local package:
conan create . --build=missingConsume it from another project with:
[requires]
libhid/0.1.0
[generators]
CMakeDeps
CMakeToolchainThen link the exported CMake target:
find_package(libhid CONFIG REQUIRED)
target_link_libraries(your_app PRIVATE libhid::libhid)The same package exports both the C++ headers and the C header
libhid/libhid_c.h.
Open the project as a CMake project by selecting CMakeLists.txt.
Qt Creator will create its own local build directory, which is ignored by Git.
The recommended public include path is libhid/libhid.h. A compatibility
wrapper is also kept at libhid.h for existing code.
The project now uses CMake as its build system. The old libhid.pro qmake file
has been removed.
Recommended migration path:
- Open or import
CMakeLists.txtinstead oflibhid.pro. - Replace direct source-file inclusion with linking to
libhidorlibhid::libhid. - Prefer
#include "libhid/libhid.h"for new code. - Keep
#include "libhid.h"only for existing code that has not been migrated yet. - Remove qmake-specific build assumptions from downstream projects over time.
GitHub Actions builds and tests the CMake project on Windows, Linux, and macOS in Debug and Release configurations. The workflow also verifies the install step so the CMake package export remains usable by downstream projects.
Version tags that start with v, for example v0.1.0, trigger the release
workflow. It builds and tests the project on Windows, Linux, and macOS, installs
the library into platform-specific packages, and uploads archives with SHA256
checksums to the GitHub release.
libhid follows semantic versioning while moving toward a stable 1.0.0
release.
Before 1.0.0, minor versions may still include API adjustments, but the project
tries to keep source compatibility whenever practical. After 1.0.0, breaking
API or ABI changes should require a major version bump.
Breaking changes include removing public functions, changing public signatures,
changing installed CMake target names, changing public header paths, or removing
documented compatibility wrappers such as LibHid.
- Debug builds define
LIB_DEBUGand may print diagnostic messages to standard output or standard error. - Debug builds now report which hardware probe sources were found or missing, without printing raw hardware values by default.
- The library exports its public API from a shared library using the generated
LIBHID_EXPORTmacro. The oldSHAREDTESTLIB_EXPORTname is kept as a compatibility alias for the0.xseries. - The hardware ID depends on values reported by the operating system and may be empty if required properties are unavailable.
- The repository includes
.clang-formatand.clang-tidyconfigs for gradual code-quality adoption. A practical localclang-tidyrun can use-DCMAKE_EXPORT_COMPILE_COMMANDS=ONandclang-tidy -p <build-dir>.
This project is licensed under the MIT License. See LICENSE for details.