-
-
Notifications
You must be signed in to change notification settings - Fork 714
/
Copy pathObfuscatedStream.php
96 lines (91 loc) · 3.72 KB
/
ObfuscatedStream.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?php
declare(strict_types=1);
/**
* Obfuscated2 stream wrapper.
*
* This file is part of MadelineProto.
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU General Public License along with MadelineProto.
* If not, see <http://www.gnu.org/licenses/>.
*
* @author Daniil Gentili <daniil@daniil.it>
* @copyright 2016-2025 Daniil Gentili <daniil@daniil.it>
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
* @link https://docs.madelineproto.xyz MadelineProto documentation
*/
namespace danog\MadelineProto\Stream\MTProtoTransport;
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
use danog\MadelineProto\Stream\Common\CtrStream;
use danog\MadelineProto\Stream\ConnectionContext;
use danog\MadelineProto\Tools;
/**
* Obfuscated2 stream wrapper.
*
* Manages obfuscated2 encryption/decryption
*
* @author Daniil Gentili <daniil@daniil.it>
*
* @implements BufferedProxyStreamInterface<array{secret?: string, address?: string, port?: int}>
*/
final class ObfuscatedStream extends CtrStream implements BufferedProxyStreamInterface
{
private $stream;
private $extra;
/**
* Connect to stream.
*
* @param ConnectionContext $ctx The connection context
*/
#[\Override]
public function connect(ConnectionContext $ctx, string $header = ''): void
{
if (isset($this->extra['address'])) {
$ctx = $ctx->clone();
$ctx->setUri('tcp://'.$this->extra['address'].':'.$this->extra['port']);
}
do {
$random = Tools::random(64);
} while (\in_array(substr($random, 0, 4), ['PVrG', 'GET ', 'POST', 'HEAD', str_repeat(\chr(238), 4), str_repeat(\chr(221), 4)], true) || $random[0] === \chr(0xef) || substr($random, 4, 4) === "\0\0\0\0");
if (\strlen($header) === 1) {
$header = str_repeat($header, 4);
}
$random = substr_replace($random, $header.substr($random, 56 + \strlen($header)), 56);
$random = substr_replace($random, pack('s', $ctx->getDc()).substr($random, 60 + 2), 60);
$reversed = strrev($random);
$key = substr($random, 8, 32);
$keyRev = substr($reversed, 8, 32);
if (isset($this->extra['secret'])) {
$key = hash('sha256', $key.$this->extra['secret'], true);
$keyRev = hash('sha256', $keyRev.$this->extra['secret'], true);
}
$iv = substr($random, 40, 16);
$ivRev = substr($reversed, 40, 16);
parent::setExtra(['encrypt' => ['key' => $key, 'iv' => $iv], 'decrypt' => ['key' => $keyRev, 'iv' => $ivRev]]);
parent::connect($ctx);
$random = substr_replace($random, substr(@$this->getEncryptor()->encrypt($random), 56, 8), 56, 8);
$this->getStream()->write($random);
}
/**
* Set extra.
*/
#[\Override]
public function setExtra($extra): void
{
if (isset($extra['secret'])) {
if (\strlen($extra['secret']) > 17) {
$extra['secret'] = hex2bin($extra['secret']);
}
if (\strlen($extra['secret']) == 17) {
$extra['secret'] = substr($extra['secret'], 1, 16);
}
}
$this->extra = $extra;
}
#[\Override]
public static function getName(): string
{
return self::class;
}
}