Skip to content

SDKeyDev/sdkey-cpp

Repository files navigation

sdkey

Official C++ client for SDKey license authentication.

Implements the sealed session protocol: Ed25519-verified handshake, HKDF session keys, and AES-256-GCM validate envelopes, plus plaintext client auth (register_user / login / upgrade). See PROTOCOL.md.

Version: 0.2.0

Requirements

  • C++17 compiler
  • CMake 3.16+ (or Visual Studio 2022)
  • OpenSSL 3.x and libcurl (statically linked into sdkey.lib on Windows)

On Windows, presets and the VS solution use the vcpkg triplet x64-windows-static-md, so OpenSSL/curl/zlib are merged into sdkey.lib. Your app does not need libcrypto / libcurl DLLs beside the exe — only link sdkey plus the usual Windows system libraries (ws2_32, crypt32, …), which CMake wires up via sdkey::sdkey.

nlohmann/json is provided via vcpkg (vcpkg.json) or fetched automatically by CMake (private to the library).

Visual Studio 2022

Open msvc/sdkey.sln. Dependencies install from vcpkg.json on first build (requires vcpkg / VS vcpkg component). See msvc/README.md.

Or generate a full CMake VS solution (includes tests):

set VCPKG_ROOT=C:\path\to\vcpkg
cmake --preset vs2022
start build\vs2022\sdkey.sln

Windows (CMake + vcpkg)

# From a Developer PowerShell, with VCPKG_ROOT set:
cmake --preset default
cmake --build build

This uses x64-windows-static-md and merges OpenSSL/curl into sdkey.lib.

Linux / macOS

# Debian/Ubuntu: sudo apt install libssl-dev libcurl4-openssl-dev
# macOS: brew install openssl curl
cmake -B build
cmake --build build

Quick start

Embed these values from the SDKey dashboard when you ship your app. app_version must exactly match the application version configured in the dashboard (applications.version); mismatch returns APP_OUTDATED.

#include <iostream>
#include <sdkey/sdkey.hpp>

int main() {
  sdkey::ClientOptions opts;
  opts.api_base_url = "https://api.sdkey.dev";
  opts.app_id = "YOUR_APP_ID";
  opts.app_version = "1.0.0";  // exact match required
  opts.app_public_key_b64 = "YOUR_APP_PUBLIC_KEY_BASE64";
  sdkey::Client client(std::move(opts));

  try {
    // Pass hwid for desktop/native; omit (or pass "") for web clients.
    const auto result = client.validate("SDKY-XXXX-XXXX-XXXX-XXXX", "machine-hwid");
    if (result.success) {
      std::cout << "licensed tier=" << result.subscription_tier
                << " " << result.status.value_or("")
                << " " << result.expires_at.value_or("")
                << " message=" << result.message << "\n";
    } else {
      // Sealed validate failures use `message` (not `error`).
      std::cerr << "denied " << result.code << " " << result.message << "\n";
    }
  } catch (const sdkey::Error& err) {
    // Init / transport failures: server text is in what(); wire code in server_code().
    std::cerr << err.code_string() << " " << err.what();
    if (!err.server_code().empty()) {
      std::cerr << " [" << err.server_code() << "]";
    }
    std::cerr << "\n";
    throw;
  }
}

Link against the sdkey target (pulls in Windows system libs when needed):

find_package(sdkey REQUIRED)
target_link_libraries(your_app PRIVATE sdkey::sdkey)

Manual / non-CMake on Windows: link sdkey.lib plus ws2_32.lib, crypt32.lib, wldap32.lib, normaliz.lib, bcrypt.lib, and advapi32.lib.

validate calls init() automatically when no session exists. Sessions last ~15 minutes server-side; on SESSION_EXPIRED the client clears local state so the next call re-handshakes.

HWID is optional. When omitted, the server skips HWID lock, mismatch, and HWID-ban checks (IP bans still apply). Prefer omitting HWID for web clients.

Message vs error

Per-app responseMessages can customize many strings. Clients receive those strings; they do not load settings themselves.

Surface Success text field Failure text field
Session init (none) error (thrown as sdkey::Error, also server_code())
Sealed validate message message
Client register / login / upgrade (none) error (on ClientAuthResult)

Sealed validate success

{
  "success": true,
  "code": "OK",
  "message": "validated",
  "status": "active",
  "expiresAt": "2026-01-01T00:00:00.000Z",
  "subscriptionTier": 0,
  "sessionId": "...",
  "timestamp": 1720000001,
  "v": 1
}

Sealed validate failure

{
  "success": false,
  "code": "HWID_MISMATCH",
  "message": "Hardware ID mismatch",
  "status": null,
  "expiresAt": null,
  "sessionId": "...",
  "timestamp": 1720000001,
  "v": 1
}

Session init / client auth failure

{
  "success": false,
  "error": "Client version outdated",
  "code": "APP_OUTDATED"
}

API

sdkey::Client(ClientOptions)

Option Type Description
api_base_url std::string API origin (no trailing slash)
app_id std::string Application UUID
app_version std::string Exact app version → sent as clientVersion (required)
app_public_key_b64 std::string Raw Ed25519 public key (32 bytes), base64
http_post HttpPostFn Optional HTTP POST override (url, jsonBody) -> {status, responseBody}

Methods

  • init() — challenge handshake; verifies the signed hello; derives the AES session key; sends clientVersion
  • validate(license_key, hwid = {}) — sealed validate; omits hwid JSON key when empty; always decrypts then verifies Ed25519 before trusting success; result includes message, code, subscription_tier
  • register_user(RegisterOptions) — plaintext POST /api/v1/client/register
  • login(LoginOptions) — plaintext POST /api/v1/client/login
  • upgrade(UpgradeOptions) — plaintext POST /api/v1/client/upgrade (username + license key only; no password)
  • get_session() / clear_session() — inspect or drop the local crypto session

register is a C++ keyword, so the method is named register_user.

Client auth example

sdkey::RegisterOptions reg;
reg.username = "player1";
reg.password = "••••••••";
reg.license_key = "SDKY-XXXX-XXXX-XXXX-XXXX";  // often required by app settings
auto auth = client.register_user(reg);
if (!auth.success) {
  std::cerr << auth.code << " " << auth.error << "\n";
} else {
  // Persist auth.session_token / auth.expires_at for your app.
  // This token is NOT the crypto sessionId used by sealed validate.
}

sdkey::UpgradeOptions up;
up.username = "player1";
up.license_key = "SDKY-HIGHER-TIER-KEY";
auth = client.upgrade(up);  // no password

Errors

Protocol / transport failures throw sdkey::Error with a local code:

INIT_FAILED · HELLO_SIGNATURE_INVALID · VALIDATE_RESPONSE_INVALID · RESPONSE_SIGNATURE_INVALID · SESSION_MISMATCH · CLOCK_SKEW · NETWORK

On init (and some plaintext validate envelopes), Error::what() is the server error string and Error::server_code() is the wire code (e.g. APP_OUTDATED).

License denials (banned, HWID mismatch, etc.) return a normal ValidateResult with success: false — they are not thrown. Auth denials return ClientAuthResult with success: false and server error / code.

This client SDK does not implement developer tooling APIs (Authorization: Bearer sdk_live_…).

Example

cmake -B build -DSDKEY_BUILD_EXAMPLES=ON
cmake --build build
# set SDKEY_APP_ID, SDKEY_APP_VERSION, SDKEY_APP_PUBLIC_KEY_B64, SDKEY_LICENSE_KEY, SDKEY_HWID
./build/examples/basic

Security notes

  • Never ship app private keys in a client.
  • Do not skip signature verification — that is the anti-spoof binding.
  • This package is open source; the SDKey server remains a separate product.

Development

cmake -B build -DSDKEY_BUILD_TESTS=ON -DSDKEY_BUILD_EXAMPLES=ON
cmake --build build
ctest --test-dir build --output-on-failure

License

MIT

About

C++ SDK for SDKey.dev authentication server

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors