RSA signed · replay resistant · per application

CovertAuth protocol v2

Client authorization does not trust HTTP JSON by itself. Every license decision must carry a verifiable application-specific RSA proof.

Trust model

Do not embed an application secret or server API key in a desktop executable. Anything symmetric shipped to the client can eventually be extracted. CovertAuth instead keeps a unique RSA-3072 private key on the server and gives the client only the public verification key.

HTTPS still protects normal network traffic. The signature protects the authorization decision even if a local debugging proxy can inspect and modify TLS traffic. A modified assembly can of course remove verification; protecting the shipped assembly is the software publisher's responsibility.

License login / binding

Each application has a custom URL such as /a/my-product-a1b2c3/v2/. Copy the exact URL, Application ID, signing key ID and public key from the application's dashboard.

POST /a/my-product-a1b2c3/v2/license/activate
Content-Type: application/json

{
  "license_key": "CA-...",
  "hwid": "WIN-...",
  "challenge": "32-random-bytes-as-base64url"
}

The server returns only a signed proof for the authorization decision. A denial is signed too.

{
  "ok": true,
  "protocol": "covertauth-proof-v2",
  "proof": "header.payload.signature"
}

What the client MUST verify

  • RSA/SHA-256 signature using the public key pinned in the application.
  • kid equals the configured application signing key ID.
  • aud equals the expected Application ID.
  • Signed challenge equals the fresh random challenge sent for this request.
  • Signed license_digest equals SHA-256 of the exact license key entered locally.
  • Signed hwid_digest equals SHA-256 of the local HWID.
  • iat/exp are current and short-lived.
  • Only then may decision=allow unlock protected application behavior.
This prevents a proxy from changing a denial to allow, replaying an old success, swapping a fake key for another key, swapping the HWID, or using a valid proof from a different application.

HWID bindings & self rebind

There is no separate activation counter in v2. Licenses track actual bound devices only. Configure max_hwid_bindings and an optional rebind_after_seconds.

  • null: never self-rebind; administrator reset required.
  • 0: immediate self-rebind when slots are full.
  • Positive seconds: replace the least-recently-seen device after it has been inactive for that long.

Exact HWIDs are encrypted at rest for administrator visibility; matching is performed with an application-scoped HMAC fingerprint.

Privileged server API

Server API keys remain server-only and are never used by the WPF/Lua/client SDK. Example:

POST /api/v1/admin/licenses.php
Authorization: Bearer ca_sk_SERVER_ONLY

{
  "application_id": "ca_app_...",
  "action": "create",
  "label": "Customer 1042",
  "bind_hwid": true,
  "max_hwid_bindings": 2,
  "rebind_after_seconds": 86400
}

POST /api/v1/admin/licenses.php
{
  "application_id": "ca_app_...",
  "action": "reset_device",
  "license_id": 123,
  "device_id": 456
}

Legacy endpoints

/api/v1/license/validate.php and /api/v1/license/activate.php are disabled in v2. They return HTTP 410 so an unsigned license protocol cannot accidentally be shipped.

The existing username/password v1 APIs are retained for compatibility but should not be used as the sole unlock decision in an untrusted desktop client until they are moved to the signed v2 proof model.