Manual/Transport & Bootstrap/Gate-server RSA+AES handshake (GSL)
v1.0.364 notes · build 2026-11-07 12:10 UTC

Gate-server RSA+AES handshake (GSL)

RSA-PKCS1v15 + AES-256-ECB · salt-derived key · exact field order
#crypto #gsl

The GSL (getserverlist.php) envelope is hybrid RSA + AES-256-ECB. Key correction: C# Mode = (CipherMode)2 is ECB, not CBC — .NET ordinals are CBC=1, ECB=2, OFB=3, CFB=4, CTS=5; verified behaviorally (same plaintext+key → byte-identical ciphertext; no IV exists).

Flow

  1. Random 20-char salt (84-char alphabet, non-crypto System.Random; charset/PRNG irrelevant to the server).
  2. Salt RSA-PKCS1v1.5-encrypted against the server pubkey (check-version resMsg, Base64 DER SubjectPublicKeyInfo, per-session) → base64 → URL-safe → uuid POST field.
  3. AES key = ASCII(lowercase_hex(MD5(salt))) — 32 ASCII hex bytes, not the raw 16-byte digest.
  4. Form body → AES-256-ECB-PKCS7 → base64 → URL-safe → data POST field (source shows KeySize=128 but .NET's Key setter overrides → effective 256-bit).
  5. Response: non-empty top-level bin field decrypted with the same salt-derived key, re-parsed as the same shape.

Parameters: RSA PKCS#1 v1.5 (OAEP explicitly disabled); AES-256 (RijndaelManaged); ECB/PKCS7, no IV; URL-safe base64 (+→-, /→_, strip =).

Inner plaintext form body

Fields joined & without URL-encoding, 1.0.364 UnityWebRequest order static: uuid=<deviceId>, airKey, loginFlag=1, country, is3D=1, lang, simOp, platform, newServer=1, isSimulator, zone=<cached zone>, gameuid=<gameUid>, openCountry=<storefront>, [opt, loginKey | rt]. Inner uuid = device id; the outer uuid POST field = the RSA-encrypted salt. BestHTTP sends the same fields with newServer after gameuid. platform = "Android"/"iOS".

Notes

  • A clean-room implementation needs: x509.ParsePKIXPublicKey(der) directly (no PEM); rsa.EncryptPKCS1v15; key = hex(md5(salt)); a hand-rolled ECB loop (Go stdlib has none) with PKCS7; POST {gate}/gameservice/getserverlist.php body uuid=...&data=...; decrypt response.bin the same way.
  • The same AESDecrypt helper is reused with a hardcoded key for the server-pushed Lua snippet init.add.data, executed via GameEntry.Lua.Env.DoString (eval-equivalent), result returned to the server in login.ext — an integrity challenge (key deliberately not published here). Elsewhere the client uses a proper AES-CBC-with-explicit-IV helper (Zendesk webchat URL), so ECB on GSL is a deliberate choice, not a general missing-IV bug.
notes build 2026-11-07 12:10 UTC · lw-notes@9c47e1b · sources: own capture/analysis cross-checked against the public dossier