AtlasAuth - HWID (Hardware ID)
A HWID is an opaque string the SDK computes to identify the machine (or, with a seed, the *owner*) behind an activation. The server binds the first HWID it sees for a key/user, then requires an exact match on later logins. This is how AtlasAuth limits a license to one machine (or to one person who knows a seed) without ever shipping a secret in the client.
The SDK sends hwid on the authenticating calls: /login, /register, and /license. /init advertises whether the app expects one (hwid_required).
1. Derivation
The HWID is always an opaque string. The SDK produces it in one of two ways.
Default - hardware fingerprint
hwid = base64( SHA256( <stable hardware ids joined with "|"> ) )The SDK gathers a few stable machine identifiers, joins them with |, hashes the joined string with SHA-256, and base64-encodes the digest. The result is the same on the same machine across runs, but reveals nothing about the underlying ids (it is a one-way hash).
- The C# SDK's default ids are the Windows registry
MachineGuid
(HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid) + the machine name + the processor count. (A stronger-fingerprint hook is available, see "Stronger fingerprints" below.)
- The C++ SDK hashes its own set of stable hardware ids the same way
(base64(SHA256(...))).
Because it is a hash, the same physical machine yields the same HWID every time, and a different machine yields a different one.
Seed override - portable, reproducible
If a developer (or the key's owner) sets a secret seed, the SDK ignores the hardware and instead returns:
hwid = base64( SHA256( "atlasauth-hwid-seed|" + seed ) )This is reproducible on any machine where the seed is known, and unguessable to anyone who does not know it. It lets the legitimate owner move their own key between their machines (all produce the identical HWID) while still binding the key to "whoever holds the seed."
- C#: pass the seed as the constructor's third argument:
new AtlasClient(APP_ID, PUBLIC_KEY, "my-secret-seed").
- C++: call
client.setHwidSeed("my-secret-seed")beforeinit()/login().
The seed is the thing to protect: anyone with it reproduces the HWID. Treat it like a
shared password for "this owner's machines."
2. Enable / disable HWID locking
HWID locking is controlled at two levels:
- Per app:
hwid_lockis either off (a key works anywhere; HWID is not
enforced) or on (the first HWID is bound and enforced). /init reports hwid_required so the client knows whether the app expects a HWID at all.
- Per key: an individual key may override the app-level setting. A key can opt
*in* to locking even when the app default is off, or opt *out* (work anywhere) even when the app default is on.
So the effective rule is simple: the key's own setting wins where present; otherwise the app's hwid_lock applies.
3. Binding & resetting
- Binding: on the first successful activation of a key/user, the server records the
HWID it received and binds it. Every later login must present the exact same HWID.
- Mismatch: a different HWID on a locked key returns
hwid_mismatch(on/loginand
/license). The activation is refused.
- Resetting: resetting a HWID clears the binding, so the key/user re-binds to
whatever HWID is presented on the next activation. Use this when a legitimate user changes hardware. (Reset is done by the app owner via the dashboard, not by the client SDK.)
To move a key to a new machine you either reset its binding (so it re-binds to the new machine's fingerprint), or use a seed so the HWID is identical on every machine that knows the seed and no reset is ever needed.
4. Bans - exact match, no false positives
Bans of type:"hwid" match the full HWID string with exact equality only. There is no prefix/substring/fuzzy matching, so a HWID ban can never accidentally catch a different machine: no false positives. A banned HWID surfaces as hwid_banned on /register, /login, and /license, and as banned: true (with reason: "banned") on the /check heartbeat.
Note that HWID bans are distinct from the other ban types the contract exposes (user_banned, license_banned, ip_banned); each has its own code on the relevant endpoint. Only the HWID ban is keyed on the opaque HWID string.
5. Lock modes - summary
| Mode | How the HWID is produced | Reproducible on | Typical use | |
|---|---|---|---|---|
| Hardware (default) | base64(SHA256(stable hardware ids)) | The same physical machine only | Lock a key to one device. | |
| Seed override | `base64(SHA256("atlasauth-hwid-seed\ | " + seed))` | Any machine that knows the seed | Let the owner roam across their own machines. |
| Lock off (per app or per key) | HWID not enforced | n/a | Keys that should work anywhere. |
And the lifecycle of a locked key:
- First activation → server binds the presented HWID.
- Later activations → must present the exact same HWID, else
hwid_mismatch. - HWID reset (owner action) → binding cleared → re-binds on next activation.
- HWID ban (owner action) → that exact HWID string is refused everywhere
(hwid_banned / banned), with no false positives.
6. Stronger fingerprints (optional)
If the default identifiers are not strong enough for your threat model, you can supply your own (motherboard serial, disk volume serial, MAC, etc.). Keep the same inputs across runs or the user's HWID will change and lock them out.
- C#: gather your id strings and pass them to the static
AtlasClient.HashIdsToHwid(IEnumerable<string>), which produces the canonical base64(SHA256(join("|", ids))) form, matching the default's encoding exactly. (Avoid pulling in System.Management/WMI unless you accept that dependency.)
- Whatever you choose, the output is still just the opaque
base64(SHA256(...))HWID the
server binds and matches.
