eveofdarkness.org

The current client plays

An earlier post ended on a loose thread. We had recorded the current official client — version 1.130 — talking to an official server, and worked out that it encrypts its traffic from the very first packet, using a key it already carries. The old scheme in the server source, where the server hands the client a key, simply isn't what 1.130 does.

This post is the other end of that thread. We got it working: a current, fully-patched official client logging in and reaching the character screen against OpenDAoC, encrypted the whole way.

Why it matters

Back in the spring, the live DAoC client was updated to 1.130, and part of that update was encryption that's on from the first byte. Community servers — "freeshards" — have run for years by accepting the older 1.127 client, which happily talks in the clear. The new client doesn't offer that option, so a stock, up-to-date official client couldn't connect to a freeshard at all. Working out the 1.130 handshake closes that gap: the current client can play on a community server, with the same encrypted, tamper-resistant connection it uses on the official one.

The wrong assumption

If you remember CapnBry's Excalibur or Odin's Eye, you know the earliest clients used a plain XOR, not real cryptographic protection. Later clients moved to RSA plus a modified RC4 stream cipher, and the server code freeshards run still carries that machinery — with a switch to turn it off, which is how the unencrypted 1.127 setup works. So we assumed 1.130 was just that same encryption with the off switch removed, and started there.

That isn't what it's doing. The RC4 stream cipher survived — it's the same one already in the old server. The key exchange didn't. We expected the old scheme, where the server hands the client a key, and went looking for it; it isn't there. 1.130 reverses the direction — the client brings its own key and sends it to the server, encrypted. The packet log says as much on its own: the first packet is already ciphertext and the server sent no key, so the key is in the client.

Version axes

"1.65 server" and "1.130 client" in the same sentence trips people up. They're different axes:

  • The client / wire protocol (1.127, 1.130, …) is the language the game.dll speaks on the network. It's set by whichever client build you run.
  • The ruleset / content era ("1.65", "classic", "Shrouded Isles") is what the world is — classes, zones, RvR rules. That lives in the server's code and database, not the client.
  • The server software (OpenDAoC, with its own date-based release numbers) and its database are a third and fourth axis again.

So a modern 1.130 client can play a classic 1.65 world: the client just draws and talks; the server decides what world you're in. The only thing that had to change to let the new client connect was the network language — the protocol — not the ruleset. That's the piece we worked on.

The 1.130 handshake

Put the guesses aside; here's what we see on the wire, from our own recordings against our own server (behaviour only — no client internals):

  • The client connects and announces its version. The server answers with 0x22 and the string 1.130b.
  • The client then sends a key-transport message: the key it brought, encrypted under the server's public key (RSA, with OAEP padding) and chunked across the packet. So there's an RSA keypair in play now — the private half lives on the server, the public half with the client.
  • From there, both sides use that key as a stream cipher — the same modified RC4 the old server already had — on every packet, both directions.
  • Underneath the encryption, the login, character and realm packets are the same ones the 1.127 client uses. The encryption is the new wrapper; the conversation inside it is familiar.

Once the server can decrypt that first message and set up the cipher, the ordinary flow takes over: log in, pick a realm, see your characters. We watched it happen, decrypted, in our server's log — eodtest1 logging on, character screen, a stable encrypted session.

Realm selection

The first thing after login was a single realm's character list, with the in-client "Realm" button dead. Nothing to do with encryption: OpenDAoC by default locks an account to one realm per server (a classic RvR rule) and tells the client so, which disables the chooser. Flip the server's "all realms" setting and the realm-select screen comes back and the button works. The odd behaviour was the server answering exactly what the client asked, not a protocol bug.

What's next

We're preparing the server-side 1.130 support as a contribution back to the OpenDAoC project, so any community server can accept the current official client if it wants to. The appeal is simple: players get to use the client they already have, patched to current, with its encrypted connection — rather than hunting down an older build.

This has always been a parallel effort to the Godot client: one track building a new, open client; the other understanding the protocol well enough to improve the server and bring the modern client into the fold. They feed each other.

This is interoperability work on a client we own, written up as what we observed on the wire. The protocol findings are public; the client-side specifics stay private.