This crate handles the low-level networking, cryptography, and request/response mapping of the LSX protocol, providing an asynchronous API for connecting, authenticating, and interacting with the Origin (EA Desktop) backend.
use origin_sdk::{protocol::game::GetAllGameInfo, sdk::OriginSdk};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to the Origin SDK server
let (client, _) = OriginSdk::connect("127.0.0.1:3216").await.unwrap();
let game_info = client.send_request(GetAllGameInfo {}).await.unwrap();
println!("{:#?}", game_info);
// GetAllGameInfoResponse {
// up_to_date: true,
// languages: "de_DE,en_US,es_ES,es_MX,fr_FR,it_IT,ja_JP,ko_KR,pl_PL,pt_BR,ru_RU,zh_CN,zh_TW",
// free_trial: false,
// full_game_purchased: true,
// full_game_released: true,
// full_game_release_date: "0000-00-00T00:00:48",
// expiration: "0000-00-00T00:00:08",
// system_time: "2025-09-03T12:15:54",
// has_expiration: false,
// installed_version: "",
// installed_language: "en_US",
// available_version: "0.0.0.0",
// display_name: "Knockout City™",
// max_group_size: 16,
// entitlement_source: "EPIC",
// }
Ok(())
}
All request types are defined in src/protocol
and are organized by domain:
achievements.rs
auth.rs
broadcast.rs
chat.rs
- etc.
Each request implements the RequestResponse
trait, which is generated by the request_response!
macro.
This ensures requests are automatically mapped to their corresponding ResponseBody
variant:
pub enum RequestBody {
// ...
GetProfile(GetProfile)
// ...
}
pub enum ResponseBody {
// ...
GetProfileResponse(GetProfileResponse),
// ...
}
request_response! {
// ...
GetProfile => GetProfileResponse,
// ...
}
// This call will be automatically deserialized into a `GetProfileResponse`
let profile = client.send_request(GetProfile { /* ... */ }).await?;
The LSX protocol uses a custom AES-128-ECB scheme with deterministic key derivation. The session key is derived through a challenge handshake with the server.
- AES-128-ECB + PKCS#7 padding is used for encryption and decryption
- Session keys are generated from integer seeds and are expanded into 16-byte arrays
- Pseudo-random number generator is used to produce bytes from the seed
- The server sends a
Challenge
event with a random hex string key - The client encrypts this ASCII challenge string using AES-128-ECB with the default key
- The encrypted data is hex-encoded into a string
- The first two bytes of the hex string as ASCII bytes are combined into a u16 seed
seed = (byte[0] << 8) | byte[1]
- The derived seed is used to generate a new AES key and it becomes the session key for all subsequent encryption/decryption operations
- The client sends a
ChallengeResponse
request back to the server, which includes:- The original challenge key
- The encrypted + hex-encoded response key
- SDK metadata (protocol version, SDK version, etc.)
- The server sends a
ChallengeAccepted
response, both sides now share the same AES key
Warranty Voider
for releasing LSX-Dumper and publishing information about the protocol on various forumsBergmann89
for releasing xsd-parser, which helped with LSX model generation
This project is a third-party reimplementation of the Origin SDK, based on reverse-engineering and observations of Origin SDK versions 9.12.1.7 and 10.6.1.8, intended for educational and research purposes only.
It is not affiliated with, endorsed by, or supported by Electronic Arts.