From 01a8e2dbf37ac9775369db5baee9c5b8b3dac46f Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 16 Dec 2019 15:48:34 +0100 Subject: [PATCH] Implement ECDH --- src/BigIntegor.php | 2 +- .../MadelineProto/TON/ADNLConnection.php | 177 ++++++++++++++++++ src/danog/MadelineProto/TON/PrivateKey.php | 41 ++++ src/danog/MadelineProto/TON/PublicKey.php | 61 ++++++ 4 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 src/danog/MadelineProto/TON/ADNLConnection.php create mode 100644 src/danog/MadelineProto/TON/PrivateKey.php create mode 100644 src/danog/MadelineProto/TON/PublicKey.php diff --git a/src/BigIntegor.php b/src/BigIntegor.php index a209e2e5..d1f6289b 100644 --- a/src/BigIntegor.php +++ b/src/BigIntegor.php @@ -17,7 +17,7 @@ * @link https://docs.madelineproto.xyz MadelineProto documentation */ -namespace phpseclib3\Math; +namespace phpseclib\Math; if (PHP_MAJOR_VERSION < 7 && !(\class_exists(\Phar::class) && \Phar::running())) { throw new \Exception('MadelineProto requires php 7 to run natively, use phar.madelineproto.xyz to run on PHP 5.6'); diff --git a/src/danog/MadelineProto/TON/ADNLConnection.php b/src/danog/MadelineProto/TON/ADNLConnection.php new file mode 100644 index 00000000..15cba2fc --- /dev/null +++ b/src/danog/MadelineProto/TON/ADNLConnection.php @@ -0,0 +1,177 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2019 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * + * @link https://docs.madelineproto.xyz MadelineProto documentation + */ + +namespace danog\MadelineProto\TON; + +use Amp\Socket\ConnectContext; +use danog\MadelineProto\Magic; +use danog\MadelineProto\MTProtoTools\Crypt; +use danog\MadelineProto\Stream\ADNLTransport\ADNLStream; +use danog\MadelineProto\Stream\Common\BufferedRawStream; +use danog\MadelineProto\Stream\Common\HashedBufferedStream; +use danog\MadelineProto\Stream\ConnectionContext; +use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream; +use danog\MadelineProto\Stream\Transport\DefaultStream; +use danog\MadelineProto\TL\TL; +use danog\MadelineProto\Tools; +use phpseclib3\Crypt\DH; +use phpseclib3\Crypt\EC; +use phpseclib3\Crypt\EC\Curves\Curve25519; +use phpseclib3\Crypt\EC\PrivateKey; +use phpseclib3\Crypt\EC\PublicKey; +use phpseclib3\Math\BigInteger; + +class ADNLConnection +{ + /** + * TL serializer instance. + * + * @var TL + */ + private $TL; + /** + * ADNL stream instance. + * + * @var StreamInterface + */ + private $stream; + /** + * Construct class. + * + * @param TL $TL TL instance + */ + public function __construct(TL $TL) + { + $this->TL = $TL; + } + /** + * Connect to specified ADNL endpoint. + * + * @param array $endpoint Endpoint + * + * @return \Generator + */ + public function connect(array $endpoint): \Generator + { + if ($endpoint['_'] !== 'liteserver.desc') { + throw new \InvalidArgumentException('Only liteservers are supported at the moment!'); + } + if ($endpoint['id']['_'] !== 'pub.ed25519') { + throw new \InvalidArgumentException('Only ECDH is supported at the moment!'); + } + + $random = Tools::random(256 - 32 - 64); + $s1 = \substr($random, 0, 32); + $s2 = \substr($random, 32, 32); + $v1 = \substr($random, 64, 16); + $v2 = \substr($random, 80, 16); + + $obf = [ + 'decrypt' => [ + 'key' => $s1, + 'iv' => $v1 + ], + 'encrypt' => [ + 'key' => $s2, + 'iv' => $v2 + ], + ]; + // Generating new private/public params + $private = EC::createKey('Ed25519'); + + $public = $private->getPublicKey(); + $public = strrev(Tools::getVar($public, 'QA')[1]->toBytes()); + + $private = strrev(Tools::getVar($private, 'dA')->toBytes()); + $private = PrivateKey::loadFormat('MontgomeryPrivate', $private); + + // Transpose their public + $key = $endpoint['id']['key']; + $key[31] = $key[31] & chr(127); + + $curve = new Curve25519; + $modulo = Tools::getVar($curve, "modulo"); + $y = new BigInteger(strrev($key), 256); + $y2 = clone $y; + $y = $y->add(Magic::$one); + $y2 = $y2->subtract(Magic::$one); + $y2 = $modulo->subtract($y2)->powMod(Magic::$one, $modulo); + $y2 = $y2->modInverse($modulo); + + $key = strrev($y->multiply($y2)->powMod(Magic::$one, $modulo)->toBytes()); + $peerPublic = PublicKey::loadFormat('MontgomeryPublic', $key); + + // Generate secret + $secret = DH::computeSecret($private, $peerPublic); + + var_dumP($private, $peerPublic, bin2hex($secret)); + + // Encrypting random with obf keys + $digest = \hash('sha256', $random, true); + + $key = \substr($secret, 0, 16).\substr($digest, 16, 16); + $iv = \substr($digest, 0, 4).\substr($secret, 20, 12); + + $encryptedRandom = Crypt::ctrEncrypt($random, $key, $iv); + + // Generating plaintext init payload + $payload = \hash('sha256', yield $this->TL->serializeObject(['type' => ''], $endpoint['id'], 'key'), true); + $payload .= $public; + $payload .= $digest; + $payload .= $encryptedRandom; + + \var_dump(bin2hex($payload)); + + $ip = \long2ip(\unpack('V', Tools::packSignedInt($endpoint['ip']))[1]); + $port = $endpoint['port']; + $ctx = (new ConnectionContext()) + ->setSocketContext(new ConnectContext) + ->setUri("tcp://$ip:$port") + ->addStream(DefaultStream::getName()) + ->addStream(BufferedRawStream::getName()) + ->addStream(ObfuscatedStream::getName(), $obf) + ->addStream(HashedBufferedStream::getName(), 'sha256') + ->addStream(ADNLStream::getName()); + + $this->stream = yield $ctx->getStream($payload); + \var_dump("Connected"); + + Tools::callFork((function () { + yield Tools::sleep(2); + while (true) { + $buffer = yield $this->stream->getReadBuffer($length); + \var_dump($length, "GOT PACKET WITH LENGTH $length"); + \var_dump($length, yield $buffer->bufferRead($length)); + } + })()); + } + + /** + * Send TL payload. + * + * @param array $payload Payload to send + * + * @return \Generator + */ + public function send(array $payload): \Generator + { + $data = yield $this->TL->serializeMethod($payload['_'], $payload); + (yield $this->stream->getWriteBuffer(\strlen($data)))->bufferWrite($data); + } +} diff --git a/src/danog/MadelineProto/TON/PrivateKey.php b/src/danog/MadelineProto/TON/PrivateKey.php new file mode 100644 index 00000000..e81488ea --- /dev/null +++ b/src/danog/MadelineProto/TON/PrivateKey.php @@ -0,0 +1,41 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2019 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * + * @link https://docs.madelineproto.xyz MadelineProto documentation + */ + +namespace danog\MadelineProto\TON; + +use phpseclib3\Crypt\EC\Curves\Curve25519; +use phpseclib3\Crypt\EC\PrivateKey as ECPrivateKey; + +class PrivateKey extends ECPrivateKey +{ + public static function load($key, $password = false) + { + self::initialize_static_variables(); + + $components = false; + + $curve = new Curve25519; + $peerPublic = Common::extractPoint($key, $curve); + + $components['format'] = 'TON'; + $components['curve'] = $curve; + + $new = static::onLoad($components); + return $new; + } +} \ No newline at end of file diff --git a/src/danog/MadelineProto/TON/PublicKey.php b/src/danog/MadelineProto/TON/PublicKey.php new file mode 100644 index 00000000..50c46ec7 --- /dev/null +++ b/src/danog/MadelineProto/TON/PublicKey.php @@ -0,0 +1,61 @@ +. + * + * @author Daniil Gentili + * @copyright 2016-2019 Daniil Gentili + * @license https://opensource.org/licenses/AGPL-3.0 AGPLv3 + * + * @link https://docs.madelineproto.xyz MadelineProto documentation + */ + +namespace danog\MadelineProto\TON; + +use danog\MadelineProto\Magic; +use danog\MadelineProto\Tools; +use phpseclib3\Crypt\EC\Curves\Curve25519; +use phpseclib3\Crypt\EC\Formats\Keys\Common; +use phpseclib3\Crypt\EC\Formats\Keys\MontgomeryPublic; +use phpseclib3\Crypt\EC\PublicKey as PPublicKey; +use phpseclib3\Math\BigInteger; + +class PublicKey extends PPublicKey +{ + public static function load($key, $password = false) + { + self::initialize_static_variables(); + + $components = false; + + // Transpose + $key[31] = $key[31] & chr(127); + + $curve = new Curve25519; + $modulo = Tools::getVar($curve, "modulo"); + $y = new BigInteger(strrev($key), 256); + $y2 = clone $y; + $y = $y->add(Magic::$one); + $y2 = $y2->subtract(Magic::$one); + $y2 = $modulo->subtract($y2)->powMod(Magic::$one, $modulo); + + $y2 = $y2->modInverse($modulo); + + $key = $y->multiply($y2)->powMod(Magic::$one, $modulo)->toBytes(); + + $components = MontgomeryPublic::load($key); + + $components['format'] = 'TON'; + $components['curve'] = $curve; + + $new = static::onLoad($components); + return $new; + } +}