Skip to content
Windows only · Invite only. To get the SDK, DM @snailyt on Telegram.

Security model

Assume your binary will be taken apart. The SDK raises the cost of reading and tampering with the traffic, but the server makes every real decision: credentials, bans, expiry, PC locks and revocation. Anti-debugging, obfuscation and anti-VM are not part of the SDK.

  1. The request body (JSON) is encrypted with AES-256-GCM using a fresh 96-bit IV from BCryptGenRandom. The encryption’s additional data binds it to your app, key set and route, so a captured body can’t be replayed against another endpoint.
  2. The ciphertext, a timestamp and a 128-bit random nonce are signed with HMAC-SHA256 (a different key from the encryption key).
  3. The server refuses anything with a bad signature, a timestamp more than 30 seconds off, or a nonce it has seen before.

Before anything in a response is used, the SDK checks:

  • that it echoes the request’s nonce and is fresh,
  • its HMAC, in constant time,
  • its Ed25519 signature from EonAuth’s server key. The private key never leaves the server; your app holds only the public key, so a fake server can’t answer for your app even with the HMAC and encryption keys taken from your binary,
  • that it decrypts.

The SDK uses TLS 1.2 or 1.3 only and turns off redirects, cookies and HTTP auth. After the handshake and before the encrypted body is sent, it walks the certificate chain Windows validated and compares each certificate’s public key (SHA-256 SPKI) with its pins. No match means TlsPinMismatch and nothing is sent. A PC with an interception root installed therefore fails, even though Windows trusts that chain.

EonAuth runs on Cloudflare, which renews certificates every few months and can switch between the CAs it uses: Let’s Encrypt, Google Trust Services, SSL.com, and Sectigo for backup certificates. Pinning a leaf or intermediate would break your app at renewal, so the SDK pins the root keys of those four CAs (19 pins, cross-signing roots included). Renewals and CA switches keep working; chains ending anywhere else are refused.

  • Not covered: a certificate wrongly issued for the hostname by one of those CAs. That would show up in Certificate Transparency logs.
  • If Cloudflare adds a new CA, old builds would refuse to connect until updated. Keep a way to tell users to update outside your app (a website or launcher).
  • To pin something else, set Config::spkiPins. Any pin there replaces the default set.
  • InspectCertificateChain, or the console demo’s --print-pins HOST, shows a host’s chain and whether the pins accept it.

After Init, the HMAC key, the encryption key and the session token are held encrypted with CryptProtectMemory. Each is decrypted only for the moment it takes to sign or encrypt one request, then wiped with SecureZeroMemory. A crash dump, the page file or a heap scan usually doesn’t hold a usable key. A debugger stopped at the right moment still can; nothing running on the user’s PC can stop that. Call WipeSecrets(config) after Init to clear your own copy.

Passwords, keys and request bodies are wiped after use.

The session token lives in memory. With persistSession on, it is written only as a DPAPI blob (CryptProtectData, current Windows user) under %LOCALAPPDATA%, with extra entropy from the app ID and HWID. A copied file won’t decrypt for another user, PC or app. The file is deleted on sign-out and on any definitive refusal, and a resumed session is always re-checked with the server.

The SDK never passes tokens, keys or passwords to its logger. As a backstop, it redacts anything shaped like a license key and any long base64, base64url or hex run, in Debug and Release.

The hardware ID is SHA-256 over the baseboard serial (WMI), a CPU signature (per-core fields masked) and the system volume serial, mixed with your app ID. Raw identifiers never leave the PC, and the same PC has unrelated HWIDs in different apps.

HWIDs can change for honest reasons: a new motherboard, Windows reinstalled on a new drive, some virtual machines. That’s why the panel gives each key a limited number of HWID resets rather than none.

Release builds use Control Flow Guard and CET shadow-stack compatibility on top of DEP, ASLR and high-entropy addresses. Copy the process hardening lines into your own main.

Init refuses a configuration that could send secrets to the wrong place: a missing or placeholder app ID, keys of the wrong length, no response signing key, a host with a scheme, path, port or credentials, or plain HTTP to anything but localhost.