void DS::CryptEstablish(uint8_t* seed, uint8_t* key, const uint8_t* N, const uint8_t* K, uint8_t* Y) { BIGNUM* bn_Y = BN_new(); BIGNUM* bn_N = BN_new(); BIGNUM* bn_K = BN_new(); BIGNUM* bn_seed = BN_new(); BN_CTX* ctx = BN_CTX_new(); /* Random 7-byte server seed */ init_rand(); RAND_bytes(reinterpret_cast<unsigned char*>(seed), 7); /* client = Y ^ K % N */ BN_bin2bn(reinterpret_cast<const unsigned char*>(Y), 64, bn_Y); BN_bin2bn(reinterpret_cast<const unsigned char*>(N), 64, bn_N); BN_bin2bn(reinterpret_cast<const unsigned char*>(K), 64, bn_K); DS_PASSERT(!BN_is_zero(bn_N)); BN_mod_exp(bn_seed, bn_Y, bn_K, bn_N, ctx); /* Apply server seed for establishing crypt state with client */ uint8_t keybuf[64]; DS_DASSERT(BN_num_bytes(bn_seed) <= 64); size_t outBytes = BN_bn2bin(bn_seed, reinterpret_cast<unsigned char*>(keybuf)); BYTE_SWAP_BUFFER(keybuf, outBytes); for (size_t i=0; i<7; ++i) key[i] = keybuf[i] ^ seed[i]; BN_free(bn_Y); BN_free(bn_N); BN_free(bn_K); BN_free(bn_seed); BN_CTX_free(ctx); }
void game_client_init(GameClient_Private& client) { /* Game client header: size, account uuid, age instance uuid */ uint32_t size = DS::RecvValue<uint32_t>(client.m_sock); DS_PASSERT(size == 36); DS::Uuid clientUuid, connUuid; DS::RecvBuffer(client.m_sock, clientUuid.m_bytes, sizeof(clientUuid.m_bytes)); DS::RecvBuffer(client.m_sock, connUuid.m_bytes, sizeof(connUuid.m_bytes)); /* Establish encryption */ uint8_t msgId = DS::RecvValue<uint8_t>(client.m_sock); DS_PASSERT(msgId == DS::e_CliToServConnect); uint8_t msgSize = DS::RecvValue<uint8_t>(client.m_sock); DS_PASSERT(msgSize == 66); uint8_t Y[64]; DS::RecvBuffer(client.m_sock, Y, 64); BYTE_SWAP_BUFFER(Y, 64); uint8_t serverSeed[7]; uint8_t sharedKey[7]; DS::CryptEstablish(serverSeed, sharedKey, DS::Settings::CryptKey(DS::e_KeyGame_N), DS::Settings::CryptKey(DS::e_KeyGame_K), Y); client.m_buffer.truncate(); client.m_buffer.write<uint8_t>(DS::e_ServToCliEncrypt); client.m_buffer.write<uint8_t>(9); client.m_buffer.writeBytes(serverSeed, 7); DS::SendBuffer(client.m_sock, client.m_buffer.buffer(), client.m_buffer.size()); client.m_crypt = DS::CryptStateInit(sharedKey, 7); }
void gate_init(GateKeeper_Private& client) { /* Gate Keeper header: size, null uuid */ uint32_t size = DS::RecvValue<uint32_t>(client.m_sock); if (size != 20) throw DS::InvalidConnectionHeader(); DS::Uuid uuid; DS::RecvBuffer(client.m_sock, uuid.m_bytes, sizeof(uuid.m_bytes)); /* Reply header */ client.m_buffer.truncate(); client.m_buffer.write<uint8_t>(DS::e_ServToCliEncrypt); /* Establish encryption, and write reply body */ uint8_t msgId = DS::RecvValue<uint8_t>(client.m_sock); if (msgId != DS::e_CliToServConnect) throw DS::InvalidConnectionHeader(); uint8_t msgSize = DS::RecvValue<uint8_t>(client.m_sock); if (msgSize == 2) { // no seed... client wishes unencrypted connection (that's okay, nobody // else can "fake" us as nobody has the private key, so if the client // actually wants encryption it will only work with the correct peer) client.m_buffer.write<uint8_t>(2); // reply with an empty seed as well } else { uint8_t Y[64]; memset(Y, 0, sizeof(Y)); if (msgSize > 66) throw DS::InvalidConnectionHeader(); DS::RecvBuffer(client.m_sock, Y, 64 - (66 - msgSize)); BYTE_SWAP_BUFFER(Y, 64); uint8_t serverSeed[7]; uint8_t sharedKey[7]; DS::CryptEstablish(serverSeed, sharedKey, DS::Settings::CryptKey(DS::e_KeyGate_N), DS::Settings::CryptKey(DS::e_KeyGate_K), Y); client.m_crypt = DS::CryptStateInit(sharedKey, 7); client.m_buffer.write<uint8_t>(9); client.m_buffer.writeBytes(serverSeed, 7); } /* send reply */ DS::SendBuffer(client.m_sock, client.m_buffer.buffer(), client.m_buffer.size()); }
void game_client_init(GameClient_Private& client) { /* Game client header: size, account uuid, age instance uuid */ uint32_t size = DS::RecvValue<uint32_t>(client.m_sock); DS_PASSERT(size == 36); DS::Uuid clientUuid, connUuid; DS::RecvBuffer(client.m_sock, clientUuid.m_bytes, sizeof(clientUuid.m_bytes)); DS::RecvBuffer(client.m_sock, connUuid.m_bytes, sizeof(connUuid.m_bytes)); /* Reply header */ client.m_buffer.truncate(); client.m_buffer.write<uint8_t>(DS::e_ServToCliEncrypt); /* Establish encryption, and write reply body */ uint8_t msgId = DS::RecvValue<uint8_t>(client.m_sock); DS_PASSERT(msgId == DS::e_CliToServConnect); uint8_t msgSize = DS::RecvValue<uint8_t>(client.m_sock); if (msgSize == 2) { // no seed... client wishes unencrypted connection (that's okay, nobody // else can "fake" us as nobody has the private key, so if the client // actually wants encryption it will only work with the correct peer) client.m_buffer.write<uint8_t>(2); // reply with an empty seed as well } else { uint8_t Y[64]; DS_PASSERT(msgSize == 66); DS::RecvBuffer(client.m_sock, Y, 64); BYTE_SWAP_BUFFER(Y, 64); uint8_t serverSeed[7]; uint8_t sharedKey[7]; DS::CryptEstablish(serverSeed, sharedKey, DS::Settings::CryptKey(DS::e_KeyGame_N), DS::Settings::CryptKey(DS::e_KeyGame_K), Y); client.m_crypt = DS::CryptStateInit(sharedKey, 7); client.m_buffer.write<uint8_t>(9); client.m_buffer.writeBytes(serverSeed, 7); } /* send reply */ DS::SendBuffer(client.m_sock, client.m_buffer.buffer(), client.m_buffer.size()); }