Module wow_srp::header_crypto
source · [−]Expand description
Functionality for encrypting/decrypting World Packet headers.
For unknown reasons the session key obtained during the SRP6 exchange is used to “encrypt” packet headers. Be aware that Login Packets are not encrypted in this way.
The packet headers are different length depending on if they are client or server headers.
The sending party will encrypt the packets they send using an Encrypter and the receiving
party will decrypt with a Decrypter.
The HeaderCrypto
struct contains both and can be split with HeaderCrypto::split
.
The Typestate pattern is used
in order to prevent incorrect use.
This means that whenever the next step of computation takes place, you call a function
taking self
, consuming the old object, and returning the new object.
When a player connects to the world server, the server will need to send a seed value
in the SMSG_AUTH_CHALLENGE
message before the username has been received in the
CMSG_AUTH_SESSION
message.
This means the following workflow has to be done:
- Create a
ProofSeed
struct containing a randomly generatedu32
seed. - Send the seed to the client in a
SMSG_AUTH_CHALLENGE
message. - Receive the username, proof and seed in the
CMSG_AUTH_SESSION
message. - Retrieve the session key from the login server.
- Create the
HeaderCrypto
struct throughProofSeed::into_header_crypto
. - Optionally, split the
HeaderCrypto
intoEncrypterHalf
andDecrypterHalf
throughHeaderCrypto::split
. - Optionally, unsplit them through
EncrypterHalf::unsplit
.
In a diagram this would look like:
Optional
|
| |-> EncrypterHalf -|
ProofSeed -> HeaderCrypto -|---| |--> HeaderCrypto
| |-> DecrypterHalf -|
|
Example
After establishing a successful connection to the world server individual headers can be encrypted or decrypted through a few different means:
use std::io::{Read, Error, Write};
use wow_srp::header_crypto::{HeaderCrypto, Decrypter, ServerHeader, Encrypter, ProofSeed};
use std::convert::TryInto;
use wow_srp::{SESSION_KEY_LENGTH, PROOF_LENGTH};
use wow_srp::normalized_string::NormalizedString;
fn establish_connection(username: NormalizedString,
session_key: [u8; SESSION_KEY_LENGTH as _],
client_proof: [u8; PROOF_LENGTH as _],
client_seed: u32) {
let seed = ProofSeed::new();
// Send seed to client
seed.seed();
// Get username from client, fetch session key from login server
let encryption = seed.into_header_crypto(&username, session_key, client_proof, client_seed);
// Send the first server message
}
fn decrypt_header<R: Read>(r: &mut R, raw_data: &mut [u8], encryption: &mut HeaderCrypto) {
let client_header = encryption.read_and_decrypt_server_header(r);
match client_header {
Ok(c) => {}
Err(_) => {
panic!("Reader error")
}
}
// OR
let header = raw_data[0..6].try_into().unwrap();
let client_header = encryption.decrypt_server_header(header);
// OR
encryption.decrypt(raw_data);
}
fn encrypt<W: Write>(w: &mut W, raw_data: &mut [u8], encryption: &mut HeaderCrypto) {
let result = encryption.write_encrypted_server_header(w, 4, 0xFF);
match result {
Ok(_) => {}
Err(_) => {
panic!("Reader error")
}
}
// OR
let server_header = encryption.encrypt_server_header(4, 0xFF);
// Send server_header
// OR
encryption.encrypt(raw_data);
}
Structs
Decrypted values from a client.
Decryption part of a HeaderCrypto
.
Encryption part of a HeaderCrypto
.
Main struct for encryption or decryption.
Random Seed part of the calculation needed to verify that a client knows the session key.
Decrypted values from a server.
Constants
Size in bytes of the client world packet header.
Size in bytes of the server world packet header.