MadelineProto/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php

615 lines
38 KiB
PHP

<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
* Manages the creation of the authorization key.
*
* https://core.telegram.org/mtproto/auth_key
* https://core.telegram.org/mtproto/samples-auth_key
*/
trait AuthKeyHandler
{
public function create_auth_key($expires_in, $datacenter)
{
$req_pq = strpos($datacenter, 'cdn') ? 'req_pq' : 'req_pq_multi';
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
try {
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['req_pq'], \danog\MadelineProto\Logger::VERBOSE);
/**
* ***********************************************************************
* Make pq request, DH exchange initiation.
*
* @method req_pq
*
* @param [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* ]
*
* @return ResPQ [
* int128 $nonce : The value of nonce is selected randomly by the server
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $pq : This is a representation of a natural number (in binary big endian format). This number is the product of two different odd prime numbers
* Vector long $server_public_key_fingerprints : This is a list of public RSA key fingerprints
* ]
*/
$nonce = $this->random(16);
$ResPQ = $this->method_call($req_pq, ['nonce' => $nonce], ['datacenter' => $datacenter]);
/*
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
if ($ResPQ['nonce'] !== $nonce) {
throw new \danog\MadelineProto\SecurityException('wrong nonce');
}
/*
* ***********************************************************************
* Find our key in the server_public_key_fingerprints vector
*/
foreach ($this->rsa_keys as $curkey) {
if (in_array($curkey->fp, $ResPQ['server_public_key_fingerprints'])) {
$key = $curkey;
}
}
if (!isset($key)) {
throw new \danog\MadelineProto\SecurityException("Couldn't find any of our keys in the server_public_key_fingerprints vector.");
}
$pq_bytes = $ResPQ['pq'];
$server_nonce = $ResPQ['server_nonce'];
/*
* ***********************************************************************
* Compute p and q
*/
$pq = new \phpseclib\Math\BigInteger($pq_bytes, 256);
$q = new \phpseclib\Math\BigInteger(0);
$p = new \phpseclib\Math\BigInteger(\danog\PrimeModule::auto_single($pq->__toString()));
if (!$p->equals(\danog\MadelineProto\Magic::$zero)) {
$q = $pq->divide($p)[0];
if ($p->compare($q) > 0) {
list($p, $q) = [$q, $p];
}
}
if (!$pq->equals($p->multiply($q))) {
$this->logger->logger('Automatic factorization failed, trying native CPP module', \danog\MadelineProto\Logger::ERROR);
$p = new \phpseclib\Math\BigInteger(\danog\PrimeModule::native_single_cpp($pq->__toString()));
if (!$p->equals(\danog\MadelineProto\Magic::$zero)) {
$q = $pq->divide($p)[0];
if ($p->compare($q) > 0) {
list($p, $q) = [$q, $p];
}
}
if (!$pq->equals($p->multiply($q))) {
$this->logger->logger('Automatic factorization failed, trying alt py module', \danog\MadelineProto\Logger::ERROR);
$p = new \phpseclib\Math\BigInteger(\danog\PrimeModule::python_single_alt($pq->__toString()));
if (!$p->equals(\danog\MadelineProto\Magic::$zero)) {
$q = $pq->divide($p)[0];
if ($p->compare($q) > 0) {
list($p, $q) = [$q, $p];
}
}
if (!$pq->equals($p->multiply($q))) {
$this->logger->logger('Automatic factorization failed, trying py module', \danog\MadelineProto\Logger::ERROR);
$p = new \phpseclib\Math\BigInteger(\danog\PrimeModule::python_single($pq->__toString()));
if (!$p->equals(\danog\MadelineProto\Magic::$zero)) {
$q = $pq->divide($p)[0];
if ($p->compare($q) > 0) {
list($p, $q) = [$q, $p];
}
}
if (!$pq->equals($p->multiply($q))) {
$this->logger->logger('Automatic factorization failed, trying native module', \danog\MadelineProto\Logger::ERROR);
$p = new \phpseclib\Math\BigInteger(\danog\PrimeModule::native_single($pq->__toString()));
if (!$p->equals(\danog\MadelineProto\Magic::$zero)) {
$q = $pq->divide($p)[0];
if ($p->compare($q) > 0) {
list($p, $q) = [$q, $p];
}
}
if (!$pq->equals($p->multiply($q))) {
$this->logger->logger('Automatic factorization failed, trying wolfram module', \danog\MadelineProto\Logger::ERROR);
if (!extension_loaded('curl')) {
throw new Exception(['extension', 'curl']);
}
$p = new \phpseclib\Math\BigInteger(\danog\PrimeModule::wolfram_single($pq->__toString()));
if (!$p->equals(\danog\MadelineProto\Magic::$zero)) {
$q = $pq->divide($p)[0];
if ($p->compare($q) > 0) {
list($p, $q) = [$q, $p];
}
}
if (!$pq->equals($p->multiply($q))) {
throw new \danog\MadelineProto\SecurityException("couldn't compute p and q. Original pq: {$pq}, computed p: {$p}, computed q: {$q}, computed pq: ".$p->multiply($q));
}
}
}
}
}
}
$this->logger->logger('Factorization '.$pq.' = '.$p.' * '.$q, \danog\MadelineProto\Logger::VERBOSE);
/*
* ***********************************************************************
* Serialize object for req_DH_params
*/
$p_bytes = $p->toBytes();
$q_bytes = $q->toBytes();
$new_nonce = $this->random(32);
$data_unserialized = ['pq' => $pq_bytes, 'p' => $p_bytes, 'q' => $q_bytes, 'nonce' => $nonce, 'server_nonce' => $server_nonce, 'new_nonce' => $new_nonce, 'expires_in' => $expires_in];
$p_q_inner_data = $this->serialize_object(['type' => 'p_q_inner_data'.($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data');
/*
* ***********************************************************************
* Encrypt serialized object
*/
$sha_digest = sha1($p_q_inner_data, true);
$random_bytes = $this->random(255 - strlen($p_q_inner_data) - strlen($sha_digest));
$to_encrypt = $sha_digest.$p_q_inner_data.$random_bytes;
$encrypted_data = $key->encrypt($to_encrypt);
$this->logger->logger('Starting Diffie Hellman key exchange', \danog\MadelineProto\Logger::VERBOSE);
/*
* ***********************************************************************
* Starting Diffie Hellman key exchange, Server authentication
* @method req_DH_params
* @param [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $p : The value of BigInteger
* string $q : The value of BigInteger
* long $public_key_fingerprint : This is our key in the server_public_key_fingerprints vector
* string $encrypted_data
* ]
* @return Server_DH_Params [
* int128 $nonce : The value of nonce is selected randomly by the server
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $new_nonce_hash : Return this value if server responds with server_DH_params_fail
* string $encrypted_answer : Return this value if server responds with server_DH_params_ok
* ]
*/
//
$server_dh_params = $this->method_call('req_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'p' => $p_bytes, 'q' => $q_bytes, 'public_key_fingerprint' => $key->fp, 'encrypted_data' => $encrypted_data], ['datacenter' => $datacenter]);
/*
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
if ($nonce != $server_dh_params['nonce']) {
throw new \danog\MadelineProto\SecurityException('wrong nonce.');
}
/*
* ***********************************************************************
* Check if server_nonce and new server_nonce are the same
*/
if ($server_nonce != $server_dh_params['server_nonce']) {
throw new \danog\MadelineProto\SecurityException('wrong server nonce.');
}
/*
* ***********************************************************************
* Check valid new nonce hash if return from server
* new nonce hash return in server_DH_params_fail
*/
if (isset($server_dh_params['new_nonce_hash']) && substr(sha1($new_nonce), -32) != $server_dh_params['new_nonce_hash']) {
throw new \danog\MadelineProto\SecurityException('wrong new nonce hash.');
}
/*
* ***********************************************************************
* Get key, iv and decrypt answer
*/
$encrypted_answer = $server_dh_params['encrypted_answer'];
$tmp_aes_key = sha1($new_nonce.$server_nonce, true).substr(sha1($server_nonce.$new_nonce, true), 0, 12);
$tmp_aes_iv = substr(sha1($server_nonce.$new_nonce, true), 12, 8).sha1($new_nonce.$new_nonce, true).substr($new_nonce, 0, 4);
$answer_with_hash = $this->ige_decrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv);
/*
* ***********************************************************************
* Separate answer and hash
*/
$answer_hash = substr($answer_with_hash, 0, 20);
$answer = substr($answer_with_hash, 20);
/*
* ***********************************************************************
* Deserialize answer
* @return Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* int $g
* string $dh_prime
* string $g_a
* int $server_time
* ]
*/
$server_DH_inner_data = $this->deserialize($answer, ['type' => '']);
/*
* ***********************************************************************
* Do some checks
*/
$server_DH_inner_data_length = $this->get_length($answer);
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
throw new \danog\MadelineProto\SecurityException('answer_hash mismatch.');
}
if ($nonce != $server_DH_inner_data['nonce']) {
throw new \danog\MadelineProto\SecurityException('wrong nonce');
}
if ($server_nonce != $server_DH_inner_data['server_nonce']) {
throw new \danog\MadelineProto\SecurityException('wrong server nonce');
}
$g = new \phpseclib\Math\BigInteger($server_DH_inner_data['g']);
$g_a = new \phpseclib\Math\BigInteger($server_DH_inner_data['g_a'], 256);
$dh_prime = new \phpseclib\Math\BigInteger($server_DH_inner_data['dh_prime'], 256);
/*
* ***********************************************************************
* Time delta
*/
$server_time = $server_DH_inner_data['server_time'];
$this->datacenter->sockets[$datacenter]->time_delta = $server_time - time();
$this->logger->logger(sprintf('Server-client time delta = %.1f s', $this->datacenter->sockets[$datacenter]->time_delta), \danog\MadelineProto\Logger::VERBOSE);
$this->check_p_g($dh_prime, $g);
$this->check_G($g_a, $dh_prime);
for ($retry_id = 0; $retry_id <= $this->settings['max_tries']['authorization']; $retry_id++) {
$this->logger->logger('Generating b...', \danog\MadelineProto\Logger::VERBOSE);
$b = new \phpseclib\Math\BigInteger($this->random(256), 256);
$this->logger->logger('Generating g_b...', \danog\MadelineProto\Logger::VERBOSE);
$g_b = $g->powMod($b, $dh_prime);
$this->check_G($g_b, $dh_prime);
/*
* ***********************************************************************
* Check validity of g_b
* 1 < g_b < dh_prime - 1
*/
$this->logger->logger('Executing g_b check...', \danog\MadelineProto\Logger::VERBOSE);
if ($g_b->compare(\danog\MadelineProto\Magic::$one) <= 0 || $g_b->compare($dh_prime->subtract(\danog\MadelineProto\Magic::$one)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g_b is invalid (1 < g_b < dh_prime - 1 is false).');
}
$this->logger->logger('Preparing client_DH_inner_data...', \danog\MadelineProto\Logger::VERBOSE);
$g_b_str = $g_b->toBytes();
/*
* ***********************************************************************
* serialize client_DH_inner_data
* @method client_DH_inner_data
* @param Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* long $retry_id : First attempt
* string $g_b : g^b mod dh_prime
* ]
*/
$data = $this->serialize_object(['type' => 'client_DH_inner_data'], ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'retry_id' => $retry_id, 'g_b' => $g_b_str], 'client_DH_inner_data');
/*
* ***********************************************************************
* encrypt client_DH_inner_data
*/
$data_with_sha = sha1($data, true).$data;
$data_with_sha_padded = $data_with_sha.$this->random($this->posmod(-strlen($data_with_sha), 16));
$encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
$this->logger->logger('Executing set_client_DH_params...', \danog\MadelineProto\Logger::VERBOSE);
/*
* ***********************************************************************
* Send set_client_DH_params query
* @method set_client_DH_params
* @param Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $encrypted_data
* ]
* @return Set_client_DH_params_answer [
* string $_ : This value is dh_gen_ok, dh_gen_retry OR dh_gen_fail
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* int128 $new_nonce_hash1 : Return this value if server responds with dh_gen_ok
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_retry
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_fail
* ]
*/
$Set_client_DH_params_answer = $this->method_call('set_client_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'encrypted_data' => $encrypted_data], ['datacenter' => $datacenter]);
/*
* ***********************************************************************
* Generate auth_key
*/
$this->logger->logger('Generating authorization key...', \danog\MadelineProto\Logger::VERBOSE);
$auth_key = $g_a->powMod($b, $dh_prime);
$auth_key_str = $auth_key->toBytes();
$auth_key_sha = sha1($auth_key_str, true);
$auth_key_aux_hash = substr($auth_key_sha, 0, 8);
$new_nonce_hash1 = substr(sha1($new_nonce.chr(1).$auth_key_aux_hash, true), -16);
$new_nonce_hash2 = substr(sha1($new_nonce.chr(2).$auth_key_aux_hash, true), -16);
$new_nonce_hash3 = substr(sha1($new_nonce.chr(3).$auth_key_aux_hash, true), -16);
/*
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
if ($Set_client_DH_params_answer['nonce'] != $nonce) {
throw new \danog\MadelineProto\SecurityException('wrong nonce.');
}
/*
* ***********************************************************************
* Check if server_nonce and new server_nonce are the same
*/
if ($Set_client_DH_params_answer['server_nonce'] != $server_nonce) {
throw new \danog\MadelineProto\SecurityException('wrong server nonce');
}
/*
* ***********************************************************************
* Check Set_client_DH_params_answer type
*/
switch ($Set_client_DH_params_answer['_']) {
case 'dh_gen_ok':
if ($Set_client_DH_params_answer['new_nonce_hash1'] != $new_nonce_hash1) {
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash1');
}
$this->logger->logger('Diffie Hellman key exchange processed successfully!', \danog\MadelineProto\Logger::VERBOSE);
$res_authorization['server_salt'] = substr($new_nonce, 0, 8 - 0) ^ substr($server_nonce, 0, 8 - 0);
$res_authorization['auth_key'] = $auth_key_str;
$res_authorization['id'] = substr($auth_key_sha, -8);
$res_authorization['connection_inited'] = false;
$this->logger->logger('Auth key generated', \danog\MadelineProto\Logger::NOTICE);
return $res_authorization;
case 'dh_gen_retry':
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash_2');
}
//repeat foreach
$this->logger->logger('Retrying Auth', \danog\MadelineProto\Logger::VERBOSE);
break;
case 'dh_gen_fail':
if ($Set_client_DH_params_answer['new_nonce_hash3'] != $new_nonce_hash3) {
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash_3');
}
$this->logger->logger('Auth Failed', \danog\MadelineProto\Logger::WARNING);
break 2;
default:
throw new \danog\MadelineProto\SecurityException('Response Error');
break;
}
}
} catch (\danog\MadelineProto\SecurityException $e) {
$this->logger->logger('An exception occurred while generating the authorization key: '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...', \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\Exception $e) {
$this->logger->logger('An exception occurred while generating the authorization key: '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...', \danog\MadelineProto\Logger::WARNING);
$req_pq = $req_pq === 'req_pq_multi' ? 'req_pq' : 'req_pq_multi';
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'RPC_CALL_FAIL') {
throw $e;
}
$this->logger->logger('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
} finally {
$this->datacenter->sockets[$datacenter]->new_outgoing = [];
$this->datacenter->sockets[$datacenter]->new_incoming = [];
}
}
if (strpos($datacenter, 'cdn') === false) {
throw new \danog\MadelineProto\SecurityException('Auth Failed');
}
}
public function check_G($g_a, $p)
{
/*
* ***********************************************************************
* Check validity of g_a
* 1 < g_a < p - 1
*/
$this->logger->logger('Executing g_a check (1/2)...', \danog\MadelineProto\Logger::VERBOSE);
if ($g_a->compare(\danog\MadelineProto\Magic::$one) <= 0 || $g_a->compare($p->subtract(\danog\MadelineProto\Magic::$one)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g_a is invalid (1 < g_a < dh_prime - 1 is false).');
}
$this->logger->logger('Executing g_a check (2/2)...', \danog\MadelineProto\Logger::VERBOSE);
if ($g_a->compare(\danog\MadelineProto\Magic::$twoe1984) < 0 || $g_a->compare($p->subtract(\danog\MadelineProto\Magic::$twoe1984)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g_a is invalid (2^1984 < gA < dh_prime - 2^1984 is false).');
}
return true;
}
public function check_p_g($p, $g)
{
/*
* ***********************************************************************
* Check validity of dh_prime
* Is it a prime?
*/
$this->logger->logger('Executing p/g checks (1/2)...', \danog\MadelineProto\Logger::VERBOSE);
if (!$p->isPrime()) {
throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime (p isn't a prime).");
}
/*
* ***********************************************************************
* Check validity of p
* Is (p - 1) / 2 a prime?
*
* Almost always fails
*/
/*
$this->logger->logger('Executing p/g checks (2/3)...', \danog\MadelineProto\Logger::VERBOSE);
if (!$p->subtract(\danog\MadelineProto\Magic::$one)->divide(\danog\MadelineProto\Magic::$two)[0]->isPrime()) {
throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime ((p - 1) / 2 isn't a prime).");
}
*/
/*
* ***********************************************************************
* Check validity of p
* 2^2047 < p < 2^2048
*/
$this->logger->logger('Executing p/g checks (2/2)...', \danog\MadelineProto\Logger::VERBOSE);
if ($p->compare(\danog\MadelineProto\Magic::$twoe2047) <= 0 || $p->compare(\danog\MadelineProto\Magic::$twoe2048) >= 0) {
throw new \danog\MadelineProto\SecurityException("g isn't a safe 2048-bit prime (2^2047 < p < 2^2048 is false).");
}
/*
* ***********************************************************************
* Check validity of g
* 1 < g < p - 1
*/
$this->logger->logger('Executing g check...', \danog\MadelineProto\Logger::VERBOSE);
if ($g->compare(\danog\MadelineProto\Magic::$one) <= 0 || $g->compare($p->subtract(\danog\MadelineProto\Magic::$one)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g is invalid (1 < g < p - 1 is false).');
}
return true;
}
public function get_dh_config()
{
$this->updates_state['sync_loading'] = true;
try {
$dh_config = $this->method_call('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]);
} finally {
$this->updates_state['sync_loading'] = false;
}
if ($dh_config['_'] === 'messages.dhConfigNotModified') {
$this->logger->logger(\danog\MadelineProto\Logger::VERBOSE, ['DH configuration not modified']);
return $this->dh_config;
}
$dh_config['p'] = new \phpseclib\Math\BigInteger($dh_config['p'], 256);
$dh_config['g'] = new \phpseclib\Math\BigInteger($dh_config['g']);
$this->check_p_g($dh_config['p'], $dh_config['g']);
return $this->dh_config = $dh_config;
}
public function bind_temp_auth_key($expires_in, $datacenter)
{
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
try {
$this->logger->logger('Binding authorization keys...', \danog\MadelineProto\Logger::VERBOSE);
$nonce = $this->random(8);
$expires_at = time() + $expires_in;
$temp_auth_key_id = $this->datacenter->sockets[$datacenter]->temp_auth_key['id'];
$perm_auth_key_id = $this->datacenter->sockets[$datacenter]->auth_key['id'];
$temp_session_id = $this->datacenter->sockets[$datacenter]->session_id;
$message_data = $this->serialize_object(['type' => 'bind_auth_key_inner'], ['nonce' => $nonce, 'temp_auth_key_id' => $temp_auth_key_id, 'perm_auth_key_id' => $perm_auth_key_id, 'temp_session_id' => $temp_session_id, 'expires_at' => $expires_at], 'bind_temp_auth_key_inner');
$message_id = $this->generate_message_id($datacenter);
$seq_no = 0;
$encrypted_data = $this->random(16).$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = $this->random($this->posmod(-strlen($encrypted_data), 16));
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key']);
$encrypted_message = $this->datacenter->sockets[$datacenter]->auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
$res = $this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], ['message_id' => $message_id, 'datacenter' => $datacenter]);
if ($res === true) {
$this->logger->logger('Successfully binded temporary and permanent authorization keys, DC '.$datacenter, \danog\MadelineProto\Logger::NOTICE);
return true;
}
} catch (\danog\MadelineProto\SecurityException $e) {
$this->logger->logger('An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\Exception $e) {
$this->logger->logger('An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\RPCErrorException $e) {
$this->logger->logger('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
} finally {
$this->datacenter->sockets[$datacenter]->new_outgoing = [];
$this->datacenter->sockets[$datacenter]->new_incoming = [];
}
}
throw new \danog\MadelineProto\SecurityException('An error occurred while binding temporary and permanent authorization keys.');
}
// Creates authorization keys
public function init_authorization()
{
$this->initing_authorization = true;
$this->updates_state['sync_loading'] = true;
$this->postpone_updates = true;
try {
foreach ($this->datacenter->sockets as $id => $socket) {
$cdn = strpos($id, 'cdn');
if (strpos($id, 'media') !== false && !$cdn) {
continue;
}
if ($socket->session_id === null) {
$socket->session_id = $this->random(8);
$socket->session_in_seq_no = 0;
$socket->session_out_seq_no = 0;
}
if ($socket->temp_auth_key === null || $socket->auth_key === null) {
$dc_config_number = isset($this->settings['connection_settings'][$id]) ? $id : 'all';
if ($socket->auth_key === null && !$cdn) {
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_perm_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
$socket->auth_key = $this->create_auth_key(-1, $id);
$socket->authorized = false;
}
if ($this->settings['connection_settings'][$dc_config_number]['pfs']) {
if (!$cdn) {
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
$socket->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
$this->bind_temp_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
$config = $this->method_call('help.getConfig', [], ['datacenter' => $id]);
$this->sync_authorization($id);
$this->get_config($config);
} elseif ($socket->temp_auth_key === null) {
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
$socket->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
}
} else {
if (!$cdn) {
$socket->temp_auth_key = $socket->auth_key;
$config = $this->method_call('help.getConfig', [], ['datacenter' => $id]);
$this->sync_authorization($id);
$this->get_config($config);
} elseif ($socket->temp_auth_key === null) {
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
$socket->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
}
}
} elseif (!$cdn) {
$this->sync_authorization($id);
}
}
} finally {
$this->postpone_updates = false;
$this->initing_authorization = false;
$this->updates_state['sync_loading'] = false;
$this->handle_pending_updates();
}
}
public function sync_authorization($id)
{
if (!isset($this->datacenter->sockets[$id])) {
return false;
}
$socket = $this->datacenter->sockets[$id];
if ($this->authorized === self::LOGGED_IN && $socket->authorized === false) {
foreach ($this->datacenter->sockets as $authorized_dc_id => $authorized_socket) {
if ($this->authorized_dc !== -1 && $authorized_dc_id !== $this->authorized_dc) {
continue;
}
if ($authorized_socket->temp_auth_key !== null && $authorized_socket->auth_key !== null && $authorized_socket->authorized === true && $this->authorized === self::LOGGED_IN && $socket->authorized === false && strpos($authorized_dc_id, 'cdn') === false) {
try {
$this->logger->logger('Trying to copy authorization from dc '.$authorized_dc_id.' to dc '.$id);
$exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => preg_replace('|_.*|', '', $id)], ['datacenter' => $authorized_dc_id]);
$authorization = $this->method_call('auth.importAuthorization', $exported_authorization, ['datacenter' => $id]);
$socket->authorized = true;
break;
} catch (\danog\MadelineProto\Exception $e) {
$this->logger->logger('Failure while syncing authorization from DC '.$authorized_dc_id.' to DC '.$id.': '.$e->getMessage(), \danog\MadelineProto\Logger::ERROR);
} catch (\danog\MadelineProto\RPCErrorException $e) {
$this->logger->logger('Failure while syncing authorization from DC '.$authorized_dc_id.' to DC '.$id.': '.$e->getMessage(), \danog\MadelineProto\Logger::ERROR);
if ($e->rpc === 'DC_ID_INVALID') {
break;
}
}
// Turns out this DC isn't authorized after all
}
}
}
}
}