Authentication - Kyoril/mmo GitHub Wiki

How authentication works

This page will feature, how client authentication works in this project. This project uses SRP6a for authentication, which means, that the password hash is never sent over the network. The generated session key is also never sent to the client nor sent by the client.

Login server authentication

  • The client connects to the login server
  • On successful connection, client sends LogonChallenge packet with:
    • Client version
    • Machine infos (Platform, OS)
    • Client Locale
    • Account Name
  • Login server does calculations and answers with LogonChallenge packet with:
    • Result Code (enum auth::AuthResult)
    • B (srp6a calculated variable)
    • g (srp6a constant)
    • N (srp6a constant)
    • s (salt for the given account name as in the database)
  • Client does calculations and answers with LogonProof packet with:
    • A (srp6a calculated variable)
    • M1 (calculated hash, will have to match server-calculated M1 hash variable)
  • Server does calculations and checks, and answers with LogonProof packet with:
    • Result (auth::AuthResult enum)
    • M2 (calculated hash, will have to match client-calculated M2 hash variable)
  • Server sends RealmList packet with:
    • Realm Count
    • For each realm:
      • Realm ID
      • Realm Name
      • Realm address

By now, the login server will have written a session key to the database for the given account, and the client will also have calculated the session key (it is not sent directly via network). The client is now able to see the realm list and choose a realm to connect to.

Then, the client will have to authenticate again at the realm.

Realm server authentication

The client needs to authenticate again at the realm server, in order to get access to the accounts character data etc. This works as follows:

  • The client connects to a realm server
  • On successful connection, the realm generates a random seed value and sends a AuthChallenge packet with:
    • Server Seed
  • The client generates a seed itself, stores the server seed and answers with a AuthSession packet with:
    • Build version
    • Account name
    • Client seed
    • Client hash (a hash which is built out of the account name, server seed, client seed and session key)

The client hash is important for verifying that the client has a valid session key without actually sending that session key to the realm.

  • The realm sends a ClientAuthSession packet to the login server with:
    • An incrementing id (request id)
    • Account name
    • Client seed
    • Realm seed
    • Client hash
  • The login server loads the session key from the database and rebuilds the hash itself, then compares it to the sent client hash
  • If both hashes match, authentication is successful, and the login servers answers with ClientAuthSessionResponse with:
    • Request id
    • Result (auth::AuthResult enum)
    • Session key binary data (uint16 length, binary uint8 data)
  • The realm now, in case of success, answers with an AuthSessionResponse packet, with:
    • Result (auth::AuthResult)
  • Both the client and the realm now initialize the packet header encryption for their connection, which makes identifying packet ids and sizes difficult to impossible using packet sniffers.

From here on, the realm and the game client can communicate using the game_protocol library with encryption enabled.

Possible attacks?

In theory, a client could try to connect to a realm directly. However, in order to do so, he would need to know the current session key which was generated on the last login of that account, and he would need to know this before anouther login attempt is made at the login server for this account. Then, he could bypass the authentication in theory.

A possible solution to this would be to force a session key refresh in intervals of 12-24 hours. Session keys have a huge size and are hard- to nearly impossible to guess, especially in such a short timeframe.