# Game IRC authentication and channel membership

Date: 2026-10-13. Static analysis of the installed Odyssey `EliteDangerous64.exe`, SHA-256 `e6be8bbe04e6a7ae226d4318945af7f367de13dc5a007a261964d9ba8144e988`. Reused the [saved Ghidra project](static-analysis-project.md), read-only, with automatic analysis disabled. No Frontier requests, game execution, client automation, configuration changes or live memory reads. [Extraction script](evidence/EDSIRC.java) records image identity, references, decompilations and bounded instruction/byte searches. Temporary function creation in `focused` mode is discarded on closing the read-only project. Initial sandbox/tool-launch failures produced no findings; successful evidence files came from the installed Ghidra launcher.

## Findings

The game implements IRC. This conclusion rests on command construction, CRLF serialization and receive dispatch, beyond the hostname and component-name strings. The service is named `IrcGameClient`.

| Finding | Static evidence |
| --- | --- |
| IRC hostname | `0x1409a7110` appends `irc.orerve.net` to the environment prefix. |
| Default port | `0x14082b050` writes `0x1a05` = 6661. Configuration can override hostname and port. |
| TLS transport | `0x140825840` selects the mbedTLS socket implementation for ports 6660-6665 inclusive, including default 6661. Other ports select `RawTcpSocket`. |
| Credential API | Constructor `0x1408208a0` creates GET `2.0/elite/comms/login`. |
| Credentials | `0x140824100` parses JSON `username` and `token`; both resulting strings must be nonempty. |
| IRC registration | `0x14082ffe0` queues `PASS`, `NICK`, then `USER`. |
| Line framing | `0x1408272b0` serializes command parameters and appends `0d 0a`. |
| Messages and keepalive | Receive dispatcher `0x140828ce0` handles `PRIVMSG`, `JOIN`, `PART`, `PING`, `ERROR`, `NOTICE`, `QUIT`, `MODE` and numeric replies. `PING` reaches the `PONG` builder `0x140829340`. |

Sources: [setup and credential response](evidence/game-irc-setup.txt), [registration and serializer](evidence/game-irc-authentication.txt), [literal bytes](evidence/game-irc-literals.txt), [constructor](evidence/game-irc-constructor.txt), [socket implementations](evidence/game-irc-transport.txt), and [raw socket operations](evidence/game-irc-send.txt).

The TLS writer `0x144b42550` references the embedded source path ending `mbedtls/library/ssl_tls.c` and writes through its SSL record routines. The raw alternative reaches Winsock `connect` and `sendto`. These are static transport branches; no handshake or selected runtime port was observed. Certificate loading/validation, pinning and accepted TLS versions remain untraced.

## Authentication

The credential request uses the central service's HTTP connection. Virtual slot `+0x70` resolves to `0x1409a5700`, returning the connection at interface offset `+0x150` (central object offset `+0x1a8`). The IRC hostname getter is the same interface's slot `+0xc8`. Evidence: [connection getter](evidence/game-irc-connection.txt), [central vtable](evidence/game-irc-central-vtable.txt), and [endpoint routing](game-server-routing.md).

The GET response consumer performs this conversion:

```text
username = response["username"]
password = text_until_first_NUL(hex_decode(response["token"]))
```

`0x140545120` converts character pairs using hexadecimal digit arithmetic; `0x140545810` wraps it and calls `0x1405571b0`, which copies bytes up to the first NUL or buffer length. The wrapper's recovered pseudocode omits a forwarded argument; [instructions](evidence/game-irc-auth-asm.txt) confirm the input survives in RDX. No hash or challenge-response transform appears in this conversion. This is not evidence that the launcher password or either launcher token is used as IRC `PASS`.

The registration builder uses the converted password for the `PASS` parameter and the returned username for `NICK`. Its `USER` record contains the username, a value constructed as `<username>@pc.elitedangerous.com`, the selected IRC hostname, and a trailing identity string built from other game-service values. The exact identity-field sources remain untraced. See [registration builder](evidence/game-irc-authentication.txt). The symbolic wire form is:

```text
PASS <decoded-token>\r\n
NICK <username>\r\n
USER <username> <username>@pc.elitedangerous.com <irc-host> :<identity>\r\n
```

Numeric reply 266 (`0x10a`) invokes the logged-in callback with boolean true; this is confirmed by instructions at `0x1408289ac`, where the decompiler incorrectly presents an indirect tail call. The callback `0x14082c8d0` requests PUT `2.0/elite/comms/login` with `serverAddr=<irc-host>`. Successful HTTP completion advances the connection state to 4. A refresh path submits the same PUT; the refresh interval is unknown. Source: [numeric-reply instructions](evidence/game-irc-auth-asm.txt), [callback](evidence/game-irc-constructor.txt), [PUT builder](evidence/game-irc-authentication.txt), and [state transitions](evidence/game-irc-setup.txt).

Registration in the traced path uses `PASS`/`NICK`/`USER`; no `CAP`, SASL `AUTHENTICATE` or NickServ exchange was identified in that path. Similar strings elsewhere in the binary are not evidence of their use by this IRC component.

## JOIN and PART

**The client receives IRC `JOIN` and `PART` and requests membership through separate HTTP APIs.** The evidence supports server-applied IRC membership, but does not expose the server implementation.

1. `0x141d9ee20` constructs PUT `2.0/elite/comms/join` and invokes `0x1434123a0`, whose identified query field is `userId`.
2. `0x141d600d0` constructs PUT `2.0/elite/comms/leave` and invokes `0x1434126c0`, with empty endpoint query/body in that builder.
3. Receive dispatcher `0x140828ce0` matches IRC `JOIN`. It extracts the nickname from the prefix before `!` and calls interface slot `+8`, mapped to `0x1408285d0`.
4. `0x1408285d0` compares the prefix nickname with the authenticated username. For the client's own nickname it records the channel and maps it to a `CommsGroup`; other users' JOINs take a separate branch.
5. IRC `PART` reaches `0x140828fd0`, then interface slot `+0x10`, mapped to `0x140829100`. The latter similarly checks the nickname and removes the client's channel/CommsGroup association.

The receive path is socket input -> queued lines -> `0x140829df0` -> parser `0x14082e970` -> dispatcher `0x140828ce0`. This establishes that the JOIN/PART handlers consume server messages, rather than local command submissions.

Sources: [join/leave HTTP construction](evidence/game-irc-join-leave-http.txt), [request builders](evidence/game-irc-membership-requests.txt), [incoming membership handlers](evidence/game-irc-membership.txt), [callback vtable](evidence/game-irc-membership-vtables.txt), and [channel mapping](evidence/game-irc-group-services.txt).

All analyzed references to the NUL-terminated `JOIN` and `PART` literals point to the receive dispatcher. No decoded instruction with the complete little-endian immediate `JOIN` or `PART` was found in `0x140820000..0x140834000`. These bounded negative searches do not rule out dynamically generated commands or paths elsewhere. Sources: [command references](evidence/game-irc-command-references.txt) and [immediate search](evidence/game-irc-membership-immediates.txt).

```mermaid
sequenceDiagram
    participant C as Game client
    participant A as Universe HTTP API
    participant I as IRC service
    C->>A: GET comms/login
    A-->>C: username, hex token
    C->>I: TLS connection; PASS, NICK, USER
    I-->>C: Numeric 266
    C->>A: PUT comms/login?serverAddr=...
    C->>A: PUT comms/join?userId=...
    Note over A,I: Membership coordination inferred; server implementation unknown
    I-->>C: :own-nick!... JOIN :channel
    C->>A: PUT comms/leave
    I-->>C: :own-nick!... PART channel
```

This diagram summarizes the supported design; it is not a captured exchange. Initial automatic joins, other users' membership, group-specific request triggers, channel permissions and ordering between HTTP completion and IRC notifications remain unresolved.

## Preservation implications and unknowns

A replacement needs the comms HTTP endpoints and IRC service to share identity and membership state. A general IRC server accepting `PASS` is insufficient to reproduce the observed client-side contract. Returning credentials, accepting registration, providing the handled login numeric, and delivering self JOIN/PART notifications are identified compatibility requirements; server-side enforcement remains an inference.

See [HTTP specification](../api/game-comms.md) and [IRC specification](../api/game-irc.md). Unresolved: concrete credential formats/lifetime, username identity semantics, exact USER identity fields, TLS trust/pinning, refresh schedule, full membership request triggers, channel namespace details, message payload extensions, and actual server responses. No live compatibility claim is made.
