Added JwtUtils::parse(), make ProcessLoginTask more robust

This commit is contained in:
Dylan K. Taylor 2020-05-13 13:36:42 +01:00
parent c69411c984
commit 8c2878fe5b
3 changed files with 20 additions and 14 deletions

View File

@ -38,22 +38,28 @@ use function strtr;
final class JwtUtils{ final class JwtUtils{
/** /**
* @return mixed[] array of claims * TODO: replace this result with an object
* @phpstan-return array<string, mixed> *
* @return mixed[]
* @phpstan-return array{mixed[], mixed[], string}
* *
* @throws \UnexpectedValueException * @throws \UnexpectedValueException
*/ */
public static function getClaims(string $token) : array{ public static function parse(string $token) : array{
$v = explode(".", $token); $v = explode(".", $token);
if(count($v) !== 3){ if(count($v) !== 3){
throw new \UnexpectedValueException("Expected exactly 3 JWT parts, got " . count($v)); throw new \UnexpectedValueException("Expected exactly 3 JWT parts, got " . count($v));
} }
$result = json_decode(self::b64UrlDecode($v[1]), true); $header = json_decode(self::b64UrlDecode($v[0]), true);
if(!is_array($result)){ if(!is_array($header)){
throw new \UnexpectedValueException("Failed to decode JWT header JSON: ". json_last_error_msg());
}
$body = json_decode(self::b64UrlDecode($v[1]), true);
if(!is_array($body)){
throw new \UnexpectedValueException("Failed to decode JWT payload JSON: " . json_last_error_msg()); throw new \UnexpectedValueException("Failed to decode JWT payload JSON: " . json_last_error_msg());
} }
$signature = self::b64UrlDecode($v[2]);
return $result; return [$header, $body, $signature];
} }
public static function b64UrlEncode(string $str) : string{ public static function b64UrlEncode(string $str) : string{

View File

@ -36,7 +36,6 @@ use function base64_decode;
use function bin2hex; use function bin2hex;
use function explode; use function explode;
use function gmp_init; use function gmp_init;
use function json_decode;
use function openssl_verify; use function openssl_verify;
use function str_split; use function str_split;
use function strlen; use function strlen;
@ -115,7 +114,11 @@ class ProcessLoginTask extends AsyncTask{
* @throws VerifyLoginException if errors are encountered * @throws VerifyLoginException if errors are encountered
*/ */
private function validateToken(string $jwt, ?string &$currentPublicKey, bool $first = false) : void{ private function validateToken(string $jwt, ?string &$currentPublicKey, bool $first = false) : void{
[$headB64, $payloadB64, $sigB64] = explode('.', $jwt); try{
[$headers, $claims, $plainSignature] = JwtUtils::parse($jwt);
}catch(\UnexpectedValueException $e){
throw new VerifyLoginException("Failed to parse JWT: " . $e->getMessage(), 0, $e);
}
if($currentPublicKey === null){ if($currentPublicKey === null){
if(!$first){ if(!$first){
@ -123,7 +126,6 @@ class ProcessLoginTask extends AsyncTask{
} }
//First link, check that it is self-signed //First link, check that it is self-signed
$headers = json_decode(JwtUtils::b64UrlDecode($headB64), true);
$currentPublicKey = $headers["x5u"]; $currentPublicKey = $headers["x5u"];
} }
@ -148,8 +150,6 @@ class ProcessLoginTask extends AsyncTask{
$this->authenticated = true; //we're signed into xbox live $this->authenticated = true; //we're signed into xbox live
} }
$claims = json_decode(JwtUtils::b64UrlDecode($payloadB64), true);
$time = time(); $time = time();
if(isset($claims["nbf"]) and $claims["nbf"] > $time + self::CLOCK_DRIFT_MAX){ if(isset($claims["nbf"]) and $claims["nbf"] > $time + self::CLOCK_DRIFT_MAX){
throw new VerifyLoginException("%pocketmine.disconnect.invalidSession.tooEarly"); throw new VerifyLoginException("%pocketmine.disconnect.invalidSession.tooEarly");

View File

@ -88,7 +88,7 @@ class LoginPacket extends DataPacket implements ServerboundPacket{
foreach($this->chainDataJwt->chain as $k => $chain){ foreach($this->chainDataJwt->chain as $k => $chain){
//validate every chain element //validate every chain element
try{ try{
$claims = JwtUtils::getClaims($chain); [, $claims, ] = JwtUtils::parse($chain);
}catch(\UnexpectedValueException $e){ }catch(\UnexpectedValueException $e){
throw new PacketDecodeException($e->getMessage(), 0, $e); throw new PacketDecodeException($e->getMessage(), 0, $e);
} }
@ -117,7 +117,7 @@ class LoginPacket extends DataPacket implements ServerboundPacket{
$this->clientDataJwt = $buffer->get($buffer->getLInt()); $this->clientDataJwt = $buffer->get($buffer->getLInt());
try{ try{
$clientData = JwtUtils::getClaims($this->clientDataJwt); [, $clientData, ] = JwtUtils::parse($this->clientDataJwt);
}catch(\UnexpectedValueException $e){ }catch(\UnexpectedValueException $e){
throw new PacketDecodeException($e->getMessage(), 0, $e); throw new PacketDecodeException($e->getMessage(), 0, $e);
} }