Improved logging, extended the tools class instead of calling its methods statically, improved generation and checking of message ids

This commit is contained in:
Daniil Gentili 2016-10-11 17:29:47 +02:00
parent 528faf16b0
commit a540b0a4cf
13 changed files with 88 additions and 63 deletions

10
composer.lock generated
View File

@ -237,16 +237,16 @@
},
{
"name": "phpseclib/phpseclib",
"version": "2.0.3",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "41f85e9c2582b3f6d1b7d20395fb40c687ad5370"
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/41f85e9c2582b3f6d1b7d20395fb40c687ad5370",
"reference": "41f85e9c2582b3f6d1b7d20395fb40c687ad5370",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/ab8028c93c03cc8d9c824efa75dc94f1db2369bf",
"reference": "ab8028c93c03cc8d9c824efa75dc94f1db2369bf",
"shasum": ""
},
"require": {
@ -325,7 +325,7 @@
"x.509",
"x509"
],
"time": "2016-08-18 18:49:14"
"time": "2016-10-04 00:57:04"
}
],
"packages-dev": [],

View File

@ -20,7 +20,7 @@ class API extends Tools
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
$this->session = new MTProto($params);
$future_salts = $this->ping(3);
var_dump($future_salts = $this->ping(3));
$future_salts = $this->get_future_salts(3);
}

View File

@ -15,7 +15,7 @@ namespace danog\MadelineProto;
/**
* Manages connection to telegram servers.
*/
class Connection
class Connection extends Tools
{
public $sock = null;
public $protocol = null;
@ -41,7 +41,7 @@ class Connection
if (!(get_resource_type($this->sock) == 'file' || get_resource_type($this->sock) == 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(Tools::string2bin('\xef'));
$this->write($this->string2bin('\xef'));
break;
case 'tcp_intermediate':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
@ -49,7 +49,7 @@ class Connection
if (!(get_resource_type($this->sock) == 'file' || get_resource_type($this->sock) == 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(Tools::string2bin('\xee\xee\xee\xee'));
$this->write($this->string2bin('\xee\xee\xee\xee'));
break;
case 'tcp_full':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
@ -147,7 +147,7 @@ class Connection
if ($in_seq_no != $this->in_seq_no) {
throw new Exception('Incoming seq_no mismatch');
}
$payload = Tools::fopen_and_write('php://memory', 'rw+b', substr($packet, 4, $packet_length - 12));
$payload = $this->fopen_and_write('php://memory', 'rw+b', substr($packet, 4, $packet_length - 12));
break;
case 'tcp_intermediate':
$packet_length_data = $this->sock->read(4);
@ -156,7 +156,7 @@ class Connection
}
$packet_length = $this->struct->unpack('<I', $packet_length_data)[0];
$packet = $this->sock->read($packet_length);
$payload = Tools::fopen_and_write('php://memory', 'rw+b', $packet);
$payload = $this->fopen_and_write('php://memory', 'rw+b', $packet);
break;
case 'tcp_abridged':
$packet_length_data = $this->sock->read(1);
@ -171,7 +171,7 @@ class Connection
$packet_length = $this->struct->unpack('<I', $packet_length_data.pack('x'))[0] << 2;
}
$packet = $this->sock->read($packet_length);
$payload = Tools::fopen_and_write('php://memory', 'rw+b', $packet);
$payload = $this->fopen_and_write('php://memory', 'rw+b', $packet);
break;
}

View File

@ -64,6 +64,7 @@ class Logging
if (!is_string($param)) {
$param = var_export($param, true);
}
$param = basename(debug_backtrace()[0]["file"], '.php').': '.$param;
switch ($mode) {
case '1':
error_log($param);

View File

@ -111,8 +111,6 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
$this->key = new RSA($settings['authorization']['rsa_key']);
// Istantiate struct class
$this->struct = new \danog\PHP\StructTools();
// Istantiate prime class
$this->PrimeModule = new PrimeModule();
// Istantiate TL class
$this->tl = new TL\TL($this->settings['tl_schema']['src']);
// Istantiate logging class

View File

@ -15,7 +15,7 @@ namespace danog\MadelineProto\MTProtoTools;
/**
* Manages acknowledgement of messages.
*/
class AckHandler extends \danog\MadelineProto\Tools
class AckHandler extends \danog\MadelineProto\PrimeModule
{
public function ack_outgoing_message_id($message_id)
{
@ -28,7 +28,7 @@ class AckHandler extends \danog\MadelineProto\Tools
public function ack_incoming_message_id($message_id)
{
if ($this->settings['authorization']['temp_auth_key']['id'] === null || $this->settings['authorization']['temp_auth_key']['id'] == \danog\MadelineProto\Tools::string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
if ($this->settings['authorization']['temp_auth_key']['id'] === null || $this->settings['authorization']['temp_auth_key']['id'] == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
return;
}
// I let the server know that I received its message

View File

@ -23,7 +23,7 @@ class AuthKeyHandler extends AckHandler
public function create_auth_key($expires_in = -1)
{
foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id_total) {
$this->log->log('Handshake: Requesting pq');
$this->log->log('Requesting pq');
/**
* ***********************************************************************
@ -54,7 +54,7 @@ class AuthKeyHandler extends AckHandler
* Check if the client's nonce and the server's nonce are the same
*/
if ($ResPQ['nonce'] !== $nonce) {
throw new Exception('Handshake: wrong nonce');
throw new Exception('wrong nonce');
}
/*
@ -71,7 +71,7 @@ class AuthKeyHandler extends AckHandler
}
if (!isset($public_key_fingerprint)) {
throw new Exception("Handshake: couldn't find our key in the server_public_key_fingerprints vector.");
throw new Exception("couldn't find our key in the server_public_key_fingerprints vector.");
}
$pq_bytes = $ResPQ['pq'];
@ -82,7 +82,7 @@ class AuthKeyHandler extends AckHandler
* Compute p and q
*/
$pq = new \phpseclib\Math\BigInteger($pq_bytes, 256);
list($p, $q) = $this->PrimeModule->primefactors($pq);
list($p, $q) = $this->PrimeFactors($pq);
$p = new \phpseclib\Math\BigInteger($p);
$q = new \phpseclib\Math\BigInteger($q);
@ -91,10 +91,10 @@ class AuthKeyHandler extends AckHandler
}
if (!($pq->equals($p->multiply($q)) && $p->compare($q) < 0)) {
throw new Exception("Handshake: couldn't compute p and q.");
throw new Exception("couldn't compute p and q.");
}
$this->log->log('Handshake: Factorization '.$pq.' = '.$p.' * '.$q);
$this->log->log('Factorization '.$pq.' = '.$p.' * '.$q);
/*
* ***********************************************************************
@ -138,7 +138,7 @@ class AuthKeyHandler extends AckHandler
$to_encrypt = $sha_digest.$data.$random_bytes;
$encrypted_data = $this->key->encrypt($to_encrypt);
$this->log->log('Handshake: Starting Diffie Hellman key exchange');
$this->log->log('Starting Diffie Hellman key exchange');
/*
* ***********************************************************************
* Starting Diffie Hellman key exchange, Server authentication
@ -175,7 +175,7 @@ class AuthKeyHandler extends AckHandler
* Check if the client's nonce and the server's nonce are the same
*/
if ($nonce != $server_dh_params['nonce']) {
throw new Exception('Handshake: wrong nonce.');
throw new Exception('wrong nonce.');
}
/*
@ -183,7 +183,7 @@ class AuthKeyHandler extends AckHandler
* Check if server_nonce and new server_nonce are the same
*/
if ($server_nonce != $server_dh_params['server_nonce']) {
throw new Exception('Handshake: wrong server nonce.');
throw new Exception('wrong server nonce.');
}
/*
@ -192,7 +192,7 @@ class AuthKeyHandler extends AckHandler
* 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 Exception('Handshake: wrong new nonce hash.');
throw new Exception('wrong new nonce hash.');
}
/*
@ -232,15 +232,15 @@ class AuthKeyHandler extends AckHandler
*/
$server_DH_inner_data_length = $this->tl->get_length($this->fopen_and_write('php://memory', 'rw+b', $answer));
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
throw new Exception('Handshake: answer_hash mismatch.');
throw new Exception('answer_hash mismatch.');
}
if ($nonce != $server_DH_inner_data['nonce']) {
throw new Exception('Handshake: wrong nonce');
throw new Exception('wrong nonce');
}
if ($server_nonce != $server_DH_inner_data['server_nonce']) {
throw new Exception('Handshake: wrong server nonce');
throw new Exception('wrong server nonce');
}
$g = new \phpseclib\Math\BigInteger($server_DH_inner_data['g']);
@ -254,13 +254,14 @@ class AuthKeyHandler extends AckHandler
$server_time = $server_DH_inner_data['server_time'];
$this->timedelta = $server_time - time();
$this->log->log(sprintf('Handshake: Server-client time delta = %.1f s', $this->timedelta));
$this->log->log(sprintf('Server-client time delta = %.1f s', $this->timedelta));
/*
* ***********************************************************************
* Define some needed numbers for BigInteger
*/
$this->log->log('Executing dh_prime checks...');
$one = new \phpseclib\Math\BigInteger(1);
$two = new \phpseclib\Math\BigInteger(2);
$twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328');
@ -272,20 +273,20 @@ class AuthKeyHandler extends AckHandler
* 2^2047 < dh_prime < 2^2048
*/
if (!$dh_prime->isPrime()) {
throw new Exception("Handshake: dh_prime isn't a safe 2048-bit prime (dh_prime isn't a prime).");
throw new Exception("dh_prime isn't a safe 2048-bit prime (dh_prime isn't a prime).");
}
/*
// Almost always fails
if (!$dh_prime->subtract($one)->divide($two)[0]->isPrime()) {
throw new Exception("Handshake: dh_prime isn't a safe 2048-bit prime ((dh_prime - 1) / 2 isn't a prime).");
throw new Exception("dh_prime isn't a safe 2048-bit prime ((dh_prime - 1) / 2 isn't a prime).");
}
*/
if ($dh_prime->compare($twoe2047) <= 0 // 2^2047 < dh_prime or dh_prime > 2^2047 or ! dh_prime <= 2^2047
|| $dh_prime->compare($twoe2048) >= 0 // dh_prime < 2^2048 or ! dh_prime >= 2^2048
) {
throw new Exception("Handshake: g isn't a safe 2048-bit prime (2^2047 < dh_prime < 2^2048 is false).");
throw new Exception("g isn't a safe 2048-bit prime (2^2047 < dh_prime < 2^2048 is false).");
}
/*
@ -296,7 +297,7 @@ class AuthKeyHandler extends AckHandler
if ($g->compare($one) <= 0 // 1 < g or g > 1 or ! g <= 1
|| $g->compare($dh_prime->subtract($one)) >= 0 // g < dh_prime - 1 or ! g >= dh_prime - 1
) {
throw new Exception('Handshake: g is invalid (1 < g < dh_prime - 1 is false).');
throw new Exception('g is invalid (1 < g < dh_prime - 1 is false).');
}
/*
@ -307,7 +308,7 @@ class AuthKeyHandler extends AckHandler
if ($g_a->compare($one) <= 0 // 1 < g_a or g_a > 1 or ! g_a <= 1
|| $g_a->compare($dh_prime->subtract($one)) >= 0 // g_a < dh_prime - 1 or ! g_a >= dh_prime - 1
) {
throw new Exception('Handshake: g_a is invalid (1 < g_a < dh_prime - 1 is false).');
throw new Exception('g_a is invalid (1 < g_a < dh_prime - 1 is false).');
}
foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id) {
@ -322,7 +323,7 @@ class AuthKeyHandler extends AckHandler
if ($g_b->compare($one) <= 0 // 1 < g_b or g_b > 1 or ! g_b <= 1
|| $g_b->compare($dh_prime->subtract($one)) >= 0 // g_b < dh_prime - 1 or ! g_b >= dh_prime - 1
) {
throw new Exception('Handshake: g_b is invalid (1 < g_b < dh_prime - 1 is false).');
throw new Exception('g_b is invalid (1 < g_b < dh_prime - 1 is false).');
}
$g_b_str = $g_b->toBytes();
@ -398,7 +399,7 @@ class AuthKeyHandler extends AckHandler
* Check if the client's nonce and the server's nonce are the same
*/
if ($Set_client_DH_params_answer['nonce'] != $nonce) {
throw new Exception('Handshake: wrong nonce.');
throw new Exception('wrong nonce.');
}
/*
@ -406,7 +407,7 @@ class AuthKeyHandler extends AckHandler
* Check if server_nonce and new server_nonce are the same
*/
if ($Set_client_DH_params_answer['server_nonce'] != $server_nonce) {
throw new Exception('Handshake: wrong server nonce');
throw new Exception('wrong server nonce');
}
/*
@ -416,10 +417,10 @@ class AuthKeyHandler extends AckHandler
switch ($Set_client_DH_params_answer['_']) {
case 'dh_gen_ok':
if ($Set_client_DH_params_answer['new_nonce_hash1'] != $new_nonce_hash1) {
throw new Exception('Handshake: wrong new_nonce_hash1');
throw new Exception('wrong new_nonce_hash1');
}
$this->log->log('Handshake: Diffie Hellman key exchange processed successfully');
$this->log->log('Diffie Hellman key exchange processed successfully');
$res_authorization['server_salt'] = $this->struct->unpack('<q', substr($new_nonce, 0, 8 - 0) ^ substr($server_nonce, 0, 8 - 0))[0];
$res_authorization['auth_key'] = $auth_key_str;
@ -429,24 +430,24 @@ class AuthKeyHandler extends AckHandler
$res_authorization['expires_in'] = $expires_in;
}
$this->log->log('Handshake: Auth key generated');
$this->log->log('Auth key generated');
$this->timedelta = 0;
return $res_authorization;
case 'dh_gen_retry':
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
throw new Exception('Handshake: wrong new_nonce_hash_2');
throw new Exception('wrong new_nonce_hash_2');
}
//repeat foreach
$this->log->log('Handshake: Retrying Auth');
$this->log->log('Retrying Auth');
break;
case 'dh_gen_fail':
if ($Set_client_DH_params_answer['new_nonce_hash3'] != $new_nonce_hash3) {
throw new Exception('Handshake: wrong new_nonce_hash_3');
throw new Exception('wrong new_nonce_hash_3');
}
$this->log->log('Handshake: Auth Failed');
$this->log->log('Auth Failed');
break 2;
default:
throw new Exception('Response Error');

View File

@ -22,7 +22,7 @@ class CallHandler extends AuthKeyHandler
$response = null;
$count = 0;
while ($response == null && $count++ < $this->settings['max_tries']['response']) {
$this->log->log('Getting response ('.$count.')...');
$this->log->log('Getting response (try number '.$count.' for '.$last_sent.')...');
$last_received = $this->recv_message();
$this->handle_message($last_sent, $last_received);
if (isset($this->outgoing_messages[$last_sent]['response']) && isset($this->incoming_messages[$this->outgoing_messages[$last_sent]['response']]['content'])) {
@ -82,7 +82,7 @@ class CallHandler extends AuthKeyHandler
// if ($server_answer == null) {
// throw new Exception('An error occurred while calling object '.$object.'.');
// }
// $deserialized = $this->tl->deserialize(\danog\MadelineProto\Tools::fopen_and_write('php://memory', 'rw+b', $server_answer));
// $deserialized = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $server_answer));
// return $deserialized;
}
throw new Exception('An error occurred while calling object '.$object.'.');

View File

@ -65,7 +65,7 @@ class Crypt extends CallHandler
$ivp = substr($iv, 0, $blocksize);
$ivp2 = substr($iv, $blocksize);
$ciphered = '';
foreach (\danog\MadelineProto\Tools::range(0, strlen($message), $blocksize) as $i) {
foreach ($this->range(0, strlen($message), $blocksize) as $i) {
$indata = substr($message, $i, $blocksize);
if ($operation == 'decrypt') {
$xored = $indata ^ $ivp2;

View File

@ -23,16 +23,15 @@ class MessageHandler extends Crypt
*/
public function send_message($message_data, $content_related)
{
$int_message_id = (int) ((time() + $this->timedelta) * pow(2, 30)) * 4;
$int_message_id = $this->generate_message_id();
$message_id = $this->struct->pack('<Q', $int_message_id);
$this->check_message_id($int_message_id, true);
if (($this->settings['authorization']['temp_auth_key']['auth_key'] == null) || ($this->settings['authorization']['temp_auth_key']['server_salt'] == null)) {
$message = \danog\MadelineProto\Tools::string2bin('\x00\x00\x00\x00\x00\x00\x00\x00').$message_id.$this->struct->pack('<I', strlen($message_data)).$message_data;
$message = $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00').$message_id.$this->struct->pack('<I', strlen($message_data)).$message_data;
} else {
$seq_no = $this->generate_seq_no($content_related);
$encrypted_data = $this->struct->pack('<q', $this->settings['authorization']['temp_auth_key']['server_salt']).$this->settings['authorization']['session_id'].$message_id.$this->struct->pack('<II', $seq_no, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = \phpseclib\Crypt\Random::string(\danog\MadelineProto\Tools::posmod(-strlen($encrypted_data), 16));
$padding = \phpseclib\Crypt\Random::string($this->posmod(-strlen($encrypted_data), 16));
list($aes_key, $aes_iv) = $this->aes_calculate($message_key);
$message = $this->settings['authorization']['temp_auth_key']['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
$this->outgoing_messages[$int_message_id]['seq_no'] = $seq_no;
@ -52,7 +51,7 @@ class MessageHandler extends Crypt
throw new Exception('Server response error: '.abs($this->struct->unpack('<i', fread($payload, 4))[0]));
}
$auth_key_id = fread($payload, 8);
if ($auth_key_id == \danog\MadelineProto\Tools::string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
if ($auth_key_id == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
list($message_id, $message_length) = $this->struct->unpack('<QI', fread($payload, 12));
$this->check_message_id($message_id, false);
$message_data = fread($payload, $message_length);
@ -104,7 +103,7 @@ class MessageHandler extends Crypt
} else {
throw new Exception('Got unknown auth_key id');
}
$deserialized = $this->tl->deserialize(\danog\MadelineProto\Tools::fopen_and_write('php://memory', 'rw+b', $message_data));
$deserialized = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $message_data));
$this->incoming_messages[$message_id]['content'] = $deserialized;
return $message_id;

View File

@ -29,6 +29,11 @@ class MsgIdHandler extends MessageHandler
if ($new_message_id % 4 != 0) {
throw new Exception('Given message id ('.$new_message_id.') is not divisible by 4.');
}
$keys = array_keys($this->outgoing_messages);
asort($keys);
if ($new_message_id <= end($keys)) {
throw new Exception('Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.end($keys).').', 1);
}
$this->outgoing_messages[$new_message_id] = [];
if (count($this->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
array_shift($this->outgoing_messages);
@ -58,4 +63,19 @@ class MsgIdHandler extends MessageHandler
ksort($this->incoming_messages);
}
}
public function generate_message_id() {
$ms_time = (time() + $this->timedelta) * 1000;
$int_message_id = (int) (
((int)($ms_time / 1000) << 32) |
($this->posmod($ms_time, 1000) << 22) |
rand(0, 524288) << 2
);
$keys = array_keys($this->outgoing_messages);
asort($keys);
if ($int_message_id <= end($keys)) {
$int_message_id += 4;
}
$this->check_message_id($int_message_id, true);
return $int_message_id;
}
}

View File

@ -48,6 +48,7 @@ class ResponseHandler extends MsgIdHandler
break;
case 'pong':
var_dump($this->outgoing_messages);
foreach ($this->outgoing_messages as $msg_id => &$omessage) {
if (isset($omessage['content']['args']['ping_id']) && $omessage['content']['args']['ping_id'] == $response['ping_id']) {
$omessage['response'] = $response['msg_id'];

View File

@ -76,16 +76,11 @@ class PrimeModule extends Tools
return ($b == 0) ? $a : $b;
}
public function primefactors($pq, $sort = false)
public function PrimeFactors($pq, $sort = false)
{
// Use the native version
$pqstr = (string) $pq;
$res = $this->find_small_multiplier_lopatin((int) $pqstr);
$res = [$res, $pqstr / $res];
if ($res[1] != 1) {
return $res;
}
// Use the python version.
$this->log->log("Trying to use the python factorization module");
if (function_exists('shell_exec')) {
try {
$res = json_decode(shell_exec('python '.__DIR__.'/getpq.py '.$pqstr));
@ -95,7 +90,8 @@ class PrimeModule extends Tools
} catch (Exception $e) {
}
}
// Else do factorization with wolfram alpha :)))))
$this->log->log("Trying to use the wolfram alpha factorization module");
$query = 'Do prime factorization of '.$pqstr;
$params = [
'async' => true,
@ -123,6 +119,15 @@ class PrimeModule extends Tools
if (count($res) == 2) {
return $res;
}
$this->log->log("Trying to use the native factorization module");
$res = $this->find_small_multiplier_lopatin((int) $pqstr);
$res = [$res, $pqstr / $res];
if ($res[1] != 1) {
return $res;
}
throw new Exception("Couldn't calculate pq!");
}
}