# Game HTTP encryption and headers

Date: 2026-10-13. Offline analysis of the installed Odyssey binary. Scope: shared central-server HTTP submission and optional encryption wrapper. No game execution, Frontier requests or live memory reads.

Follow-up: [offline saved-capture decoding](capture-decryption.md) validated the documented key and transport rules on 659 encrypted messages. Four messages were plaintext, and seven HTTP 204 responses had no captured body despite encryption headers. The decoder only read local capture files.

## Findings

The public connection configures an application key before endpoint submission. Requests through the game wrapper use ChaCha20 and standard Base64; wrapper responses reverse those layers, validate an `EDDE` marker and decompress an LZ4 block before text parsing. TLS is a separate outer layer. Edserver login sets the direct-HTTP flag and bypasses this wrapper, so its query and JSON response do not use these extra layers.

## Hard-coded key

Extracted key: `52381239578582178380088936356181`.

This is **32 ASCII bytes embedded in the game executable**, at preferred virtual address `0x145132a08`, PE file offset `0x5131e08`. It is not a decimal number or a hex-encoded key. Builder `0x1409b2690` copies this literal when constructing the public connection, stores it at connection `+0x78`, and supplies it to request encryption and response decryption. Cipher setup reads the bytes directly as eight little-endian uint32 words.

The traced setup does not obtain this key from login or derive it from launcher tokens. Request nonces are generated separately; response nonces come from response headers. This finding applies to the identified executable, not every released version.

Evidence: [file-backed key literal and cipher](evidence/game-http-cipher-static.txt), [direct extraction from the installed PE file](evidence/game-http-key-file.json), and [builder copying the literal into the connection](evidence/game-http-request-static.txt). The installed file's SHA-256 was checked and matches the saved analysis artifact.

The complete reusable wire rules and header tables are maintained in [shared game HTTP transport](../api/game-http-transport.md). Endpoint-specific fields are maintained in [game authentication](../api/game-authentication.md).

## Finding ledger

| Finding | Evidence anchor | Confidence/coverage |
| --- | --- | --- |
| Public HTTPS/443 and fixed application key | `0x1409b2690`, connection factory `0x1405c2a00`, constants `0x1450ba354` and `0x145132a08` | Confirmed static data flow; internal branch has empty key. |
| Shared query fields, auth tokens, headers | `0x1409a9d90`, `0x1409aec00` | Confirmed for these wrappers; plugin/modifier inventory incomplete. |
| Encrypt query/body as one stream, then split | `0x1409aec00`, `0x140989600`, `0x140546520` | Confirmed static ranges and Base64 encoder. |
| Standard ChaCha20, counter zero | `0x140545900`, `0x14055e240`, `0x14055cf20` | State constants, 80 quarter-rounds and independent block comparison. |
| Nonce is 12 ASCII hex characters | `0x1409aec00`, `0x14055c5d0` | PRNG output formatted `%016llx`, first 12 characters; cipher reads bytes directly. |
| Base64 then decrypt before interpreting response | `0x1409a7800`, `0x1405456b0`, `0x1405575c0` | Confirmed receive pipeline. |
| `Nonce`, `uncompressedSize`, `EDDE`, skip 8 bytes | `0x1409a7800` | Confirmed headers, magic and frame offsets; [bytes 4-7 are little-endian uncompressed length](edde-frame-length.md), confirmed by 325 saved responses. Wrapper skips that DWORD. |
| LZ4 block decompression | `0x1409b7090`, `0x1409b70d0` | Algorithm identified from token/literal/match decoding; no server-frame oracle test. |
| Diagnostics and status/retry handling | `0x1409a3130`, `0x1409a7290` | Confirmed branches; user-visible effects of all flags not resolved. |

## Cipher validation

[Verification script](evidence/verify-game-chacha.cjs) mechanically evaluates the recovered unrolled block operations as uint32 arithmetic. It compares outputs against Node's OpenSSL-backed `chacha20` cipher for 32 synthetic key/nonce/counter combinations. All 32 matched; the recovered function contains 80 quarter-rounds, corresponding to 20 rounds.

The oracle's 16-byte IV is constructed as little-endian 32-bit counter followed by the 12 nonce bytes, matching the recovered initial state for one block. This check does not imply identical counter overflow semantics over extremely long streams. No target executable was run. The test uses generated data, not live credentials.

Result: [cipher verification output](evidence/game-chacha-verification.json). Run from the repository root with Node using `node docs/investigations/evidence/verify-game-chacha.cjs`.

## Reproduction and limitations

The [extraction script](evidence/EDSLogin.java) accepts `p:<address>` for decompilation, `x:<address>` for references, `q:<address>:<count>` for pointer data, `s:<address>:<length>` for file-backed string regions, and `i:<address>:<count>` for instructions. It writes source addresses and program SHA-256. Reuse [the saved project](static-analysis-project.md) with `-readonly -noanalysis`; do not execute the game or import it as a runtime target.

Ghidra initially encountered sandbox restrictions compiling local scripts. The successful offline runs used workspace-local settings/cache and the saved project. Temporary disassembly/function creation occurred only in a read-only analysis session and was discarded; the game image was unchanged.

REA could not open the `.gpr` as a binary. These are preserved local Ghidra excerpts, not REA Evidence IDs. Decompiler warnings, inferred types and unresolved indirect calls must remain separate from confirmed byte/string observations.

Follow-up: [EDDE frame length](edde-frame-length.md) identifies bytes 4-7 from 325 saved responses and excludes standard CRC32 over the compressed or decompressed bodies. Saved encrypted responses also establish successful LZ4 decoding; see [capture validation](capture-decryption.md).

Remaining questions: complete callback/modifier coverage, network-service variants, per-build key differences, and end-to-end replacement-server acceptance.

## Evidence and artifact

- [Request construction, routing and URL normalization](evidence/game-http-request-static.txt).
- [Cipher initialization/block function and Base64 encoding](evidence/game-http-cipher-static.txt).
- [Response wrapper, Base64 decoding and LZ4 decoder](evidence/game-http-response-static.txt).

Target: `Products/elite-dangerous-odyssey-64/EliteDangerous64.exe`; SHA-256 `e6be8bbe04e6a7ae226d4318945af7f367de13dc5a007a261964d9ba8144e988`; Ghidra 12.1.4; preferred base `0x140000000`. Findings describe this exact artifact.
