/* * This is the source code of tgnet library v. 1.1 * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * * Copyright Nikolai Kudashov, 2015-2018. */ #include #include #include #include #include #include #include #include #include "Handshake.h" #include "FileLog.h" #include "Datacenter.h" #include "ConnectionsManager.h" #include "MTProtoScheme.h" #include "ApiScheme.h" #include "BuffersStorage.h" #include "NativeByteBuffer.h" #include "Config.h" #include "Connection.h" thread_local static std::vector serverPublicKeys; thread_local static std::vector serverPublicKeysFingerprints; thread_local static std::map cdnPublicKeys; thread_local static std::map cdnPublicKeysFingerprints; thread_local static std::vector cdnWaitingDatacenters; thread_local static bool loadingCdnKeys = false; thread_local static BN_CTX *bnContext = nullptr; thread_local static Config *cdnConfig = nullptr; Handshake::Handshake(Datacenter *datacenter, HandshakeType type, HandshakeDelegate *handshakeDelegate) { currentDatacenter = datacenter; handshakeType = type; delegate = handshakeDelegate; } Handshake::~Handshake() { cleanupHandshake(); } void Handshake::beginHandshake(bool reconnect) { DEBUG_D("dc%u handshake: begin, type = %d", currentDatacenter->datacenterId, handshakeType); cleanupHandshake(); Connection *connection = getConnection(); handshakeState = 1; if (reconnect) { connection->suspendConnection(); connection->connect(); } #ifdef USE_OLD_KEYS TL_req_pq *request = new TL_req_pq(); request->nonce = std::unique_ptr(new ByteArray(16)); RAND_bytes(request->nonce->bytes, 16); authNonce = new ByteArray(request->nonce.get()); sendRequestData(request, true); #else TL_req_pq_multi *request = new TL_req_pq_multi(); request->nonce = std::unique_ptr(new ByteArray(16)); RAND_bytes(request->nonce->bytes, 16); authNonce = new ByteArray(request->nonce.get()); sendRequestData(request, true); #endif } void Handshake::cleanupHandshake() { handshakeState = 0; if (handshakeRequest != nullptr) { delete handshakeRequest; handshakeRequest = nullptr; } if (handshakeServerSalt != nullptr) { delete handshakeServerSalt; handshakeServerSalt = nullptr; } if (authNonce != nullptr) { delete authNonce; authNonce = nullptr; } if (authServerNonce != nullptr) { delete authServerNonce; authServerNonce = nullptr; } if (authNewNonce != nullptr) { delete authNewNonce; authNewNonce = nullptr; } if (handshakeAuthKey != nullptr) { delete handshakeAuthKey; handshakeAuthKey = nullptr; } if (authKeyTempPending != nullptr) { delete authKeyTempPending; authKeyTempPending = nullptr; } if (authKeyPendingMessageId != 0 || authKeyPendingRequestId != 0) { ConnectionsManager::getInstance(currentDatacenter->instanceNum).cancelRequestInternal(authKeyPendingRequestId, authKeyPendingMessageId, false, false); authKeyPendingMessageId = 0; authKeyPendingRequestId = 0; } authKeyTempPendingId = 0; } inline Connection *Handshake::getConnection() { return handshakeType == HandshakeTypeMediaTemp ? currentDatacenter->createGenericMediaConnection() : currentDatacenter->createGenericConnection(); } void Handshake::sendRequestData(TLObject *object, bool important) { uint32_t messageLength = object->getObjectSize(); NativeByteBuffer *buffer = BuffersStorage::getInstance().getFreeBuffer(20 + messageLength); buffer->writeInt64(0); buffer->writeInt64(ConnectionsManager::getInstance(currentDatacenter->instanceNum).generateMessageId()); buffer->writeInt32(messageLength); object->serializeToStream(buffer); getConnection()->sendData(buffer, false, false); if (important) { if (handshakeRequest != object) { if (handshakeRequest != nullptr) { delete handshakeRequest; } handshakeRequest = object; } } else { delete object; } } inline uint64_t gcd(uint64_t a, uint64_t b) { while (a != 0 && b != 0) { while ((b & 1) == 0) { b >>= 1; } while ((a & 1) == 0) { a >>= 1; } if (a > b) { a -= b; } else { b -= a; } } return b == 0 ? a : b; } inline bool factorizeValue(uint64_t what, uint32_t &p, uint32_t &q) { int32_t it = 0, i, j; uint64_t g = 0; for (i = 0; i < 3 || it < 1000; i++) { uint64_t t = ((lrand48() & 15) + 17) % what; uint64_t x = (long long) lrand48() % (what - 1) + 1, y = x; int32_t lim = 1 << (i + 18); for (j = 1; j < lim; j++) { ++it; uint64_t a = x, b = x, c = t; while (b) { if (b & 1) { c += a; if (c >= what) { c -= what; } } a += a; if (a >= what) { a -= what; } b >>= 1; } x = c; uint64_t z = x < y ? what + x - y : x - y; g = gcd(z, what); if (g != 1) { break; } if (!(j & (j - 1))) { y = x; } } if (g > 1 && g < what) { break; } } if (g > 1 && g < what) { p = (uint32_t) g; q = (uint32_t) (what / g); if (p > q) { uint32_t tmp = p; p = q; q = tmp; } return true; } else { DEBUG_E("factorization failed for %" PRIu64, what); p = 0; q = 0; return false; } } inline bool check_prime(BIGNUM *p) { int result = 0; if (!BN_primality_test(&result, p, BN_prime_checks, bnContext, 0, NULL)) { DEBUG_E("OpenSSL error at BN_primality_test"); return false; } return result != 0; } inline bool isGoodPrime(BIGNUM *p, uint32_t g) { if (g < 2 || g > 7 || BN_num_bits(p) != 2048) { return false; } BIGNUM *t = BN_new(); BIGNUM *dh_g = BN_new(); if (!BN_set_word(dh_g, 4 * g)) { DEBUG_E("OpenSSL error at BN_set_word(dh_g, 4 * g)"); BN_free(t); BN_free(dh_g); return false; } if (!BN_mod(t, p, dh_g, bnContext)) { DEBUG_E("OpenSSL error at BN_mod"); BN_free(t); BN_free(dh_g); return false; } uint64_t x = BN_get_word(t); if (x >= 4 * g) { DEBUG_E("OpenSSL error at BN_get_word"); BN_free(t); BN_free(dh_g); return false; } BN_free(dh_g); bool result = true; switch (g) { case 2: if (x != 7) { result = false; } break; case 3: if (x % 3 != 2) { result = false; } break; case 5: if (x % 5 != 1 && x % 5 != 4) { result = false; } break; case 6: if (x != 19 && x != 23) { result = false; } break; case 7: if (x % 7 != 3 && x % 7 != 5 && x % 7 != 6) { result = false; } break; default: break; } char *prime = BN_bn2hex(p); static const char *goodPrime = "c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c3720fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f642477fe96bb2a941d5bcd1d4ac8cc49880708fa9b378e3c4f3a9060bee67cf9a4a4a695811051907e162753b56b0f6b410dba74d8a84b2a14b3144e0ef1284754fd17ed950d5965b4b9dd46582db1178d169c6bc465b0d6ff9ca3928fef5b9ae4e418fc15e83ebea0f87fa9ff5eed70050ded2849f47bf959d956850ce929851f0d8115f635b105ee2e4e15d04b2454bf6f4fadf034b10403119cd8e3b92fcc5b"; if (!strcasecmp(prime, goodPrime)) { OPENSSL_free(prime); BN_free(t); return true; } OPENSSL_free(prime); if (!result || !check_prime(p)) { BN_free(t); return false; } BIGNUM *b = BN_new(); if (!BN_set_word(b, 2)) { DEBUG_E("OpenSSL error at BN_set_word(b, 2)"); BN_free(b); BN_free(t); return false; } if (!BN_div(t, 0, p, b, bnContext)) { DEBUG_E("OpenSSL error at BN_div"); BN_free(b); BN_free(t); return false; } if (!check_prime(t)) { result = false; } BN_free(b); BN_free(t); return result; } inline bool isGoodGaAndGb(BIGNUM *g_a, BIGNUM *p) { if (BN_num_bytes(g_a) > 256 || BN_num_bits(g_a) < 2048 - 64 || BN_cmp(p, g_a) <= 0) { return false; } BIGNUM *dif = BN_new(); BN_sub(dif, p, g_a); if (BN_num_bits(dif) < 2048 - 64) { BN_free(dif); return false; } BN_free(dif); return true; } void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) { if (handshakeState == 0) { return; } const std::type_info &typeInfo = typeid(*message); if (typeInfo == typeid(TL_resPQ)) { if (handshakeState != 1) { sendAckRequest(messageId); return; } handshakeState = 2; TL_resPQ *result = (TL_resPQ *) message; if (authNonce->isEqualTo(result->nonce.get())) { std::string key; int64_t keyFingerprint = 0; size_t count1 = result->server_public_key_fingerprints.size(); if (currentDatacenter->isCdnDatacenter) { std::map::iterator iter = cdnPublicKeysFingerprints.find(currentDatacenter->datacenterId); if (iter != cdnPublicKeysFingerprints.end()) { for (uint32_t a = 0; a < count1; a++) { if ((uint64_t) result->server_public_key_fingerprints[a] == iter->second) { keyFingerprint = iter->second; key = cdnPublicKeys[currentDatacenter->datacenterId]; } } } } else { if (serverPublicKeys.empty()) { #ifdef USE_OLD_KEYS serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6\n" "lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS\n" "an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw\n" "Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+\n" "8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n\n" "Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB\n" "-----END RSA PUBLIC KEY-----"); serverPublicKeysFingerprints.push_back(0xc3b42b026ce86b21LL); serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt\n" "ksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru\n" "vCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L\n" "xI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi\n" "XvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp\n" "NTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB\n" "-----END RSA PUBLIC KEY-----"); serverPublicKeysFingerprints.push_back(0x9a996a1db11c729bLL); serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+\n" "DyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB\n" "1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s\n" "g1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z\n" "hRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f\n" "x5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB\n" "-----END RSA PUBLIC KEY-----"); serverPublicKeysFingerprints.push_back(0xb05b2a6f70cdea78LL); serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa\n" "xDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i\n" "qAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc\n" "/n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks\n" "WV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t\n" "UiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB\n" "-----END RSA PUBLIC KEY-----"); serverPublicKeysFingerprints.push_back(0x71e025b6c76033e3LL); #endif serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAruw2yP/BCcsJliRoW5eBVBVle9dtjJw+OYED160Wybum9SXtBBLX\n" "riwt4rROd9csv0t0OHCaTmRqBcQ0J8fxhN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/\n" "j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvdl84Kd9ORYjDEAyFnEA7dD556OptgLQQ2\n" "e2iVNq8NZLYTzLp5YpOdO1doK+ttrltggTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnS\n" "Lj16yE5HvJQn0CNpRdENvRUXe6tBP78O39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wF\n" "XGF710w9lwCGNbmNxNYhtIkdqfsEcwR5JwIDAQAB\n" "-----END RSA PUBLIC KEY-----"); serverPublicKeysFingerprints.push_back(0xbc35f3509f7b7a5LL); serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAvfLHfYH2r9R70w8prHblWt/nDkh+XkgpflqQVcnAfSuTtO05lNPs\n" "pQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOOKPi0OfJXoRVylFzAQG/j83u5K3kRLbae\n" "7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ3TDS2pQOCtovG4eDl9wacrXOJTG2990V\n" "jgnIKNA0UMoP+KF03qzryqIt3oTvZq03DyWdGK+AZjgBLaDKSnC6qD2cFY81UryR\n" "WOab8zKkWAnhw2kFpcqhI0jdV5QaSCExvnsjVaX0Y1N0870931/5Jb9ICe4nweZ9\n" "kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV/wIDAQAB\n" "-----END RSA PUBLIC KEY-----"); serverPublicKeysFingerprints.push_back(0x15ae5fa8b5529542LL); serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAs/ditzm+mPND6xkhzwFIz6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGr\n" "zqTDHkO30R8VeRM/Kz2f4nR05GIFiITl4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+\n" "th6knSU0yLtNKuQVP6voMrnt9MV1X92LGZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvS\n" "Uwwc+yi1/gGaybwlzZwqXYoPOhwMebzKUk0xW14htcJrRrq+PXXQbRzTMynseCoP\n" "Ioke0dtCodbA3qQxQovE16q9zz4Otv2k4j63cz53J+mhkVWAeWxVGI0lltJmWtEY\n" "K6er8VqqWot3nqmWMXogrgRLggv/NbbooQIDAQAB\n" "-----END RSA PUBLIC KEY-----"); serverPublicKeysFingerprints.push_back(0xaeae98e13cd7f94fLL); serverPublicKeys.push_back("-----BEGIN RSA PUBLIC KEY-----\n" "MIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q05shjg8/4p6047bn6/m8yPy1RBsvIyvuD\n" "uGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xbnfxL5BXHplJhMtADXKM9bWB11PU1Eioc\n" "3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvi\n" "fRLJbY08/Gp66KpQvy7g8w7VB8wlgePexW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqe\n" "Pji9NP3tJUFQjcECqcm0yV7/2d0t/pbCm+ZH1sadZspQCEPPrtbkQBlvHb4OLiIW\n" "PGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6MAQIDAQAB\n" "-----END RSA PUBLIC KEY-----"); serverPublicKeysFingerprints.push_back(0x5a181b2235057d98LL); } size_t count2 = serverPublicKeysFingerprints.size(); for (uint32_t a = 0; a < count1; a++) { for (uint32_t b = 0; b < count2; b++) { if ((uint64_t) result->server_public_key_fingerprints[a] == serverPublicKeysFingerprints[b]) { keyFingerprint = result->server_public_key_fingerprints[a]; key = serverPublicKeys[a]; break; } } if (keyFingerprint != 0) { break; } } } if (keyFingerprint == 0) { if (currentDatacenter->isCdnDatacenter) { DEBUG_D("dc%u handshake: can't find valid cdn server public key, type = %d", currentDatacenter->datacenterId, handshakeType); loadCdnConfig(currentDatacenter); } else { DEBUG_E("dc%u handshake: can't find valid server public key, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); } return; } authServerNonce = new ByteArray(result->server_nonce.get()); uint64_t pq = ((uint64_t) (result->pq->bytes[0] & 0xff) << 56) | ((uint64_t) (result->pq->bytes[1] & 0xff) << 48) | ((uint64_t) (result->pq->bytes[2] & 0xff) << 40) | ((uint64_t) (result->pq->bytes[3] & 0xff) << 32) | ((uint64_t) (result->pq->bytes[4] & 0xff) << 24) | ((uint64_t) (result->pq->bytes[5] & 0xff) << 16) | ((uint64_t) (result->pq->bytes[6] & 0xff) << 8) | ((uint64_t) (result->pq->bytes[7] & 0xff)); uint32_t p, q; if (!factorizeValue(pq, p, q)) { beginHandshake(false); return; } TL_req_DH_params *request = new TL_req_DH_params(); request->nonce = std::unique_ptr(new ByteArray(authNonce)); request->server_nonce = std::unique_ptr(new ByteArray(authServerNonce)); request->p = std::unique_ptr(new ByteArray(4)); request->p->bytes[3] = (uint8_t) p; request->p->bytes[2] = (uint8_t) (p >> 8); request->p->bytes[1] = (uint8_t) (p >> 16); request->p->bytes[0] = (uint8_t) (p >> 24); request->q = std::unique_ptr(new ByteArray(4)); request->q->bytes[3] = (uint8_t) q; request->q->bytes[2] = (uint8_t) (q >> 8); request->q->bytes[1] = (uint8_t) (q >> 16); request->q->bytes[0] = (uint8_t) (q >> 24); request->public_key_fingerprint = keyFingerprint; TLObject *innerData; if (handshakeType == HandshakeTypePerm) { TL_p_q_inner_data_dc *tl_p_q_inner_data = new TL_p_q_inner_data_dc(); tl_p_q_inner_data->nonce = std::unique_ptr(new ByteArray(authNonce)); tl_p_q_inner_data->server_nonce = std::unique_ptr(new ByteArray(authServerNonce)); tl_p_q_inner_data->pq = std::unique_ptr(new ByteArray(result->pq.get())); tl_p_q_inner_data->p = std::unique_ptr(new ByteArray(request->p.get())); tl_p_q_inner_data->q = std::unique_ptr(new ByteArray(request->q.get())); tl_p_q_inner_data->new_nonce = std::unique_ptr(new ByteArray(32)); if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).testBackend) { tl_p_q_inner_data->dc = 10000 + currentDatacenter->datacenterId; } else { tl_p_q_inner_data->dc = currentDatacenter->datacenterId; } RAND_bytes(tl_p_q_inner_data->new_nonce->bytes, 32); authNewNonce = new ByteArray(tl_p_q_inner_data->new_nonce.get()); innerData = tl_p_q_inner_data; } else { TL_p_q_inner_data_temp_dc *tl_p_q_inner_data_temp = new TL_p_q_inner_data_temp_dc(); tl_p_q_inner_data_temp->nonce = std::unique_ptr(new ByteArray(authNonce)); tl_p_q_inner_data_temp->server_nonce = std::unique_ptr(new ByteArray(authServerNonce)); tl_p_q_inner_data_temp->pq = std::unique_ptr(new ByteArray(result->pq.get())); tl_p_q_inner_data_temp->p = std::unique_ptr(new ByteArray(request->p.get())); tl_p_q_inner_data_temp->q = std::unique_ptr(new ByteArray(request->q.get())); tl_p_q_inner_data_temp->new_nonce = std::unique_ptr(new ByteArray(32)); /*if (handshakeType == HandshakeTypeMediaTemp) { if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).testBackend) { tl_p_q_inner_data_temp->dc = -(10000 + currentDatacenter->datacenterId); } else { tl_p_q_inner_data_temp->dc = -currentDatacenter->datacenterId; } } else {*/ if (ConnectionsManager::getInstance(currentDatacenter->instanceNum).testBackend) { tl_p_q_inner_data_temp->dc = 10000 + currentDatacenter->datacenterId; } else { tl_p_q_inner_data_temp->dc = currentDatacenter->datacenterId; } //} tl_p_q_inner_data_temp->expires_in = TEMP_AUTH_KEY_EXPIRE_TIME; RAND_bytes(tl_p_q_inner_data_temp->new_nonce->bytes, 32); authNewNonce = new ByteArray(tl_p_q_inner_data_temp->new_nonce.get()); innerData = tl_p_q_inner_data_temp; } uint32_t innerDataSize = innerData->getObjectSize(); uint32_t additionalSize = innerDataSize + SHA_DIGEST_LENGTH < 255 ? 255 - (innerDataSize + SHA_DIGEST_LENGTH) : 0; NativeByteBuffer *innerDataBuffer = BuffersStorage::getInstance().getFreeBuffer(innerDataSize + additionalSize + SHA_DIGEST_LENGTH); innerDataBuffer->position(SHA_DIGEST_LENGTH); innerData->serializeToStream(innerDataBuffer); delete innerData; SHA1(innerDataBuffer->bytes() + SHA_DIGEST_LENGTH, innerDataSize, innerDataBuffer->bytes()); if (additionalSize != 0) { RAND_bytes(innerDataBuffer->bytes() + SHA_DIGEST_LENGTH + innerDataSize, additionalSize); } BIO *keyBio = BIO_new(BIO_s_mem()); BIO_write(keyBio, key.c_str(), (int) key.length()); RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL); BIO_free(keyBio); if (bnContext == nullptr) { bnContext = BN_CTX_new(); } BIGNUM *a = BN_bin2bn(innerDataBuffer->bytes(), innerDataBuffer->limit(), NULL); BIGNUM *r = BN_new(); BN_mod_exp(r, a, rsaKey->e, rsaKey->n, bnContext); uint32_t size = BN_num_bytes(r); ByteArray *rsaEncryptedData = new ByteArray(size >= 256 ? size : 256); size_t resLen = BN_bn2bin(r, rsaEncryptedData->bytes); if (256 - resLen > 0) { memset(rsaEncryptedData->bytes + resLen, 0, 256 - resLen); } BN_free(a); BN_free(r); RSA_free(rsaKey); innerDataBuffer->reuse(); request->encrypted_data = std::unique_ptr(rsaEncryptedData); sendAckRequest(messageId); sendRequestData(request, true); } else { DEBUG_E("dc%u handshake: invalid client nonce, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); } } else if (dynamic_cast(message)) { if (typeInfo == typeid(TL_server_DH_params_ok)) { if (handshakeState != 2) { sendAckRequest(messageId); return; } handshakeState = 3; TL_server_DH_params_ok *result = (TL_server_DH_params_ok *) message; NativeByteBuffer *tmpAesKeyAndIv = BuffersStorage::getInstance().getFreeBuffer(84); NativeByteBuffer *newNonceAndServerNonce = BuffersStorage::getInstance().getFreeBuffer(authNewNonce->length + authServerNonce->length); newNonceAndServerNonce->writeBytes(authNewNonce); newNonceAndServerNonce->writeBytes(authServerNonce); SHA1(newNonceAndServerNonce->bytes(), newNonceAndServerNonce->limit(), tmpAesKeyAndIv->bytes()); newNonceAndServerNonce->reuse(); NativeByteBuffer *serverNonceAndNewNonce = BuffersStorage::getInstance().getFreeBuffer(authServerNonce->length + authNewNonce->length); serverNonceAndNewNonce->writeBytes(authServerNonce); serverNonceAndNewNonce->writeBytes(authNewNonce); SHA1(serverNonceAndNewNonce->bytes(), serverNonceAndNewNonce->limit(), tmpAesKeyAndIv->bytes() + 20); serverNonceAndNewNonce->reuse(); NativeByteBuffer *newNonceAndNewNonce = BuffersStorage::getInstance().getFreeBuffer(authNewNonce->length + authNewNonce->length); newNonceAndNewNonce->writeBytes(authNewNonce); newNonceAndNewNonce->writeBytes(authNewNonce); SHA1(newNonceAndNewNonce->bytes(), newNonceAndNewNonce->limit(), tmpAesKeyAndIv->bytes() + 40); newNonceAndNewNonce->reuse(); memcpy(tmpAesKeyAndIv->bytes() + 60, authNewNonce->bytes, 4); Datacenter::aesIgeEncryption(result->encrypted_answer->bytes, tmpAesKeyAndIv->bytes(), tmpAesKeyAndIv->bytes() + 32, false, false, result->encrypted_answer->length); bool hashVerified = false; for (uint32_t i = 0; i < 16; i++) { SHA1(result->encrypted_answer->bytes + SHA_DIGEST_LENGTH, result->encrypted_answer->length - i - SHA_DIGEST_LENGTH, tmpAesKeyAndIv->bytes() + 64); if (!memcmp(tmpAesKeyAndIv->bytes() + 64, result->encrypted_answer->bytes, SHA_DIGEST_LENGTH)) { hashVerified = true; break; } } if (!hashVerified) { DEBUG_E("dc%u handshake: can't decode DH params, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); return; } bool error = false; NativeByteBuffer *answerWithHash = new NativeByteBuffer(result->encrypted_answer->bytes + SHA_DIGEST_LENGTH, result->encrypted_answer->length - SHA_DIGEST_LENGTH); uint32_t constructor = answerWithHash->readUint32(&error); TL_server_DH_inner_data *dhInnerData = TL_server_DH_inner_data::TLdeserialize(answerWithHash, constructor, currentDatacenter->instanceNum, error); delete answerWithHash; if (error) { DEBUG_E("dc%u handshake: can't parse decoded DH params, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); return; } if (!authNonce->isEqualTo(dhInnerData->nonce.get())) { DEBUG_E("dc%u handshake: invalid DH nonce, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); return; } if (!authServerNonce->isEqualTo(dhInnerData->server_nonce.get())) { DEBUG_E("dc%u handshake: invalid DH server nonce, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); return; } BIGNUM *p = BN_bin2bn(dhInnerData->dh_prime->bytes, dhInnerData->dh_prime->length, NULL); if (p == nullptr) { DEBUG_E("can't allocate BIGNUM p"); exit(1); } if (!isGoodPrime(p, dhInnerData->g)) { DEBUG_E("dc%u handshake: bad prime, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); BN_free(p); return; } BIGNUM *g_a = BN_new(); if (g_a == nullptr) { DEBUG_E("can't allocate BIGNUM g_a"); exit(1); } BN_bin2bn(dhInnerData->g_a->bytes, dhInnerData->g_a->length, g_a); if (!isGoodGaAndGb(g_a, p)) { DEBUG_E("dc%u handshake: bad prime and g_a, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); BN_free(p); BN_free(g_a); return; } BIGNUM *g = BN_new(); if (g == nullptr) { DEBUG_E("can't allocate BIGNUM g"); exit(1); } if (!BN_set_word(g, dhInnerData->g)) { DEBUG_E("OpenSSL error at BN_set_word(g_b, dhInnerData->g)"); beginHandshake(false); BN_free(g); BN_free(g_a); BN_free(p); return; } thread_local static uint8_t bytes[256]; RAND_bytes(bytes, 256); BIGNUM *b = BN_bin2bn(bytes, 256, NULL); if (b == nullptr) { DEBUG_E("can't allocate BIGNUM b"); exit(1); } BIGNUM *g_b = BN_new(); if (!BN_mod_exp(g_b, g, b, p, bnContext)) { DEBUG_E("OpenSSL error at BN_mod_exp(g_b, g, b, p, bnContext)"); beginHandshake(false); BN_free(g); BN_free(g_a); BN_free(g_b); BN_free(b); BN_free(p); return; } TL_client_DH_inner_data *clientInnerData = new TL_client_DH_inner_data(); clientInnerData->g_b = std::unique_ptr(new ByteArray(BN_num_bytes(g_b))); BN_bn2bin(g_b, clientInnerData->g_b->bytes); clientInnerData->nonce = std::unique_ptr(new ByteArray(authNonce)); clientInnerData->server_nonce = std::unique_ptr(new ByteArray(authServerNonce)); clientInnerData->retry_id = 0; BN_free(g_b); BN_free(g); BIGNUM *authKeyNum = BN_new(); BN_mod_exp(authKeyNum, g_a, b, p, bnContext); size_t l = BN_num_bytes(authKeyNum); handshakeAuthKey = new ByteArray(256); BN_bn2bin(authKeyNum, handshakeAuthKey->bytes); if (l < 256) { memmove(handshakeAuthKey->bytes + 256 - l, handshakeAuthKey->bytes, l); memset(handshakeAuthKey->bytes, 0, 256 - l); } BN_free(authKeyNum); BN_free(g_a); BN_free(b); BN_free(p); uint32_t clientInnerDataSize = clientInnerData->getObjectSize(); uint32_t additionalSize = (clientInnerDataSize + SHA_DIGEST_LENGTH) % 16; if (additionalSize != 0) { additionalSize = 16 - additionalSize; } NativeByteBuffer *clientInnerDataBuffer = BuffersStorage::getInstance().getFreeBuffer(clientInnerDataSize + additionalSize + SHA_DIGEST_LENGTH); clientInnerDataBuffer->position(SHA_DIGEST_LENGTH); clientInnerData->serializeToStream(clientInnerDataBuffer); delete clientInnerData; SHA1(clientInnerDataBuffer->bytes() + SHA_DIGEST_LENGTH, clientInnerDataSize, clientInnerDataBuffer->bytes()); if (additionalSize != 0) { RAND_bytes(clientInnerDataBuffer->bytes() + SHA_DIGEST_LENGTH + clientInnerDataSize, additionalSize); } TL_set_client_DH_params *setClientDhParams = new TL_set_client_DH_params(); setClientDhParams->nonce = std::unique_ptr(new ByteArray(authNonce)); setClientDhParams->server_nonce = std::unique_ptr(new ByteArray(authServerNonce)); Datacenter::aesIgeEncryption(clientInnerDataBuffer->bytes(), tmpAesKeyAndIv->bytes(), tmpAesKeyAndIv->bytes() + 32, true, false, clientInnerDataBuffer->limit()); setClientDhParams->encrypted_data = std::unique_ptr(new ByteArray(clientInnerDataBuffer->bytes(), clientInnerDataBuffer->limit())); clientInnerDataBuffer->reuse(); tmpAesKeyAndIv->reuse(); sendAckRequest(messageId); sendRequestData(setClientDhParams, true); int32_t currentTime = (int32_t) (ConnectionsManager::getInstance(currentDatacenter->instanceNum).getCurrentTimeMillis() / 1000); timeDifference = dhInnerData->server_time - currentTime; handshakeServerSalt = new TL_future_salt(); handshakeServerSalt->valid_since = currentTime + timeDifference - 5; handshakeServerSalt->valid_until = handshakeServerSalt->valid_since + 30 * 60; for (int32_t a = 7; a >= 0; a--) { handshakeServerSalt->salt <<= 8; handshakeServerSalt->salt |= (authNewNonce->bytes[a] ^ authServerNonce->bytes[a]); } } else { DEBUG_E("dc%u handshake: can't set DH params, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); } } else if (dynamic_cast(message)) { if (handshakeState != 3) { sendAckRequest(messageId); return; } handshakeState = 4; Set_client_DH_params_answer *result = (Set_client_DH_params_answer *) message; if (!authNonce->isEqualTo(result->nonce.get())) { DEBUG_E("dc%u handshake: invalid DH answer nonce, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); return; } if (!authServerNonce->isEqualTo(result->server_nonce.get())) { DEBUG_E("dc%u handshake: invalid DH answer server nonce, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); return; } sendAckRequest(messageId); uint32_t authKeyAuxHashLength = authNewNonce->length + SHA_DIGEST_LENGTH + 1; NativeByteBuffer *authKeyAuxHashBuffer = BuffersStorage::getInstance().getFreeBuffer(authKeyAuxHashLength + SHA_DIGEST_LENGTH); authKeyAuxHashBuffer->writeBytes(authNewNonce); SHA1(handshakeAuthKey->bytes, handshakeAuthKey->length, authKeyAuxHashBuffer->bytes() + authNewNonce->length + 1); if (typeInfo == typeid(TL_dh_gen_ok)) { authKeyAuxHashBuffer->writeByte(1); SHA1(authKeyAuxHashBuffer->bytes(), authKeyAuxHashLength - 12, authKeyAuxHashBuffer->bytes() + authKeyAuxHashLength); if (memcmp(result->new_nonce_hash1->bytes, authKeyAuxHashBuffer->bytes() + authKeyAuxHashLength + SHA_DIGEST_LENGTH - 16, 16)) { DEBUG_E("dc%u handshake: invalid DH answer nonce hash 1, type = %d", currentDatacenter->datacenterId, handshakeType); authKeyAuxHashBuffer->reuse(); beginHandshake(false); } else { DEBUG_D("dc%u handshake: completed, time difference = %d, type = %d", currentDatacenter->datacenterId, timeDifference, handshakeType); authKeyAuxHashBuffer->position(authNewNonce->length + 1 + 12); authKeyTempPendingId = authKeyAuxHashBuffer->readInt64(nullptr); authKeyAuxHashBuffer->reuse(); if (handshakeRequest != nullptr) { delete handshakeRequest; handshakeRequest = nullptr; } std::unique_ptr salt = std::unique_ptr(handshakeServerSalt); currentDatacenter->addServerSalt(salt); handshakeServerSalt = nullptr; if (handshakeType == HandshakeTypePerm) { ConnectionsManager::getInstance(currentDatacenter->instanceNum).scheduleTask([&] { ByteArray *authKey = handshakeAuthKey; handshakeAuthKey = nullptr; delegate->onHandshakeComplete(this, authKeyTempPendingId, authKey, timeDifference); }); } else { authKeyTempPending = handshakeAuthKey; handshakeAuthKey = nullptr; Connection *connection = getConnection(); TL_auth_bindTempAuthKey *request = new TL_auth_bindTempAuthKey(); request->initFunc = [&, request, connection](int64_t messageId) { TL_bind_auth_key_inner *inner = new TL_bind_auth_key_inner(); inner->expires_at = ConnectionsManager::getInstance(currentDatacenter->instanceNum).getCurrentTime() + timeDifference + TEMP_AUTH_KEY_EXPIRE_TIME; inner->perm_auth_key_id = currentDatacenter->authKeyPermId; inner->temp_auth_key_id = authKeyTempPendingId; RAND_bytes((uint8_t *) &inner->nonce, 8); inner->temp_session_id = connection->getSessionId(); NetworkMessage *networkMessage = new NetworkMessage(); networkMessage->message = std::unique_ptr(new TL_message()); networkMessage->message->msg_id = authKeyPendingMessageId = messageId; networkMessage->message->bytes = inner->getObjectSize(); networkMessage->message->body = std::unique_ptr(inner); networkMessage->message->seqno = 0; std::vector> array; array.push_back(std::unique_ptr(networkMessage)); request->perm_auth_key_id = inner->perm_auth_key_id; request->nonce = inner->nonce; request->expires_at = inner->expires_at; request->encrypted_message = currentDatacenter->createRequestsData(array, nullptr, connection, true); }; authKeyPendingRequestId = ConnectionsManager::getInstance(currentDatacenter->instanceNum).sendRequest(request, [&](TLObject *response, TL_error *error, int32_t networkType) { authKeyPendingMessageId = 0; authKeyPendingRequestId = 0; if (response != nullptr && typeid(*response) == typeid(TL_boolTrue)) { DEBUG_D("dc%u handshake: bind completed", currentDatacenter->datacenterId); ConnectionsManager::getInstance(currentDatacenter->instanceNum).scheduleTask([&] { ByteArray *authKey = authKeyTempPending; authKeyTempPending = nullptr; delegate->onHandshakeComplete(this, authKeyTempPendingId, authKey, timeDifference); }); } else { ConnectionsManager::getInstance(currentDatacenter->instanceNum).scheduleTask([&] { beginHandshake(true); }); } }, nullptr, RequestFlagWithoutLogin | RequestFlagEnableUnauthorized | RequestFlagUseUnboundKey, currentDatacenter->datacenterId, connection->getConnectionType(), true, 0); } } } else if (typeInfo == typeid(TL_dh_gen_retry)) { authKeyAuxHashBuffer->writeByte(2); SHA1(authKeyAuxHashBuffer->bytes(), authKeyAuxHashLength - 12, authKeyAuxHashBuffer->bytes() + authKeyAuxHashLength); if (memcmp(result->new_nonce_hash2->bytes, authKeyAuxHashBuffer->bytes() + authKeyAuxHashLength + SHA_DIGEST_LENGTH - 16, 16)) { DEBUG_E("dc%u handshake: invalid DH answer nonce hash 2, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); } else { DEBUG_D("dc%u handshake: retry DH, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); } authKeyAuxHashBuffer->reuse(); } else if (typeInfo == typeid(TL_dh_gen_fail)) { authKeyAuxHashBuffer->writeByte(3); SHA1(authKeyAuxHashBuffer->bytes(), authKeyAuxHashLength - 12, authKeyAuxHashBuffer->bytes() + authKeyAuxHashLength); if (memcmp(result->new_nonce_hash3->bytes, authKeyAuxHashBuffer->bytes() + authKeyAuxHashLength + SHA_DIGEST_LENGTH - 16, 16)) { DEBUG_E("dc%u handshake: invalid DH answer nonce hash 3, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); } else { DEBUG_E("dc%u handshake: server declined DH params, type = %d", currentDatacenter->datacenterId, handshakeType); beginHandshake(false); } authKeyAuxHashBuffer->reuse(); } } } void Handshake::sendAckRequest(int64_t messageId) { TL_msgs_ack *msgsAck = new TL_msgs_ack(); msgsAck->msg_ids.push_back(messageId); sendRequestData(msgsAck, false); } TLObject *Handshake::getCurrentHandshakeRequest() { return handshakeRequest; } void Handshake::saveCdnConfigInternal(NativeByteBuffer *buffer) { buffer->writeInt32(1); buffer->writeInt32((int32_t) cdnPublicKeys.size()); for (std::map::iterator iter = cdnPublicKeys.begin(); iter != cdnPublicKeys.end(); iter++) { buffer->writeInt32(iter->first); buffer->writeString(iter->second); buffer->writeInt64(cdnPublicKeysFingerprints[iter->first]); } } void Handshake::saveCdnConfig(Datacenter *datacenter) { if (cdnConfig == nullptr) { cdnConfig = new Config(datacenter->instanceNum, "cdnkeys.dat"); } thread_local static NativeByteBuffer *sizeCalculator = new NativeByteBuffer(true); sizeCalculator->clearCapacity(); saveCdnConfigInternal(sizeCalculator); NativeByteBuffer *buffer = BuffersStorage::getInstance().getFreeBuffer(sizeCalculator->capacity()); saveCdnConfigInternal(buffer); cdnConfig->writeConfig(buffer); buffer->reuse(); } void Handshake::loadCdnConfig(Datacenter *datacenter) { if (std::find(cdnWaitingDatacenters.begin(), cdnWaitingDatacenters.end(), datacenter) != cdnWaitingDatacenters.end()) { return; } cdnWaitingDatacenters.push_back(datacenter); if (loadingCdnKeys) { return; } if (cdnPublicKeysFingerprints.empty()) { if (cdnConfig == nullptr) { cdnConfig = new Config(datacenter->instanceNum, "cdnkeys.dat"); } NativeByteBuffer *buffer = cdnConfig->readConfig(); if (buffer != nullptr) { uint32_t version = buffer->readUint32(nullptr); if (version >= 1) { size_t count = buffer->readUint32(nullptr); for (uint32_t a = 0; a < count; a++) { int dcId = buffer->readInt32(nullptr); cdnPublicKeys[dcId] = buffer->readString(nullptr); cdnPublicKeysFingerprints[dcId] = buffer->readUint64(nullptr); } } buffer->reuse(); if (!cdnPublicKeysFingerprints.empty()) { size_t count = cdnWaitingDatacenters.size(); for (uint32_t a = 0; a < count; a++) { cdnWaitingDatacenters[a]->beginHandshake(HandshakeTypeCurrent, false); } cdnWaitingDatacenters.clear(); return; } } } loadingCdnKeys = true; TL_help_getCdnConfig *request = new TL_help_getCdnConfig(); ConnectionsManager::getInstance(datacenter->instanceNum).sendRequest(request, [&, datacenter](TLObject *response, TL_error *error, int32_t networkType) { if (response != nullptr) { TL_cdnConfig *config = (TL_cdnConfig *) response; size_t count = config->public_keys.size(); BIO *keyBio = BIO_new(BIO_s_mem()); NativeByteBuffer *buffer = BuffersStorage::getInstance().getFreeBuffer(1024); thread_local static uint8_t sha1Buffer[20]; for (uint32_t a = 0; a < count; a++) { TL_cdnPublicKey *publicKey = config->public_keys[a].get(); cdnPublicKeys[publicKey->dc_id] = publicKey->public_key; BIO_write(keyBio, publicKey->public_key.c_str(), (int) publicKey->public_key.length()); RSA *rsaKey = PEM_read_bio_RSAPublicKey(keyBio, NULL, NULL, NULL); int nBytes = BN_num_bytes(rsaKey->n); int eBytes = BN_num_bytes(rsaKey->e); std::string nStr(nBytes, 0), eStr(eBytes, 0); BN_bn2bin(rsaKey->n, (uint8_t *)&nStr[0]); BN_bn2bin(rsaKey->e, (uint8_t *)&eStr[0]); buffer->writeString(nStr); buffer->writeString(eStr); SHA1(buffer->bytes(), buffer->position(), sha1Buffer); cdnPublicKeysFingerprints[publicKey->dc_id] = ((uint64_t) sha1Buffer[19]) << 56 | ((uint64_t) sha1Buffer[18]) << 48 | ((uint64_t) sha1Buffer[17]) << 40 | ((uint64_t) sha1Buffer[16]) << 32 | ((uint64_t) sha1Buffer[15]) << 24 | ((uint64_t) sha1Buffer[14]) << 16 | ((uint64_t) sha1Buffer[13]) << 8 | ((uint64_t) sha1Buffer[12]); RSA_free(rsaKey); if (a != count - 1) { buffer->position(0); BIO_reset(keyBio); } } buffer->reuse(); BIO_free(keyBio); count = cdnWaitingDatacenters.size(); for (uint32_t a = 0; a < count; a++) { cdnWaitingDatacenters[a]->beginHandshake(HandshakeTypeCurrent, false); } cdnWaitingDatacenters.clear(); saveCdnConfig(datacenter); } loadingCdnKeys = false; }, nullptr, RequestFlagEnableUnauthorized | RequestFlagWithoutLogin, DEFAULT_DATACENTER_ID, ConnectionTypeGeneric, true); } HandshakeType Handshake::getType() { return handshakeType; } ByteArray *Handshake::getAuthKeyTempPending() { return authKeyTempPending; } int64_t Handshake::getAuthKeyTempPendingId() { return authKeyTempPendingId; } void Handshake::onHandshakeConnectionClosed() { if (handshakeState == 0) { return; } needResendData = true; } void Handshake::onHandshakeConnectionConnected() { if (handshakeState == 0 || !needResendData) { return; } beginHandshake(false); }