Security model
The server is the trust boundary
Section titled “The server is the trust boundary”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.
Every request
Section titled “Every request”- 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. - The ciphertext, a timestamp and a 128-bit random nonce are signed with HMAC-SHA256 (a different key from the encryption key).
- The server refuses anything with a bad signature, a timestamp more than 30 seconds off, or a nonce it has seen before.
Every response
Section titled “Every response”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.
TLS pinning
Section titled “TLS pinning”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.
The default pins
Section titled “The default pins”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.
Secrets in memory
Section titled “Secrets in memory”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.
Saved sessions
Section titled “Saved sessions”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.
Binary hardening
Section titled “Binary hardening”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.
Configuration checks
Section titled “Configuration checks”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.