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) {
var_dump($e->getMessage());
}
var_dump(file_exists('.env'));
if (file_exists('.env')) {
echo 'Loading .env...'.PHP_EOL;
$dotenv = new Dotenv\Dotenv(getcwd());
@ -74,17 +73,19 @@ echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('s.madeline', $Madel
$offset = 0;
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
//\danog\MadelineProto\Logger::log([$updates]);
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
switch ($update['update']['_']) {
case 'updateNewEncryptedMessage':
$i = 0;
while (true) {
$MadelineProto->messages->sendEncrypted(['peer' => $update['update']['message']['chat_id'], 'message' => ['_' => 'decryptedMessage', 'ttl' => 0, 'message' => $i++]]);
}
try {
$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
//\danog\MadelineProto\Logger::log([$updates]);
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
switch ($update['update']['_']) {
case 'updateNewEncryptedMessage':
$i = 0;
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;
}

View File

@ -251,7 +251,7 @@ trait UpdateHandler
$this->set_update_state($difference['state']);
//var_dump($difference['new_encrypted_messages']);
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_update_messages($difference['new_messages']);

View File

@ -24,6 +24,12 @@ trait AuthKeyHandler
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;
$dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
@ -85,9 +91,8 @@ trait AuthKeyHandler
unset($this->temp_requested_secret_chats[$params['id']]);
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
//var_dump($key);
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!');
}
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
@ -232,4 +237,22 @@ trait AuthKeyHandler
{
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) {
$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];
$this->secret_chats[$chat_id]['out_seq_no']++;
}
$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']);
@ -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');
$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);
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']])) {
\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']);
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']) {
$this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException('Key fingerprint mismatch');
}
$old = true;
} else {
$this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException('Key fingerprint mismatch');
}
}
@ -94,7 +96,11 @@ trait MessageHandler
}
unset($message['message']['bytes']);
$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);
$this->handle_decrypted_update($message);
}

View File

@ -72,6 +72,13 @@ trait ResponseHandler
break;
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'])) {
$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'];
$this->handle_decrypted_update($update);
}

View File

@ -24,14 +24,14 @@ trait SeqNoHandler
foreach ($this->secret_chats[$chat_id]['incoming'] as $message) {
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) {
unset($this->secret_chats[$chat_id]);
$this->discard_secret_chat($chat_id);
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;
}
}
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');
}
@ -43,14 +43,17 @@ trait SeqNoHandler
$seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2;
$C = 0;
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) {
$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));
} else {
$C++;
}
}
}
var_dump($C, $seqno);
if ($seqno < $C) { // <= C
\danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno '.$seqno]);
$this->secret_chats[$chat_id]['in_seq_no']--;
@ -58,7 +61,7 @@ trait SeqNoHandler
return false;
}
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.')!');
return false;
}