From 0eb4231b5174e57371ad40e91abd26c2977dd810 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Thu, 22 Jul 2021 23:04:00 +0100 Subject: [PATCH] Use OpenSSL for ECDH during client login, drop mdanter/ecc (#4328) This brings a significant performance improvement to login sequence handling, reducing CPU cost of `PrepareEncryptionTask` by over 90% and `ProcessLoginTask` by over 60%. It also allows us to shed a dependency. --- composer.json | 2 +- composer.lock | 78 +------------ phpstan.neon.dist | 1 + src/network/mcpe/JwtUtils.php | 104 ++++++++++++++---- src/network/mcpe/NetworkSession.php | 5 +- src/network/mcpe/auth/ProcessLoginTask.php | 53 ++++----- .../mcpe/encryption/EncryptionUtils.php | 27 +++-- .../mcpe/encryption/PrepareEncryptionTask.php | 37 +++++-- .../mcpe/handler/LoginPacketHandler.php | 9 +- tests/phpstan/configs/l8-baseline.neon | 2 +- tests/phpstan/stubs/phpasn1.stub | 22 ++++ 11 files changed, 186 insertions(+), 154 deletions(-) create mode 100644 tests/phpstan/stubs/phpasn1.stub diff --git a/composer.json b/composer.json index b6659c8c1..6548e7df5 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "ext-zlib": ">=1.2.11", "composer-runtime-api": "^2.0", "adhocore/json-comment": "^1.1", - "mdanter/ecc": "^1.0", + "fgrosse/phpasn1": "^2.3", "netresearch/jsonmapper": "^4.0", "pocketmine/bedrock-protocol": "dev-master#88ae308a03e8e61ccfdddd42623efabe9e772b42", "pocketmine/binaryutils": "^0.2.1", diff --git a/composer.lock b/composer.lock index 622e8f1ce..e33951329 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7c8ebad0871e3c90e8894476e3c5b925", + "content-hash": "49005f17832ef5949b4a2ac04cd1ee93", "packages": [ { "name": "adhocore/json-comment", @@ -192,82 +192,6 @@ }, "time": "2021-04-24T19:01:55+00:00" }, - { - "name": "mdanter/ecc", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpecc/phpecc.git", - "reference": "34e2eec096bf3dcda814e8f66dd91ae87a2db7cd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpecc/phpecc/zipball/34e2eec096bf3dcda814e8f66dd91ae87a2db7cd", - "reference": "34e2eec096bf3dcda814e8f66dd91ae87a2db7cd", - "shasum": "" - }, - "require": { - "ext-gmp": "*", - "fgrosse/phpasn1": "^2.0", - "php": "^7.0||^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0||^8.0||^9.0", - "squizlabs/php_codesniffer": "^2.0", - "symfony/yaml": "^2.6|^3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Mdanter\\Ecc\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matyas Danter", - "homepage": "http://matejdanter.com/", - "role": "Author" - }, - { - "name": "Thibaud Fabre", - "email": "thibaud@aztech.io", - "homepage": "http://aztech.io", - "role": "Maintainer" - }, - { - "name": "Thomas Kerin", - "email": "afk11@users.noreply.github.com", - "role": "Maintainer" - } - ], - "description": "PHP Elliptic Curve Cryptography library", - "homepage": "https://github.com/phpecc/phpecc", - "keywords": [ - "Diffie", - "ECDSA", - "Hellman", - "curve", - "ecdh", - "elliptic", - "nistp192", - "nistp224", - "nistp256", - "nistp384", - "nistp521", - "phpecc", - "secp256k1", - "secp256r1" - ], - "support": { - "issues": "https://github.com/phpecc/phpecc/issues", - "source": "https://github.com/phpecc/phpecc/tree/v1.0.0" - }, - "time": "2021-01-16T19:42:14+00:00" - }, { "name": "netresearch/jsonmapper", "version": "v4.0.0", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4e1631d93..cd3c5b18b 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -46,6 +46,7 @@ parameters: - tests/phpstan/stubs/JsonMapper.stub - tests/phpstan/stubs/pthreads.stub - tests/phpstan/stubs/leveldb.stub + - tests/phpstan/stubs/phpasn1.stub reportUnmatchedIgnoredErrors: false #no other way to silence platform-specific non-warnings staticReflectionClassNamePatterns: - "#^COM$#" diff --git a/src/network/mcpe/JwtUtils.php b/src/network/mcpe/JwtUtils.php index 1b1162362..f2fb273c0 100644 --- a/src/network/mcpe/JwtUtils.php +++ b/src/network/mcpe/JwtUtils.php @@ -23,36 +23,39 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; -use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface; -use Mdanter\Ecc\Crypto\Key\PublicKeyInterface; -use Mdanter\Ecc\Crypto\Signature\Signature; -use Mdanter\Ecc\Serializer\PrivateKey\DerPrivateKeySerializer; -use Mdanter\Ecc\Serializer\PrivateKey\PemPrivateKeySerializer; -use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer; -use Mdanter\Ecc\Serializer\PublicKey\PemPublicKeySerializer; -use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer; +use FG\ASN1\Exception\ParserException; +use FG\ASN1\Universal\Integer; +use FG\ASN1\Universal\Sequence; use pocketmine\utils\AssumptionFailedError; use function base64_decode; use function base64_encode; -use function bin2hex; use function count; use function explode; +use function gmp_export; +use function gmp_import; use function gmp_init; use function gmp_strval; -use function hex2bin; use function is_array; use function json_decode; use function json_encode; use function json_last_error_msg; use function openssl_error_string; +use function openssl_pkey_get_details; +use function openssl_pkey_get_public; use function openssl_sign; use function openssl_verify; +use function preg_match; use function rtrim; +use function sprintf; use function str_pad; use function str_repeat; +use function str_replace; use function str_split; use function strlen; use function strtr; +use const GMP_BIG_ENDIAN; +use const GMP_MSW_FIRST; +use const JSON_THROW_ON_ERROR; use const OPENSSL_ALGO_SHA384; use const STR_PAD_LEFT; @@ -94,9 +97,11 @@ final class JwtUtils{ } /** + * @param resource $signingKey + * * @throws JwtException */ - public static function verify(string $jwt, PublicKeyInterface $signingKey) : bool{ + public static function verify(string $jwt, $signingKey) : bool{ [$header, $body, $signature] = self::split($jwt); $plainSignature = self::b64UrlDecode($signature); @@ -105,12 +110,17 @@ final class JwtUtils{ } [$rString, $sString] = str_split($plainSignature, 48); - $sig = new Signature(gmp_init(bin2hex($rString), 16), gmp_init(bin2hex($sString), 16)); + $convert = fn(string $str) => gmp_strval(gmp_import($str, 1, GMP_BIG_ENDIAN | GMP_MSW_FIRST), 10); + + $sequence = new Sequence( + new Integer($convert($rString)), + new Integer($convert($sString)) + ); $v = openssl_verify( $header . '.' . $body, - (new DerSignatureSerializer())->serialize($sig), - (new PemPublicKeySerializer(new DerPublicKeySerializer()))->serialize($signingKey), + $sequence->getBinary(), + $signingKey, OPENSSL_ALGO_SHA384 ); switch($v){ @@ -122,24 +132,43 @@ final class JwtUtils{ } /** + * @param resource $signingKey + * * @phpstan-param array $header * @phpstan-param array $claims */ - public static function create(array $header, array $claims, PrivateKeyInterface $signingKey) : string{ - $jwtBody = JwtUtils::b64UrlEncode(json_encode($header)) . "." . JwtUtils::b64UrlEncode(json_encode($claims)); + public static function create(array $header, array $claims, $signingKey) : string{ + $jwtBody = JwtUtils::b64UrlEncode(json_encode($header, JSON_THROW_ON_ERROR)) . "." . JwtUtils::b64UrlEncode(json_encode($claims, JSON_THROW_ON_ERROR)); openssl_sign( $jwtBody, - $sig, - (new PemPrivateKeySerializer(new DerPrivateKeySerializer()))->serialize($signingKey), + $rawDerSig, + $signingKey, OPENSSL_ALGO_SHA384 ); - $decodedSig = (new DerSignatureSerializer())->parse($sig); - $jwtSig = JwtUtils::b64UrlEncode( - hex2bin(str_pad(gmp_strval($decodedSig->getR(), 16), 96, "0", STR_PAD_LEFT)) . - hex2bin(str_pad(gmp_strval($decodedSig->getS(), 16), 96, "0", STR_PAD_LEFT)) + try{ + $asnObject = Sequence::fromBinary($rawDerSig); + }catch(ParserException $e){ + throw new AssumptionFailedError("Failed to parse OpenSSL signature: " . $e->getMessage(), 0, $e); + } + if(count($asnObject) !== 2){ + throw new AssumptionFailedError("OpenSSL produced invalid signature, expected exactly 2 parts"); + } + [$r, $s] = [$asnObject[0], $asnObject[1]]; + if(!($r instanceof Integer) || !($s instanceof Integer)){ + throw new AssumptionFailedError("OpenSSL produced invalid signature, expected 2 INTEGER parts"); + } + $rString = $r->getContent(); + $sString = $s->getContent(); + + $toBinary = fn($str) => str_pad( + gmp_export(gmp_init($str, 10), 1, GMP_BIG_ENDIAN | GMP_MSW_FIRST), + 48, + "\x00", + STR_PAD_LEFT ); + $jwtSig = JwtUtils::b64UrlEncode($toBinary($rString) . $toBinary($sString)); return "$jwtBody.$jwtSig"; } @@ -158,4 +187,35 @@ final class JwtUtils{ } return $decoded; } + + /** + * @param resource $opensslKey + */ + public static function emitDerPublicKey($opensslKey) : string{ + $details = openssl_pkey_get_details($opensslKey); + if($details === false){ + throw new AssumptionFailedError("Failed to get details from OpenSSL key resource"); + } + + /** @var string $pemKey */ + $pemKey = $details['key']; + if(preg_match("@^-----BEGIN[A-Z\d ]+PUBLIC KEY-----\n([A-Za-z\d+/\n]+)\n-----END[A-Z\d ]+PUBLIC KEY-----\n$@", $pemKey, $matches) === 1){ + $derKey = base64_decode(str_replace("\n", "", $matches[1]), true); + if($derKey !== false){ + return $derKey; + } + } + throw new AssumptionFailedError("OpenSSL resource contains invalid public key"); + } + + /** + * @return resource + */ + public static function parseDerPublicKey(string $derKey){ + $signingKeyOpenSSL = openssl_pkey_get_public(sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", base64_encode($derKey))); + if($signingKeyOpenSSL === false){ + throw new JwtException("OpenSSL failed to parse key: " . openssl_error_string()); + } + return $signingKeyOpenSSL; + } } diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 258e21ee9..650d72d6c 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\network\mcpe; -use Mdanter\Ecc\Crypto\Key\PublicKeyInterface; use pocketmine\data\bedrock\EffectIdMap; use pocketmine\entity\Attribute; use pocketmine\entity\effect\EffectInstance; @@ -203,7 +202,7 @@ class NetworkSession{ $this->logger->info("Player: " . TextFormat::AQUA . $info->getUsername() . TextFormat::RESET); $this->logger->setPrefix($this->getLogPrefix()); }, - function(bool $isAuthenticated, bool $authRequired, ?string $error, ?PublicKeyInterface $clientPubKey) : void{ + function(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{ $this->setAuthenticationStatus($isAuthenticated, $authRequired, $error, $clientPubKey); } )); @@ -570,7 +569,7 @@ class NetworkSession{ }, $reason); } - private function setAuthenticationStatus(bool $authenticated, bool $authRequired, ?string $error, ?PublicKeyInterface $clientPubKey) : void{ + private function setAuthenticationStatus(bool $authenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void{ if(!$this->connected){ return; } diff --git a/src/network/mcpe/auth/ProcessLoginTask.php b/src/network/mcpe/auth/ProcessLoginTask.php index 103a39405..b02f9e215 100644 --- a/src/network/mcpe/auth/ProcessLoginTask.php +++ b/src/network/mcpe/auth/ProcessLoginTask.php @@ -23,9 +23,6 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\auth; -use FG\ASN1\Exception\ParserException; -use Mdanter\Ecc\Crypto\Key\PublicKeyInterface; -use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer; use pocketmine\lang\KnownTranslationKeys; use pocketmine\network\mcpe\JwtException; use pocketmine\network\mcpe\JwtUtils; @@ -35,6 +32,8 @@ use pocketmine\scheduler\AsyncTask; use function base64_decode; use function igbinary_serialize; use function igbinary_unserialize; +use function openssl_error_string; +use function openssl_free_key; use function time; class ProcessLoginTask extends AsyncTask{ @@ -65,12 +64,12 @@ class ProcessLoginTask extends AsyncTask{ /** @var bool */ private $authRequired; - /** @var PublicKeyInterface|null */ + /** @var string|null */ private $clientPublicKey = null; /** * @param string[] $chainJwts - * @phpstan-param \Closure(bool $isAuthenticated, bool $authRequired, ?string $error, ?PublicKeyInterface $clientPublicKey) : void $onCompletion + * @phpstan-param \Closure(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPublicKey) : void $onCompletion */ public function __construct(array $chainJwts, string $clientDataJwt, bool $authRequired, \Closure $onCompletion){ $this->storeLocal(self::TLS_KEY_ON_COMPLETION, $onCompletion); @@ -88,7 +87,7 @@ class ProcessLoginTask extends AsyncTask{ } } - private function validateChain() : PublicKeyInterface{ + private function validateChain() : string{ /** @var string[] $chain */ $chain = igbinary_unserialize($this->chain); @@ -107,7 +106,7 @@ class ProcessLoginTask extends AsyncTask{ $this->validateToken($this->clientDataJwt, $currentKey); - return (new DerPublicKeySerializer())->parse(base64_decode($clientKey, true)); + return $clientKey; } /** @@ -132,38 +131,36 @@ class ProcessLoginTask extends AsyncTask{ throw new VerifyLoginException("Invalid JWT header: " . $e->getMessage(), 0, $e); } + $headerDerKey = base64_decode($headers->x5u, true); + if($headerDerKey === false){ + throw new VerifyLoginException("Invalid JWT public key: base64 decoding error decoding x5u"); + } + if($currentPublicKey === null){ if(!$first){ throw new VerifyLoginException("%" . KnownTranslationKeys::POCKETMINE_DISCONNECT_INVALIDSESSION_MISSINGKEY); } - - //First link, check that it is self-signed - $currentPublicKey = $headers->x5u; - }elseif($headers->x5u !== $currentPublicKey){ + }elseif($headerDerKey !== $currentPublicKey){ //Fast path: if the header key doesn't match what we expected, the signature isn't going to validate anyway throw new VerifyLoginException("%" . KnownTranslationKeys::POCKETMINE_DISCONNECT_INVALIDSESSION_BADSIGNATURE); } - $derPublicKeySerializer = new DerPublicKeySerializer(); - $rawPublicKey = base64_decode($currentPublicKey, true); - if($rawPublicKey === false){ - throw new VerifyLoginException("Failed to decode base64'd public key"); + try{ + $signingKeyOpenSSL = JwtUtils::parseDerPublicKey($headerDerKey); + }catch(JwtException $e){ + throw new VerifyLoginException("Invalid JWT public key: " . openssl_error_string()); } try{ - $signingKey = $derPublicKeySerializer->parse($rawPublicKey); - }catch(\RuntimeException | ParserException $e){ - throw new VerifyLoginException("Failed to parse DER public key: " . $e->getMessage(), 0, $e); - } - - try{ - if(!JwtUtils::verify($jwt, $signingKey)){ + if(!JwtUtils::verify($jwt, $signingKeyOpenSSL)){ throw new VerifyLoginException("%" . KnownTranslationKeys::POCKETMINE_DISCONNECT_INVALIDSESSION_BADSIGNATURE); } }catch(JwtException $e){ throw new VerifyLoginException($e->getMessage(), 0, $e); } - if($currentPublicKey === self::MOJANG_ROOT_PUBLIC_KEY){ + openssl_free_key($signingKeyOpenSSL); + + if($headers->x5u === self::MOJANG_ROOT_PUBLIC_KEY){ $this->authenticated = true; //we're signed into xbox live } @@ -188,13 +185,19 @@ class ProcessLoginTask extends AsyncTask{ throw new VerifyLoginException("%" . KnownTranslationKeys::POCKETMINE_DISCONNECT_INVALIDSESSION_TOOLATE); } - $currentPublicKey = $claims->identityPublicKey ?? null; //if there are further links, the next link should be signed with this + if(isset($claims->identityPublicKey)){ + $identityPublicKey = base64_decode($claims->identityPublicKey, true); + if($identityPublicKey === false){ + throw new VerifyLoginException("Invalid identityPublicKey: base64 error decoding"); + } + $currentPublicKey = $identityPublicKey; //if there are further links, the next link should be signed with this + } } public function onCompletion() : void{ /** * @var \Closure $callback - * @phpstan-var \Closure(bool, bool, ?string, ?PublicKeyInterface) : void $callback + * @phpstan-var \Closure(bool, bool, ?string, ?string) : void $callback */ $callback = $this->fetchLocal(self::TLS_KEY_ON_COMPLETION); $callback($this->authenticated, $this->authRequired, $this->error, $this->clientPublicKey); diff --git a/src/network/mcpe/encryption/EncryptionUtils.php b/src/network/mcpe/encryption/EncryptionUtils.php index c34e4a38f..45682a300 100644 --- a/src/network/mcpe/encryption/EncryptionUtils.php +++ b/src/network/mcpe/encryption/EncryptionUtils.php @@ -23,14 +23,15 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\encryption; -use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface; -use Mdanter\Ecc\Crypto\Key\PublicKeyInterface; -use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer; use pocketmine\network\mcpe\JwtUtils; use function base64_encode; +use function bin2hex; +use function gmp_init; use function gmp_strval; use function hex2bin; use function openssl_digest; +use function openssl_error_string; +use function openssl_pkey_derive; use function str_pad; final class EncryptionUtils{ @@ -39,18 +40,30 @@ final class EncryptionUtils{ //NOOP } - public static function generateSharedSecret(PrivateKeyInterface $localPriv, PublicKeyInterface $remotePub) : \GMP{ - return $localPriv->createExchange($remotePub)->calculateSharedKey(); + /** + * @param resource $localPriv + * @param resource $remotePub + */ + public static function generateSharedSecret($localPriv, $remotePub) : \GMP{ + $hexSecret = openssl_pkey_derive($remotePub, $localPriv, 48); + if($hexSecret === false){ + throw new \InvalidArgumentException("Failed to derive shared secret: " . openssl_error_string()); + } + return gmp_init(bin2hex($hexSecret), 16); } public static function generateKey(\GMP $secret, string $salt) : string{ return openssl_digest($salt . hex2bin(str_pad(gmp_strval($secret, 16), 96, "0", STR_PAD_LEFT)), 'sha256', true); } - public static function generateServerHandshakeJwt(PrivateKeyInterface $serverPriv, string $salt) : string{ + /** + * @param resource $serverPriv + */ + public static function generateServerHandshakeJwt($serverPriv, string $salt) : string{ + $derPublicKey = JwtUtils::emitDerPublicKey($serverPriv); return JwtUtils::create( [ - "x5u" => base64_encode((new DerPublicKeySerializer())->serialize($serverPriv->getPublicKey())), + "x5u" => base64_encode($derPublicKey), "alg" => "ES384" ], [ diff --git a/src/network/mcpe/encryption/PrepareEncryptionTask.php b/src/network/mcpe/encryption/PrepareEncryptionTask.php index 883947e04..e5cfaf92d 100644 --- a/src/network/mcpe/encryption/PrepareEncryptionTask.php +++ b/src/network/mcpe/encryption/PrepareEncryptionTask.php @@ -23,50 +23,65 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\encryption; -use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface; -use Mdanter\Ecc\Crypto\Key\PublicKeyInterface; -use Mdanter\Ecc\EccFactory; +use pocketmine\network\mcpe\JwtUtils; use pocketmine\scheduler\AsyncTask; use pocketmine\utils\AssumptionFailedError; +use function igbinary_serialize; +use function igbinary_unserialize; +use function openssl_error_string; +use function openssl_free_key; +use function openssl_pkey_get_details; +use function openssl_pkey_new; use function random_bytes; class PrepareEncryptionTask extends AsyncTask{ private const TLS_KEY_ON_COMPLETION = "completion"; - /** @var PrivateKeyInterface|null */ + /** @var resource|null */ private static $SERVER_PRIVATE_KEY = null; - /** @var PrivateKeyInterface */ + /** @var string */ private $serverPrivateKey; /** @var string|null */ private $aesKey = null; /** @var string|null */ private $handshakeJwt = null; - /** @var PublicKeyInterface */ + /** @var string */ private $clientPub; /** * @phpstan-param \Closure(string $encryptionKey, string $handshakeJwt) : void $onCompletion */ - public function __construct(PublicKeyInterface $clientPub, \Closure $onCompletion){ + public function __construct(string $clientPub, \Closure $onCompletion){ if(self::$SERVER_PRIVATE_KEY === null){ - self::$SERVER_PRIVATE_KEY = EccFactory::getNistCurves()->generator384()->createPrivateKey(); + $serverPrivateKey = openssl_pkey_new(["ec" => ["curve_name" => "secp384r1"]]); + if($serverPrivateKey === false){ + throw new \RuntimeException("openssl_pkey_new() failed: " . openssl_error_string()); + } + self::$SERVER_PRIVATE_KEY = $serverPrivateKey; } - $this->serverPrivateKey = self::$SERVER_PRIVATE_KEY; + $this->serverPrivateKey = igbinary_serialize(openssl_pkey_get_details(self::$SERVER_PRIVATE_KEY)); $this->clientPub = $clientPub; $this->storeLocal(self::TLS_KEY_ON_COMPLETION, $onCompletion); } public function onRun() : void{ - $serverPriv = $this->serverPrivateKey; - $sharedSecret = EncryptionUtils::generateSharedSecret($serverPriv, $this->clientPub); + /** @var mixed[] $serverPrivDetails */ + $serverPrivDetails = igbinary_unserialize($this->serverPrivateKey); + $serverPriv = openssl_pkey_new($serverPrivDetails); + if($serverPriv === false) throw new AssumptionFailedError("Failed to restore server signing key from details"); + $clientPub = JwtUtils::parseDerPublicKey($this->clientPub); + $sharedSecret = EncryptionUtils::generateSharedSecret($serverPriv, $clientPub); $salt = random_bytes(16); $this->aesKey = EncryptionUtils::generateKey($sharedSecret, $salt); $this->handshakeJwt = EncryptionUtils::generateServerHandshakeJwt($serverPriv, $salt); + + openssl_free_key($serverPriv); + openssl_free_key($clientPub); } public function onCompletion() : void{ diff --git a/src/network/mcpe/handler/LoginPacketHandler.php b/src/network/mcpe/handler/LoginPacketHandler.php index 989bac9f6..926e20740 100644 --- a/src/network/mcpe/handler/LoginPacketHandler.php +++ b/src/network/mcpe/handler/LoginPacketHandler.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\network\mcpe\handler; -use Mdanter\Ecc\Crypto\Key\PublicKeyInterface; use pocketmine\entity\InvalidSkinException; use pocketmine\event\player\PlayerPreLoginEvent; use pocketmine\lang\KnownTranslationKeys; @@ -63,13 +62,13 @@ class LoginPacketHandler extends PacketHandler{ private $playerInfoConsumer; /** * @var \Closure - * @phpstan-var \Closure(bool, bool, ?string, ?PublicKeyInterface) : void + * @phpstan-var \Closure(bool, bool, ?string, ?string) : void */ private $authCallback; /** * @phpstan-param \Closure(PlayerInfo) : void $playerInfoConsumer - * @phpstan-param \Closure(bool $isAuthenticated, bool $authRequired, ?string $error, ?PublicKeyInterface $clientPubKey) : void $authCallback + * @phpstan-param \Closure(bool $isAuthenticated, bool $authRequired, ?string $error, ?string $clientPubKey) : void $authCallback */ public function __construct(Server $server, NetworkSession $session, \Closure $playerInfoConsumer, \Closure $authCallback){ $this->session = $session; @@ -78,10 +77,6 @@ class LoginPacketHandler extends PacketHandler{ $this->authCallback = $authCallback; } - private static function dummy() : void{ - echo PublicKeyInterface::class; //this prevents the import getting removed by tools that don't understand phpstan - } - public function handleLogin(LoginPacket $packet) : bool{ if(!$this->isCompatibleProtocol($packet->protocol)){ $this->session->sendDataPacket(PlayStatusPacket::create($packet->protocol < ProtocolInfo::CURRENT_PROTOCOL ? PlayStatusPacket::LOGIN_FAILED_CLIENT : PlayStatusPacket::LOGIN_FAILED_SERVER), true); diff --git a/tests/phpstan/configs/l8-baseline.neon b/tests/phpstan/configs/l8-baseline.neon index 98057695c..6e5d7a4fe 100644 --- a/tests/phpstan/configs/l8-baseline.neon +++ b/tests/phpstan/configs/l8-baseline.neon @@ -136,7 +136,7 @@ parameters: path: ../../../src/network/mcpe/NetworkSession.php - - message: "#^Parameter \\#1 \\$clientPub of class pocketmine\\\\network\\\\mcpe\\\\encryption\\\\PrepareEncryptionTask constructor expects Mdanter\\\\Ecc\\\\Crypto\\\\Key\\\\PublicKeyInterface, Mdanter\\\\Ecc\\\\Crypto\\\\Key\\\\PublicKeyInterface\\|null given\\.$#" + message: "#^Parameter \\#1 \\$clientPub of class pocketmine\\\\network\\\\mcpe\\\\encryption\\\\PrepareEncryptionTask constructor expects string, string\\|null given\\.$#" count: 1 path: ../../../src/network/mcpe/NetworkSession.php diff --git a/tests/phpstan/stubs/phpasn1.stub b/tests/phpstan/stubs/phpasn1.stub new file mode 100644 index 000000000..b459289ef --- /dev/null +++ b/tests/phpstan/stubs/phpasn1.stub @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FG\ASN1\Universal; + +class Integer +{ + /** + * @param int|string $value + */ + public function __construct($value){} + + /** @return int|string */ + public function getContent(){} +}