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:

  1. Create a ProofSeed struct containing a randomly generated u32 seed.
  2. Send the seed to the client in a SMSG_AUTH_CHALLENGE message.
  3. Receive the username, proof and seed in the CMSG_AUTH_SESSION message.
  4. Retrieve the session key from the login server.
  5. Create the HeaderCrypto struct through ProofSeed::into_header_crypto.
  6. Optionally, split the HeaderCrypto into EncrypterHalf and DecrypterHalf through HeaderCrypto::split.
  7. 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.

Traits

The Decrypter trait allows for decrypting world packet headers.

The Encrypter trait allows for decrypting world packet headers.