Secret chat fixes

This commit is contained in:
Daniil Gentili 2017-06-02 13:25:05 +02:00
parent 77923b231d
commit 85b71b2a3e
6 changed files with 63 additions and 23 deletions

View File

@ -22,7 +22,6 @@ try {
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
var_dump($e->getMessage()); var_dump($e->getMessage());
} }
var_dump(file_exists('.env'));
if (file_exists('.env')) { if (file_exists('.env')) {
echo 'Loading .env...'.PHP_EOL; echo 'Loading .env...'.PHP_EOL;
$dotenv = new Dotenv\Dotenv(getcwd()); $dotenv = new Dotenv\Dotenv(getcwd());
@ -74,17 +73,19 @@ echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('s.madeline', $Madel
$offset = 0; $offset = 0;
while (true) { while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout try {
//\danog\MadelineProto\Logger::log([$updates]); $updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) { //\danog\MadelineProto\Logger::log([$updates]);
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id foreach ($updates as $update) {
switch ($update['update']['_']) { $offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
case 'updateNewEncryptedMessage': switch ($update['update']['_']) {
$i = 0; case 'updateNewEncryptedMessage':
while (true) { $i = 0;
$MadelineProto->messages->sendEncrypted(['peer' => $update['update']['message']['chat_id'], 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => $i++]]); while (true) {
} $MadelineProto->messages->sendEncrypted(['peer' => $update['update']['message']['chat_id'], 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => $i++]]);
}
}
} }
} } catch (\danog\MadelineProto\SecurityException $e) { ; }
echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('bot.madeline', $MadelineProto).' bytes'.PHP_EOL; echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('bot.madeline', $MadelineProto).' bytes'.PHP_EOL;
} }

View File

@ -251,7 +251,7 @@ trait UpdateHandler
$this->set_update_state($difference['state']); $this->set_update_state($difference['state']);
//var_dump($difference['new_encrypted_messages']); //var_dump($difference['new_encrypted_messages']);
foreach ($difference['new_encrypted_messages'] as $encrypted) { foreach ($difference['new_encrypted_messages'] as $encrypted) {
$this->handle_encrypted_update(['_' => 'updateNewEncryptedMessage', 'message' => $encrypted]); $this->handle_encrypted_update(['_' => 'updateNewEncryptedMessage', 'message' => $encrypted], true);
} }
$this->handle_multiple_update($difference['other_updates']); $this->handle_multiple_update($difference['other_updates']);
$this->handle_update_messages($difference['new_messages']); $this->handle_update_messages($difference['new_messages']);

View File

@ -24,6 +24,12 @@ trait AuthKeyHandler
public function accept_secret_chat($params) public function accept_secret_chat($params)
{ {
if ($this->secret_chat_status($params['id']) !== 0) {
//var_dump($this->secret_chat_status($params['id']));
\danog\MadelineProto\Logger::log(["I've already accepted secret chat ".$params['id']]);
return false;
}
$this->should_serialize = true; $this->should_serialize = true;
$dh_config = $this->get_dh_config(); $dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
@ -85,9 +91,8 @@ trait AuthKeyHandler
unset($this->temp_requested_secret_chats[$params['id']]); unset($this->temp_requested_secret_chats[$params['id']]);
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8); $key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
//var_dump($key); //var_dump($key);
if ($key['fingerprint'] !== $params['key_fingerprint']) { if ($key['fingerprint'] !== $params['key_fingerprint']) {
$this->method_call('messages.discardEncryption', ['chat_id' => $params['id']], ['datacenter' => $this->datacenter->curdc]); $this->discard_secret_chat($params['id']);
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!'); throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
} }
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16); $key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
@ -232,4 +237,22 @@ trait AuthKeyHandler
{ {
return $this->secret_chats[$chat]; return $this->secret_chats[$chat];
} }
public function discard_secret_chat($chat) {
if (isset($this->secret_chats[$chat])) {
unset($this->secret_chats[$chat]);
}
if (isset($this->temp_requested_secret_chats[$chat])) {
unset($this->temp_requested_secret_chats[$chat]);
}
if (isset($this->temp_rekeyed_secret_chats[$chat])) {
unset($this->temp_rekeyed_secret_chats[$chat]);
}
try {
$this->method_call('messages.discardEncryption', ['chat_id' => $chat], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc !== "ENCRYPTION_ALREADY_DECLINED") throw $e;
}
}
} }

View File

@ -29,8 +29,9 @@ trait MessageHandler
if (($this->secret_chats[$chat_id]['ttr'] <= 0 || time() - $this->secret_chats[$chat_id]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$chat_id]['rekeying'][0] === 0) { if (($this->secret_chats[$chat_id]['ttr'] <= 0 || time() - $this->secret_chats[$chat_id]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$chat_id]['rekeying'][0] === 0) {
$this->rekey($chat_id); $this->rekey($chat_id);
} }
if ($this->secret_chats[$chat_id]['layer'] !== 8) { if ($this->secret_chats[$chat_id]['layer'] > 8) {
$message = ['_' => 'decryptedMessageLayer', 'layer' => $this->secret_chats[$chat_id]['layer'], 'in_seq_no' => $this->generate_secret_in_seq_no($chat_id), 'out_seq_no' => $this->generate_secret_out_seq_no($chat_id), 'message' => $message]; $message = ['_' => 'decryptedMessageLayer', 'layer' => $this->secret_chats[$chat_id]['layer'], 'in_seq_no' => $this->generate_secret_in_seq_no($chat_id), 'out_seq_no' => $this->generate_secret_out_seq_no($chat_id), 'message' => $message];
$this->secret_chats[$chat_id]['out_seq_no']++;
} }
$this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message; $this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message;
$message = $this->serialize_object(['type' => $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $this->secret_chats[$chat_id]['layer']); $message = $this->serialize_object(['type' => $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $this->secret_chats[$chat_id]['layer']);
@ -40,14 +41,13 @@ trait MessageHandler
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], 'to server'); list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], 'to server');
$message .= $this->random($this->posmod(-strlen($message), 16)); $message .= $this->random($this->posmod(-strlen($message), 16));
$this->secret_chats[$chat_id]['out_seq_no']++;
$message = $this->secret_chats[$chat_id]['key']['fingerprint'].$message_key.$this->ige_encrypt($message, $aes_key, $aes_iv); $message = $this->secret_chats[$chat_id]['key']['fingerprint'].$message_key.$this->ige_encrypt($message, $aes_key, $aes_iv);
return $message; return $message;
} }
public function handle_encrypted_update($message) public function handle_encrypted_update($message, $test = false)
{ {
if (!isset($this->secret_chats[$message['message']['chat_id']])) { if (!isset($this->secret_chats[$message['message']['chat_id']])) {
\danog\MadelineProto\Logger::log('I do not have the secret chat '.$message['message']['chat_id'].' in the database, skipping message...'); \danog\MadelineProto\Logger::log('I do not have the secret chat '.$message['message']['chat_id'].' in the database, skipping message...');
@ -60,10 +60,12 @@ trait MessageHandler
//var_dump($auth_key_id, $this->secret_chats[$message['message']['chat_id']]['key']['fingerprint']); //var_dump($auth_key_id, $this->secret_chats[$message['message']['chat_id']]['key']['fingerprint']);
if (isset($this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint'])) { if (isset($this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint'])) {
if ($auth_key_id !== $this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint']) { if ($auth_key_id !== $this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint']) {
$this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException('Key fingerprint mismatch'); throw new \danog\MadelineProto\SecurityException('Key fingerprint mismatch');
} }
$old = true; $old = true;
} else { } else {
$this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException('Key fingerprint mismatch'); throw new \danog\MadelineProto\SecurityException('Key fingerprint mismatch');
} }
} }
@ -94,7 +96,11 @@ trait MessageHandler
} }
unset($message['message']['bytes']); unset($message['message']['bytes']);
$message['message']['decrypted_message'] = $deserialized; $message['message']['decrypted_message'] = $deserialized;
$this->secret_chats[$message['message']['chat_id']]['incoming'][$this->secret_chats[$message['message']['chat_id']]['in_seq_no']++] = $message['message']; if ($test) {
var_dump($message);
}
$this->secret_chats[$message['message']['chat_id']]['incoming'][$this->secret_chats[$message['message']['chat_id']]['in_seq_no']] = $message['message'];
//var_dump($message); //var_dump($message);
$this->handle_decrypted_update($message); $this->handle_decrypted_update($message);
} }

View File

@ -72,6 +72,13 @@ trait ResponseHandler
break; break;
case 'decryptedMessageLayer': case 'decryptedMessageLayer':
if ($this->check_secret_out_seq_no($update['message']['chat_id'], $update['message']['decrypted_message']['out_seq_no']) && $this->check_secret_in_seq_no($update['message']['chat_id'], $update['message']['decrypted_message']['in_seq_no'])) { if ($this->check_secret_out_seq_no($update['message']['chat_id'], $update['message']['decrypted_message']['out_seq_no']) && $this->check_secret_in_seq_no($update['message']['chat_id'], $update['message']['decrypted_message']['in_seq_no'])) {
$this->secret_chats[$update['message']['chat_id']]['in_seq_no']++;
if ($update['message']['decrypted_message']['layer'] >= 17) {
$this->secret_chats[$update['message']['chat_id']]['layer'] = $update['message']['decrypted_message']['layer'];
if ($update['message']['decrypted_message']['layer'] >= 17 && time() - $this->secret_chats[$update['message']['chat_id']]['created'] > 15) {
$this->notify_layer($update['message']['chat_id']);
}
}
$update['message']['decrypted_message'] = $update['message']['decrypted_message']['message']; $update['message']['decrypted_message'] = $update['message']['decrypted_message']['message'];
$this->handle_decrypted_update($update); $this->handle_decrypted_update($update);
} }

View File

@ -24,14 +24,14 @@ trait SeqNoHandler
foreach ($this->secret_chats[$chat_id]['incoming'] as $message) { foreach ($this->secret_chats[$chat_id]['incoming'] as $message) {
if (isset($message['decrypted_message']['in_seq_no'])) { if (isset($message['decrypted_message']['in_seq_no'])) {
if (($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2 < $last) { if (($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2 < $last) {
unset($this->secret_chats[$chat_id]); $this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('in_seq_no is not increasing'); throw new \danog\MadelineProto\SecurityException('in_seq_no is not increasing');
} }
$last = ($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2; $last = ($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2;
} }
} }
if ($seqno > $this->secret_chats[$chat_id]['out_seq_no'] + 1) { if ($seqno > $this->secret_chats[$chat_id]['out_seq_no'] + 1) {
unset($this->secret_chats[$chat_id]); $this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('in_seq_no is too big'); throw new \danog\MadelineProto\SecurityException('in_seq_no is too big');
} }
@ -43,14 +43,17 @@ trait SeqNoHandler
$seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2; $seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2;
$C = 0; $C = 0;
foreach ($this->secret_chats[$chat_id]['incoming'] as $message) { foreach ($this->secret_chats[$chat_id]['incoming'] as $message) {
if (isset($message['decrypted_message']['out_seq_no']) && $C !== $this->secret_chats[$chat_id]['in_seq_no'] - 1) { var_dump($message);
if (isset($message['decrypted_message']['out_seq_no']) && $C < $this->secret_chats[$chat_id]['in_seq_no']) {
if (($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2 !== $C) { if (($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2 !== $C) {
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('out_seq_no hole: should be '.$C.', is '.(($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2)); throw new \danog\MadelineProto\SecurityException('out_seq_no hole: should be '.$C.', is '.(($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2));
} else { } else {
$C++; $C++;
} }
} }
} }
var_dump($C, $seqno);
if ($seqno < $C) { // <= C if ($seqno < $C) { // <= C
\danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno '.$seqno]); \danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno '.$seqno]);
$this->secret_chats[$chat_id]['in_seq_no']--; $this->secret_chats[$chat_id]['in_seq_no']--;
@ -58,7 +61,7 @@ trait SeqNoHandler
return false; return false;
} }
if ($seqno > $C) { // > C+1 if ($seqno > $C) { // > C+1
unset($this->secret_chats[$chat_id]); $this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('WARNING: out_seq_no gap detected ('.$seqno.' > '.$C.')!'); throw new \danog\MadelineProto\SecurityException('WARNING: out_seq_no gap detected ('.$seqno.' > '.$C.')!');
return false; return false;
} }