Skip to content

Repository files navigation

System Locker Simple — C++

Official C++20 client for the System Locker Simple protocol (POST /auth): one request, one answer. No sessions, no heartbeats, no signatures — the right fit when the machine running the check is one you control (Threat Model 2). For software distributed to untrusted machines, use a Bedrock client instead: it verifies an Ed25519 signature on every response.

The repository is a source-only distribution. There are two ways to integrate the library into your own project:

  1. Integrate the source directly — recommended. Copy include/ and src/ into your project (C++20, libcurl) and compile them as part of your own build.
  2. Link the prebuilt static package in static/ — the fastest setup when you prefer a ready-made static library for Windows x64; see STATIC-LIBRARY.md.

The library is provided for your convenience above all else. Compiling the implementation directly into your own binary keeps your existing build flags and lets you step through the source in your own build; the prebuilt package is there when you want a quick, known-good link.

Quickstart

#include <syslocker/simple/client.hpp>

syslocker::simple::Config config;
config.systemId = "abcdefghijklmnopqrst"; // from the dashboard
config.version = "1.0.0";
// config.hwid stays empty: the default SL-HWID identity, shared with a
// Bedrock program on the same machine. Use "1" to disable device locking.

syslocker::simple::Client client(std::move(config));

auto authenticated = client.authenticateWithKey("SL-XXXX-XXXX-XXXX");
if (!authenticated.ok())
    return handle(authenticated.error()); // transport / server error
if (!*authenticated)
    return block(); // rejected — block the action
// …run the gated action…

authenticateWithKey reports success only for a literal true answer. Everything else is a typed Errorerror().kind categorizes it and error().reason carries the server's raw reason:

Kind Meaning
Configuration invalid local configuration; no request was sent
Transport the HTTP exchange failed or returned a non-2xx status
Server the server reported an internal error (dbe)
Denied license denial (frozen, hwid banned, expired key, …)
Sso Google-SSO account; ssoLink(error) extracts the portal
LocalFailure the opt-in SL-HWID device identity could not be produced
UnknownReason the server emitted a new reason; the raw string is carried

Operations

Operation Method
Check a license key authenticateWithKey(key)
Check username + password authenticateWithPassword(user, pass)
Key expiry (Never or a UTC date) keyExpirationForKey / keyExpirationForPassword
Server-side variable getVariable(name, key = "")
Self-service HWID reset resetHwidForKey / resetHwidForPasswordGranted/Denied/TooSoon

Management API (server-side tooling)

syslocker::simple::Config config; // … systemId, version …
config.apiKey = ""; // management key — keep it on your server

syslocker::simple::Client client(std::move(config));
auto count = client.management().redeemedUserCount();
auto keys = client.management().generateKeys(3, 10, "june-batch");

management() wraps POST /api/v1: key status/expiration, HWID resets (single or whole-system), key generation, bans, expiry adjustment. The API key grants all of it — keep it on servers you control.

Google SSO (account authentication)

Accounts created through Google sign-in have no local password on the server. A username/password check for such an account fails with an Sso error whose reason embeds the portal URL where the user completes Google sign-in and receives a system-specific password (valid 180 days) to use as their account password. There is no callback; the user transcribes the generated password into your login form and you simply retry.

Deliver the portal link to your user through your own channel (API response, email, chat).

auto authenticated = client.authenticateWithPassword(username, password);
if (!authenticated.ok() && authenticated.error().kind == syslocker::simple::ErrorKind::Sso)
{
    // sso / ssoexp / ssowrong — the portal URL is embedded in the error.
    std::string portal = syslocker::simple::ssoLink(authenticated.error());
    SendToUser(user, portal); // your channel: API response, email, chat…
}

googleSsoUrl(systemId) (or client.googleSsoUrl()) builds the same portal URL before any denial, if you already know the account signs in through Google.

Device identifiers (HWID)

The default derivation is the fault-tolerant SL-HWID module (hwidMode = "sl-hwid", see slhwid/slhwid.hpp, vendored from the standalone SL-HWID module). The HWID comes from a random key locked behind threshold secret sharing instead of hashing hardware directly. It is fault tolerant and cross platform (Windows, macOS, Linux), combines 14 hardware factors, and any two of them can fail or change without changing the HWID; drifted factors are quietly re-absorbed after each successful authentication. The module's own persisted value is hard-locked, so copied state cannot stand in for changed hardware.

Things to know:

  • Storage is shared. The enrollment lives in one per-machine location (the registry on Windows, an application-support directory elsewhere), shared by every System Locker client on the device. Configure slhwidStore only when you deliberately need separate device state.
  • Re-activation exists. If hardware drifts past the recovery threshold, requests fail with a LocalFailure error and the user needs a reset.

SL-HWID is the natural fit for a launcher: a Simple-based launcher that opens a Bedrock-protected program reports exactly the HWID the Bedrock client reports, because both share the same per-machine enrollment. The key the user already activated in the launcher works for the protected program too — one device, one HWID, no hwid mismatch between the two.

SL-HWID changes the device identifier only. It does not change what the Simple protocol guarantees: responses are still unsigned, so only use this client on machines you control.

Plain hardware hash (legacy), opt-in

hwidMode = "legacy" restores the plain hardware-factor hash used by the Go, Node.js, and Python Simple clients (syslocker/simple/hwid.hpp): the machine GUID, hardware UUID, CPU id, and MAC, normalized, joined in a fixed order, and hashed with SHA-256 (base64url). It is the weaker option — the hash over-fits a handful of hardware values, so swapping a disk or NIC — or cloning the machine into a VM — changes the HWID and forces your user through a device reset.

A developer-supplied stable value works as well as either mode:

config.hwid = MyStableId();

Set config.hwid = "1" only to explicitly disable device locking. An explicit hwid value (including "1") always wins over both modes.

Security

See SECURITY.md. Report vulnerabilities privately through the System Locker support channels, not via public issues.

About

Reference implementation of the System Locker Simple Auth API for C++

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages