Implement seed exchange algorithm for 2.7.50+

This commit is contained in:
Nobody 2022-05-28 23:10:13 +05:00
parent 88657e7d11
commit 9dace4f236

View File

@ -1,5 +1,10 @@
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::rsa::Padding;
use openssl::sign::Signer;
use std::sync::mpsc; use std::sync::mpsc;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto;
use prost::Message; use prost::Message;
@ -8,6 +13,7 @@ use crate::server::IpcMessage;
use packet_processor_macro::*; use packet_processor_macro::*;
#[macro_use] #[macro_use]
use packet_processor::*; use packet_processor::*;
use crate::DispatchServer;
#[packet_processor(GetPlayerTokenReq)] #[packet_processor(GetPlayerTokenReq)]
pub struct AuthManager { pub struct AuthManager {
@ -33,15 +39,77 @@ impl AuthManager {
} }
pub fn process_get_player_token(&mut self, conv: u32, metadata: &proto::PacketHead, req: &proto::GetPlayerTokenReq, rsp: &mut proto::GetPlayerTokenRsp) { pub fn process_get_player_token(&mut self, conv: u32, metadata: &proto::PacketHead, req: &proto::GetPlayerTokenReq, rsp: &mut proto::GetPlayerTokenRsp) {
let seed: u64 = 0x123456789ABCDEF0; // TODO: use real value! let seed: u64 = 0xBABECAFEF00D; // TODO: use real value!
let client_hardcoded_seed: u64 = 0x12345678;
let uid = self.get_uid_by_account_id(req.account_uid.parse().unwrap()); let uid = self.get_uid_by_account_id(req.account_uid.parse().unwrap());
rsp.account_type = req.account_type; rsp.account_type = req.account_type;
rsp.account_uid = req.account_uid.clone(); rsp.account_uid = req.account_uid.clone();
rsp.token = req.account_token.clone(); rsp.token = req.account_token.clone();
rsp.secret_key_seed = 0;//seed; // TODO: temporary workaround! rsp.secret_key_seed = seed; // TODO: temporary workaround!
rsp.uid = uid; rsp.uid = uid;
if req.unk4 > 0 { // TODO: detect client version properly!
// Versions 2.7.5x+ use different algorithm for key initialization
// TODO: as of now (2022-05-16) this algorithm here is more of a PoC, because we can't really sign the data
// or decrypt the client seed we're getting from the client.
//
// Connecting to our server still requires patching the client to disable signature verification and hardcoding
// some known client seed value. This will allow the patched client to connect to official servers (beware of
// the ban for modding the client!)
//
// An alternative approach to hardcoding the client seed would be to employ RCE in WindSeedClientNotify to extract
// the seed from the client itself. That still would require patching the client though (to allow invalid signatures),
// so it's of a very little difference to us.
//
// Another alternative is to replace keys inside global-metadata.dat file, but that requires writing an encryption
// tool. While still possible, it's tiresome, and won't allow patched client to connect to official server without
// switching back and forth between two versions of global-metadata.dat file.
let key_id = req.unk4 as u8;
let rsa_key_collection = DispatchServer::load_rsa_keys("RSAConfig");
let keys = match rsa_key_collection.get(&key_id) {
Some(keys) => keys,
None => panic!("Unknown key ID {}!", key_id),
};
// Decrypt received client seed
let client_seed_encrypted = base64::decode(&req.unk3).unwrap();
let mut dec_buf: Vec<u8> = vec![0; 256];
let client_seed = match keys.signing_key.private_decrypt(&client_seed_encrypted, &mut dec_buf, Padding::PKCS1) {
Ok(seed_size) => {
// Note: from_be_bytes here, because client seems to swap order of bytes for the seed
u64::from_be_bytes(dec_buf[0..seed_size].try_into().unwrap())
},
Err(e) => { // TODO: must panic here!
println!("Error decrypting client seed: {}", e);
client_hardcoded_seed // TODO: temporary workaround!
},
};
// Encrypt server seed which we'll use in negotiating with the client
let mut enc_buf: Vec<u8> = vec![0; 256];
// Note: to_be_bytes here, because client seems to swap order of bytes for the seed
let seed_bytes = (seed ^ client_seed).to_be_bytes();
let len = keys.encrypt_key.public_encrypt(&seed_bytes, &mut enc_buf, Padding::PKCS1).unwrap();
// Sign it
let keypair = PKey::from_rsa(keys.signing_key.clone()).unwrap();
let mut signer = Signer::new(MessageDigest::sha256(), &keypair).unwrap();
let signature = signer.sign_oneshot_to_vec(&seed_bytes).unwrap();
rsp.unk5 = base64::encode(&enc_buf);
rsp.unk6 = base64::encode(&signature);
}
self.conv_to_user.insert(conv, uid); self.conv_to_user.insert(conv, uid);
self.user_to_conv.insert(uid, conv); self.user_to_conv.insert(uid, conv);
} }