mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-03 16:49:53 +00:00
Harden login EC key validation
This commit is contained in:
parent
2a11762e61
commit
4e646d19a4
@ -61,6 +61,7 @@ use const OPENSSL_ALGO_SHA384;
|
|||||||
use const STR_PAD_LEFT;
|
use const STR_PAD_LEFT;
|
||||||
|
|
||||||
final class JwtUtils{
|
final class JwtUtils{
|
||||||
|
public const BEDROCK_SIGNING_KEY_CURVE_NAME = "secp384r1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string[]
|
* @return string[]
|
||||||
@ -203,6 +204,17 @@ final class JwtUtils{
|
|||||||
if($signingKeyOpenSSL === false){
|
if($signingKeyOpenSSL === false){
|
||||||
throw new JwtException("OpenSSL failed to parse key: " . openssl_error_string());
|
throw new JwtException("OpenSSL failed to parse key: " . openssl_error_string());
|
||||||
}
|
}
|
||||||
|
$details = openssl_pkey_get_details($signingKeyOpenSSL);
|
||||||
|
if($details === false){
|
||||||
|
throw new JwtException("OpenSSL failed to get details from key: " . openssl_error_string());
|
||||||
|
}
|
||||||
|
if(!isset($details['ec']['curve_name'])){
|
||||||
|
throw new JwtException("Expected an EC key");
|
||||||
|
}
|
||||||
|
$curve = $details['ec']['curve_name'];
|
||||||
|
if($curve !== self::BEDROCK_SIGNING_KEY_CURVE_NAME){
|
||||||
|
throw new JwtException("Key must belong to curve " . self::BEDROCK_SIGNING_KEY_CURVE_NAME . ", got $curve");
|
||||||
|
}
|
||||||
return $signingKeyOpenSSL;
|
return $signingKeyOpenSSL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ use pocketmine\scheduler\AsyncTask;
|
|||||||
use function base64_decode;
|
use function base64_decode;
|
||||||
use function igbinary_serialize;
|
use function igbinary_serialize;
|
||||||
use function igbinary_unserialize;
|
use function igbinary_unserialize;
|
||||||
use function openssl_error_string;
|
|
||||||
use function time;
|
use function time;
|
||||||
|
|
||||||
class ProcessLoginTask extends AsyncTask{
|
class ProcessLoginTask extends AsyncTask{
|
||||||
@ -156,7 +155,8 @@ class ProcessLoginTask extends AsyncTask{
|
|||||||
try{
|
try{
|
||||||
$signingKeyOpenSSL = JwtUtils::parseDerPublicKey($headerDerKey);
|
$signingKeyOpenSSL = JwtUtils::parseDerPublicKey($headerDerKey);
|
||||||
}catch(JwtException $e){
|
}catch(JwtException $e){
|
||||||
throw new VerifyLoginException("Invalid JWT public key: " . openssl_error_string());
|
//TODO: we shouldn't be showing this internal information to the client
|
||||||
|
throw new VerifyLoginException("Invalid JWT public key: " . $e->getMessage(), null, 0, $e);
|
||||||
}
|
}
|
||||||
try{
|
try{
|
||||||
if(!JwtUtils::verify($jwt, $signingKeyOpenSSL)){
|
if(!JwtUtils::verify($jwt, $signingKeyOpenSSL)){
|
||||||
@ -196,6 +196,12 @@ class ProcessLoginTask extends AsyncTask{
|
|||||||
if($identityPublicKey === false){
|
if($identityPublicKey === false){
|
||||||
throw new VerifyLoginException("Invalid identityPublicKey: base64 error decoding");
|
throw new VerifyLoginException("Invalid identityPublicKey: base64 error decoding");
|
||||||
}
|
}
|
||||||
|
try{
|
||||||
|
//verify key format and parameters
|
||||||
|
JwtUtils::parseDerPublicKey($identityPublicKey);
|
||||||
|
}catch(JwtException $e){
|
||||||
|
throw new VerifyLoginException("Invalid identityPublicKey: " . $e->getMessage(), null, 0, $e);
|
||||||
|
}
|
||||||
$currentPublicKey = $identityPublicKey; //if there are further links, the next link should be signed with this
|
$currentPublicKey = $identityPublicKey; //if there are further links, the next link should be signed with this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ use function hex2bin;
|
|||||||
use function openssl_digest;
|
use function openssl_digest;
|
||||||
use function openssl_error_string;
|
use function openssl_error_string;
|
||||||
use function openssl_pkey_derive;
|
use function openssl_pkey_derive;
|
||||||
|
use function openssl_pkey_get_details;
|
||||||
use function str_pad;
|
use function str_pad;
|
||||||
use const STR_PAD_LEFT;
|
use const STR_PAD_LEFT;
|
||||||
|
|
||||||
@ -42,7 +43,20 @@ final class EncryptionUtils{
|
|||||||
//NOOP
|
//NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function validateKey(\OpenSSLAsymmetricKey $key) : void{
|
||||||
|
$keyDetails = Utils::assumeNotFalse(openssl_pkey_get_details($key));
|
||||||
|
if(!isset($keyDetails["ec"]["curve_name"])){
|
||||||
|
throw new \InvalidArgumentException("Key must be an EC key");
|
||||||
|
}
|
||||||
|
$curveName = $keyDetails["ec"]["curve_name"];
|
||||||
|
if($curveName !== JwtUtils::BEDROCK_SIGNING_KEY_CURVE_NAME){
|
||||||
|
throw new \InvalidArgumentException("Key must belong to the " . JwtUtils::BEDROCK_SIGNING_KEY_CURVE_NAME . " elliptic curve, got $curveName");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function generateSharedSecret(\OpenSSLAsymmetricKey $localPriv, \OpenSSLAsymmetricKey $remotePub) : \GMP{
|
public static function generateSharedSecret(\OpenSSLAsymmetricKey $localPriv, \OpenSSLAsymmetricKey $remotePub) : \GMP{
|
||||||
|
self::validateKey($localPriv);
|
||||||
|
self::validateKey($remotePub);
|
||||||
$hexSecret = openssl_pkey_derive($remotePub, $localPriv, 48);
|
$hexSecret = openssl_pkey_derive($remotePub, $localPriv, 48);
|
||||||
if($hexSecret === false){
|
if($hexSecret === false){
|
||||||
throw new \InvalidArgumentException("Failed to derive shared secret: " . openssl_error_string());
|
throw new \InvalidArgumentException("Failed to derive shared secret: " . openssl_error_string());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user