Start writing tdesktop conversion

This commit is contained in:
Daniil Gentili 2018-04-12 21:46:19 +02:00
parent f246f5aa40
commit d8a61db7ac

View File

@ -15,6 +15,56 @@ namespace danog\MadelineProto;
class Conversion
{
public static function random($length)
{
return $length === 0 ? '' : \phpseclib\Crypt\Random::string($length);
}
public static function unpack_signed_int($value)
{
if (strlen($value) !== 4) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_4']);
}
return unpack('l', !\danog\MadelineProto\Magic::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public static function pack_signed_int($value)
{
if ($value > 2147483647) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_bigger_than_2147483647'], $value));
}
if ($value < -2147483648) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_2147483648'], $value));
}
$res = pack('l', $value);
return !\danog\MadelineProto\Magic::$BIG_ENDIAN ? strrev($res) : $res;
}
public static function old_aes_calculate($msg_key, $auth_key, $to_server = true)
{
$x = $to_server ? 0 : 8;
$sha1_a = sha1($msg_key.substr($auth_key, $x, 32), true);
$sha1_b = sha1(substr($auth_key, 32 + $x, 16).$msg_key.substr($auth_key, 48 + $x, 16), true);
$sha1_c = sha1(substr($auth_key, 64 + $x, 32).$msg_key, true);
$sha1_d = sha1($msg_key.substr($auth_key, 96 + $x, 32), true);
$aes_key = substr($sha1_a, 0, 8).substr($sha1_b, 8, 12).substr($sha1_c, 4, 12);
$aes_iv = substr($sha1_a, 8, 12).substr($sha1_b, 0, 8).substr($sha1_c, 16, 4).substr($sha1_d, 0, 8);
return [$aes_key, $aes_iv];
}
public static function ige_decrypt($message, $key, $iv)
{
$cipher = new \phpseclib\Crypt\AES('ige');
$cipher->setKey($key);
$cipher->setIV($iv);
return @$cipher->decrypt($message);
}
public static function telethon($session, $new_session, $settings = [])
{
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
@ -78,4 +128,116 @@ class Conversion
$MadelineProto->API->init_authorization();
return $MadelineProto;
}
public static function tdesktop_md5($data) {
$result = '';
foreach (str_split(md5($data), 2) as $byte) {
$result .= strrev($byte);
}
return strtoupper($result);
}
const FILEOPTION_SAFE = 1;
const FILEOPTION_USER = 2;
public static $tdesktop_base_path;
public static $tdesktop_user_base_path;
public static $tdesktop_key;
public static function tdesktop_fopen($fileName, $options = 3) {
$name = ($options & self::FILEOPTION_USER ? self::$tdesktop_user_base_path : self::$tdesktop_base_path) . $fileName;
$totry = [];
for ($x = 0; $x <= 1; $x++) {
if (file_exists($name.$x)) $totry []= fopen($name.$x, 'rb');
}
foreach ($totry as $fp) {
if (stream_get_contents($fp, 4) !== 'TDF$') {
\danog\MadelineProto\Logger::log('Wrong magic', Logger::ERROR);
continue;
}
$versionBytes = stream_get_contents($fp, 4);
$version = self::unpack_signed_int($versionBytes);
\danog\MadelineProto\Logger::log("TDesktop version: $version");
$data = stream_get_contents($fp);
$md5 = substr($data, -16);
$data = substr($data, 0, -16);
$length = pack('l', strlen($data));
$length = \danog\MadelineProto\Magic::$BIG_ENDIAN ? strrev($length) : $length;
if (md5($data.$length.$versionBytes.'TDF$', true) !== $md5) {
\danog\MadelineProto\Logger::log('Wrong MD5', Logger::ERROR);
}
$res = fopen('php://memory', 'rw+b');
fwrite($res, $data);
fseek($res, 0);
return $res;
}
throw new Exception("Could not open $fileName");
}
public static function tdesktop_fopen_encrypted($fileName, $options = 3) {
$f = self::tdesktop_fopen($fileName, $options);
$data = self::tdesktop_read_bytearray($f);
return self::tdesktop_decrypt($data, self::$tdesktop_key);
}
public static function tdesktop_read_bytearray($fp) {
$length = self::unpack_signed_int(stream_get_contents($fp, 4));
$data = $length ? stream_get_contents($fp, $length) : '';
$res = fopen('php://memory', 'rw+b');
fwrite($res, $data);
fseek($res, 0);
return $res;
}
public static function tdesktop_decrypt($data, $auth_key) {
$message_key = stream_get_contents($data, 16);
$encrypted_data = stream_get_contents($data);
list($aes_key, $aes_iv) = self::old_aes_calculate($message_key, $auth_key, false);
$decrypted_data = self::ige_decrypt($encrypted_data, $aes_key, $aes_iv);
if ($message_key != substr(sha1($decrypted_data, true), 0, 16)) {
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
}
$res = fopen('php://memory', 'rw+b');
fwrite($res, $decrypted_data);
fseek($res, 0);
return $res;
}
public static function tdesktop($session, $new_session, $settings = []) {
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
if (!isset($settings['old_session_key'])) $settings['old_session_key'] = 'data';
if (!isset($settings['old_session_passcode'])) $settings['old_session_passcode'] = '';
list($part_one_md5, $part_two_md5) = str_split(self::tdesktop_md5($settings['old_session_key']), 16);
self::$tdesktop_base_path = $session.'/';
self::$tdesktop_user_base_path = self::$tdesktop_base_path.$part_one_md5.'/';
$data = self::tdesktop_fopen('map');
$salt = self::tdesktop_read_bytearray($data);
$salt = fstat($salt)['size'] ? $salt : self::random(32);
$encryptedKey = self::tdesktop_read_bytearray($data);
$keyIterCount = strlen($settings['old_session_passcode']) ? 4000 : 4;
$passKey = openssl_pbkdf2($settings['old_session_passcode'], stream_get_contents($salt), 256, $keyIterCount);
self::$tdesktop_key = stream_get_contents(self::tdesktop_read_bytearray(self::tdesktop_decrypt($encryptedKey, $passKey)));
$main = self::tdesktop_fopen_encrypted($part_one_md5, self::FILEOPTION_SAFE);
$magic = self::pack_signed_int(0x4b);
while (($crc = stream_get_contents($main, 4)) !== $magic) {
}
$main = self::tdesktop_read_bytearray($main);
$user_id = self::unpack_signed_int(stream_get_contents($main, 4));
$dc_id = self::unpack_signed_int(stream_get_contents($main, 4));
$keys = [];
$length = self::unpack_signed_int(stream_get_contents($main, 4));
for ($x = 0; $x < $length; $x++) {
$keys[$x]['dc_id'] = self::unpack_signed_int(stream_get_contents($main, 4));
$keys[$x]['auth_key'] = self::tdesktop_read_bytearray($main);
}
var_dump($keys, $length, $dc_id, $user_id);
}
}