Implemented VoIP key generation

This commit is contained in:
Daniil Gentili 2017-04-02 16:42:17 +02:00
parent cd1040ec19
commit c438a57513
7 changed files with 93 additions and 48 deletions

View File

@ -80,6 +80,7 @@ class MTProto
$this->zero = new \phpseclib\Math\BigInteger(0);
$this->one = new \phpseclib\Math\BigInteger(1);
$this->two = new \phpseclib\Math\BigInteger(2);
$this->three = new \phpseclib\Math\BigInteger(3);
$this->four = new \phpseclib\Math\BigInteger(4);
$this->twoe1984 = new \phpseclib\Math\BigInteger('1751908409537131537220509645351687597690304110853111572994449976845956819751541616602568796259317428464425605223064365804210081422215355425149431390635151955247955156636234741221447435733643262808668929902091770092492911737768377135426590363166295684370498604708288556044687341394398676292971255828404734517580702346564613427770683056761383955397564338690628093211465848244049196353703022640400205739093118270803778352768276670202698397214556629204420309965547056893233608758387329699097930255380715679250799950923553703740673620901978370802540218870279314810722790539899334271514365444369275682816');
@ -282,9 +283,10 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
],
'calls' => [
'accept_calls' => true, // Should I accept calls? Can be true, false or on array of user ids from which to accept calls
'allow_p2p' => false // Should I accept p2p calls?
],
'threading' => [
'allow_threading' => true, // Should I use threading, if it is enabled?
'allow_threading' => false, // Should I use threading, if it is enabled?
'handler_workers' => 10, // How many workers should every message handler pool of each socket reader have
],
'pwr' => ['pwr' => false, 'db_token' => false, 'strict' => false],
@ -441,7 +443,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
public function getV()
{
return 8;
return 9;
}
public function get_self()

View File

@ -20,6 +20,7 @@ trait AckHandler
public function ack_outgoing_message_id($message_id, $datacenter)
{
// The server acknowledges that it received my message
//var_dump($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]);
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) {
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);

View File

@ -495,8 +495,18 @@ trait UpdateHandler
switch ($update['phone_call']['_']) {
case 'phoneCallRequested':
return $this->accept_call($update['phone_call']);
case 'phoneCallAccepted':
$this->confirm_call($update['phone_call']);
return;
case 'phoneCall':
return $this->complete_call($update['phone_call']);
$this->complete_call($update['phone_call']);
break;
case 'phoneCallDiscarded':
\danog\MadelineProto\Logger::log(['Revoking call '.$update['phone_call']['id']], \danog\MadelineProto\Logger::NOTICE);
if (isset($this->secret_chats[$update['phone_call']['id']])) {
unset($this->secret_chats[$update['phone_call']['id']]);
}
break;
}
}
if ($update['_'] === 'updateNewEncryptedMessage' && !isset($update['message']['decrypted_message'])) {
@ -546,15 +556,15 @@ trait UpdateHandler
unset($this->temp_requested_secret_chats[$update['chat']['id']]);
}
return;
break;
case 'encryptedChat':
\danog\MadelineProto\Logger::log(['Completing creation of secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
return $this->complete_secret_chat($update['chat']);
$this->complete_secret_chat($update['chat']);
return;
}
\danog\MadelineProto\Logger::log([$update], \danog\MadelineProto\Logger::NOTICE);
//\danog\MadelineProto\Logger::log([$update], \danog\MadelineProto\Logger::NOTICE);
return;
}
if (!$this->settings['updates']['handle_updates']) {
return;

View File

@ -212,4 +212,5 @@ trait AuthKeyHandler
return 0;
}
public function get_secret_chat($chat) { return $this->secret_chats[$chat]; }
}

View File

@ -45,7 +45,7 @@ class SocketReader extends \Threaded implements \Collectable
require_once __DIR__.'/../TL/Exception.php';
require_once __DIR__.'/../NothingInTheSocketException.php';
require_once __DIR__.'/../Exception.php';
$handler_pool = new \Pool(2);
$handler_pool = new \Pool($this->API->settings['threading']['handler_workers']);
while ($this->API->run_workers) {
try {

View File

@ -20,83 +20,108 @@ namespace danog\MadelineProto\VoIP;
*/
trait AuthKeyHandler
{
private $temp_requested_calls = [];
private $calls = [];
public function accept_call($params)
{
$dh_config = $this->get_dh_config();
$phone_config = $this->method_call('phone.getCallConfig');
$b = new \phpseclib\Math\BigInteger($this->random(256), 256);
$params['g_a'] = new \phpseclib\Math\BigInteger($params['g_a'], 256);
$this->check_G($params['g_a'], $dh_config['p']);
$key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
$this->calls[$params['id']] = ['key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'layer' => 65, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0], 'protocol' => $params['protocol'], 'connection' => $params['connection'], 'alternative_connections' => $params['alternative_connections']];
//$this->calls[$params['id']] = ['key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputEncryptedChat' => ['_' => 'inputEncryptedChat', 'chat_id' => $params['id'], 'access_hash' => $params['access_hash']], 'in_seq_no_x' => 1, 'out_seq_no_x' => 0, 'layer' => 8, 'ttl' => PHP_INT_MAX, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0]];
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
$this->check_G($g_b, $dh_config['p']);
$this->handle_pending_updates();
}
public $REQUESTED = 0;
public $ACCEPTED = 1;
public $CONFIRMED = 2;
public $READY = 3;
private $emojis = ['😉', '😍', '😛', '😭', '😱', '😡', '😎', '😴', '😵', '😈', '😬', '😇', '😏', '👮', '👷', '💂', '👶', '👨', '👩', '👴', '👵', '😻', '😽', '🙀', '👺', '🙈', '🙉', '🙊', '💀', '👽', '💩', '🔥', '💥', '💤', '👂', '👀', '👃', '👅', '👄', '👍', '👎', '👌', '👊', '✌', '✋', '👐', '👆', '👇', '👉', '👈', '🙏', '👏', '💪', '🚶', '🏃', '💃', '👫', '👪', '👬', '👭', '💅', '🎩', '👑', '👒', '👟', '👞', '👠', '👕', '👗', '👖', '👙', '👜', '👓', '🎀', '💄', '💛', '💙', '💜', '💚', '💍', '💎', '🐶', '🐺', '🐱', '🐭', '🐹', '🐰', '🐸', '🐯', '🐨', '🐻', '🐷', '🐮', '🐗', '🐴', '🐑', '🐘', '🐼', '🐧', '🐥', '🐔', '🐍', '🐢', '🐛', '🐝', '🐜', '🐞', '🐌', '🐙', '🐚', '🐟', '🐬', '🐋', '🐐', '🐊', '🐫', '🍀', '🌹', '🌻', '🍁', '🌾', '🍄', '🌵', '🌴', '🌳', '🌞', '🌚', '🌙', '🌎', '🌋', '⚡', '☔', '❄', '⛄', '🌀', '🌈', '🌊', '🎓', '🎆', '🎃', '👻', '🎅', '🎄', '🎁', '🎈', '🔮', '🎥', '📷', '💿', '💻', '☎', '📡', '📺', '📻', '🔉', '🔔', '⏳', '⏰', '⌚', '🔒', '🔑', '🔎', '💡', '🔦', '🔌', '🔋', '🚿', '🚽', '🔧', '🔨', '🚪', '🚬', '💣', '🔫', '🔪', '💊', '💉', '💰', '💵', '💳', '✉', '📫', '📦', '📅', '📁', '✂', '📌', '📎', '✒', '✏', '📐', '📚', '🔬', '🔭', '🎨', '🎬', '🎤', '🎧', '🎵', '🎹', '🎻', '🎺', '🎸', '👾', '🎮', '🃏', '🎲', '🎯', '🏈', '🏀', '⚽', '⚾', '🎾', '🎱', '🏉', '🎳', '🏁', '🏇', '🏆', '🏊', '🏄', '☕', '🍼', '🍺', '🍷', '🍴', '🍕', '🍔', '🍟', '🍗', '🍱', '🍚', '🍜', '🍡', '🍳', '🍞', '🍩', '🍦', '🎂', '🍰', '🍪', '🍫', '🍭', '🍯', '🍎', '🍏', '🍊', '🍋', '🍒', '🍇', '🍉', '🍓', '🍑', '🍌', '🍐', '🍍', '🍆', '🍅', '🌽', '🏡', '🏥', '🏦', '⛪', '🏰', '⛺', '🏭', '🗻', '🗽', '🎠', '🎡', '⛲', '🎢', '🚢', '🚤', '⚓', '🚀', '✈', '🚁', '🚂', '🚋', '🚎', '🚌', '🚙', '🚗', '🚕', '🚛', '🚨', '🚔', '🚒', '🚑', '🚲', '🚠', '🚜', '🚦', '⚠', '🚧', '⛽', '🎰', '🗿', '🎪', '🎭', '🇯🇵', '🇰🇷', '🇩🇪', '🇨🇳', '🇺🇸', '🇫🇷', '🇪🇸', '🇮🇹', '🇷🇺', '🇬🇧', '1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '0⃣', '🔟', '❗', '❓', '♥', '♦', '💯', '🔗', '🔱', '🔴', '🔵', '🔶', '🔷'];
public function request_call($user)
{
$user = $this->get_info($user)['InputUser'];
\danog\MadelineProto\Logger::log(['Calling '.$user['user_id'].'...'], \danog\MadelineProto\Logger::VERBOSE);
$dh_config = $this->get_dh_config();
$phone_config = $this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]);
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
$a = new \phpseclib\Math\BigInteger($this->random(256), 256);
$a = \phpseclib\Math\BigInteger::randomRange($this->two, $dh_config['p']->subtract($this->two));
\danog\MadelineProto\Logger::log(['Generating g_a...'], \danog\MadelineProto\Logger::VERBOSE);
$g_a = $dh_config['g']->powMod($a, $dh_config['p']);
$this->check_G($g_a, $dh_config['p']);
$res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a_hash' => hash('sha256', $g_a->toBytes(), true), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => $this->settings['tl_schema']['layer'], 'max_layer' => $this->settings['tl_schema']['layer']]], ['datacenter' => $this->datacenter->curdc]);
$this->temp_requested_calls[$res['phone_call']['id']] = $a;
$this->calls[$res['phone_call']['id']] = ['status' => $this->REQUESTED, 'a' => $a, 'g_a' => $g_a];
$this->handle_pending_updates();
$this->get_updates_difference();
return $res['phone_call']['id'];
}
public function complete_call($params)
public function accept_call($params)
{
if ($this->call_status($params['id']) !== 1) {
\danog\MadelineProto\Logger::log(['Could not find and complete secret chat '.$params['id']]);
if ($this->settings['calls']['accept_calls'] === false) return false;
if (is_array($this->settings['calls']['accept_calls']) && !in_array($this->settings['calls']['accept_calls'])) return false;
if ($params['protocol']['udp_p2p'] && !$this->settings['calls']['allow_p2p']) {
return false;
}
$dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
$b = \phpseclib\Math\BigInteger::randomRange($this->two, $dh_config['p']->subtract($this->two));
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
$this->check_G($g_b, $dh_config['p']);
$this->calls[$res['phone_call']['id']] = ['status' => $this->ACCEPTED, 'b' => $b, 'g_a_hash' => $params['g_a_hash']];
$res = $this->method_call('phone.acceptCall', ['user_id' => $user, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => $this->settings['tl_schema']['layer'], 'max_layer' => $this->settings['tl_schema']['layer']]], ['datacenter' => $this->datacenter->curdc]);
$this->handle_pending_updates();
$this->get_updates_difference();
}
public function confirm_call($params)
{
if ($this->call_status($params['id']) !== $this->REQUESTED) {
\danog\MadelineProto\Logger::log(['Could not find and confirm call '.$params['id']]);
return false;
}
$dh_config = $this->get_dh_config();
$params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256);
$this->check_G($params['g_b'], $dh_config['p']);
$key = ['auth_key' => str_pad($params['g_b']->powMod($this->calls[$params['id']]['a'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
$res = $this->method_call('phone.confirmCall', ['user_id' => $user, 'g_a' => $this->calls[$params['id']]['g_a']->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => $this->settings['tl_schema']['layer'], 'max_layer' => $this->settings['tl_schema']['layer']]], ['datacenter' => $this->datacenter->curdc]);
$key['visualization'] = '';
$length = new \phpseclib\Math\BigInteger(count($this->emojis));
foreach (str_split(strrev(substr(hash('sha256', $this->calls[$params['id']]['g_a']->toBytes().$key['auth_key'], true), 20)), 8) as $number) {
$key['visualization'] .= $this->emojis[(int)((new \phpseclib\Math\BigInteger($number, -256))->divide($length)[1]->toString())];
}
$this->calls[$params['id']] = ['status' => $this->READY, 'key' => $key, 'admin' => true, 'user_id' => $params['participant_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'layer' => $this->settings['tl_scheme']['layer'], 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'protocol' => $params['protocol']];
$this->handle_pending_updates();
}
public function complete_call($params) {
if ($this->call_status($params['id']) !== $this->ACCEPTED) {
\danog\MadelineProto\Logger::log(['Could not find and confirm call '.$params['id']]);
return false;
}
$dh_config = $this->get_dh_config();
if (hash('sha256', $key['g_a_or_b'], true) !== $this->calls[$params['id']]['g_a_hash']) {
throw new \danog\MadelineProto\SecurityException('Invalid g_a!');
}
$params['g_a_or_b'] = new \phpseclib\Math\BigInteger($params['g_a_or_b'], 256);
$this->check_G($params['g_a_or_b'], $dh_config['p']);
$key = ['auth_key' => str_pad($params['g_a_or_b']->powMod($this->temp_requested_calls[$params['id']], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
unset($this->temp_requested_calls[$params['id']]);
$key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
if ($key['fingerprint'] !== $params['key_fingerprint']) {
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
}
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
$this->calls[$params['id']] = ['key' => $key, 'admin' => true, 'user_id' => $params['participant_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'layer' => 65, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0], 'protocol' => $params['protocol'], 'connection' => $params['connection'], 'alternative_connections' => $params['alternative_connections']];
$this->handle_pending_updates();
$key['visualization'] = '';
$length = new \phpseclib\Math\BigInteger(count($this->emojis));
foreach (str_split(strrev(substr(hash('sha256', $params['g_a_or_b']->toBytes().$key['auth_key'], true), 20)), 8) as $number) {
$key['visualization'] .= $this->emojis[(int)((new \phpseclib\Math\BigInteger($number, -256))->divide($length)[1]->toString())];
}
$this->calls[$params['id']] = ['status' => $this->READY, 'key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 1, 'out_seq_no_x' => 0, 'layer' => $this->settings['tl_scheme']['layer'], 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'protocol' => $params['protocol']];
}
public function call_status($id)
{
if (isset($this->calls[$id])) {
return 2;
}
if (isset($this->temp_requested_calls[$id])) {
return 1;
return $this->calls[$id]['status'];
}
return 0;
return -1;
}
public function get_secret_chat($chat)
public function get_call($chat)
{
return $this->calls[$chat];
}
}

View File

@ -66,11 +66,17 @@ if ($MadelineProto === false) {
}
$message = (getenv('TRAVIS_COMMIT') == '') ? 'I iz works always (io laborare sembre) (yo lavorar siempre) (mi labori ĉiam) (я всегда работать) (Ik werkuh altijd)' : ('Travis ci tests in progress: commit '.getenv('TRAVIS_COMMIT').', job '.getenv('TRAVIS_JOB_NUMBER').', PHP version: '.getenv('TRAVIS_PHP_VERSION'));
//$MadelineProto->API->request_call('@danogentili');
echo 'Serializing MadelineProto to session.madeline...'.PHP_EOL;
echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('session.madeline', $MadelineProto).' bytes'.PHP_EOL;
echo 'Size of MadelineProto instance is '.strlen(serialize($MadelineProto)).' bytes'.PHP_EOL;
/*
$call = $MadelineProto->API->request_call(getenv('TEST_SECRET_CHAT'));
echo 'Waiting for '.getenv('TEST_SECRET_CHAT').' to accept the call...'.PHP_EOL;
while ($MadelineProto->call_status($call) !== $MadelineProto->API->READY) {
$MadelineProto->get_updates();
}
var_dump($MadelineProto->get_call($call));
*/
$secret = $MadelineProto->API->request_secret_chat(getenv('TEST_SECRET_CHAT'));
echo 'Waiting for '.getenv('TEST_SECRET_CHAT').' to accept the secret chat...'.PHP_EOL;
while ($MadelineProto->secret_chat_status($secret) !== 2) {