Change encryption to use CTR instead of GCM

despite MCPE claiming to use GCM, it omits the auth tag, which defeats the whole point of using GCM.
CTR can be used instead, with the final 4 bytes of the IV being 0002.
This commit is contained in:
Dylan K. Taylor 2021-06-13 21:57:23 +01:00
parent 0df2677464
commit 04a6e89d6f
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
2 changed files with 30 additions and 7 deletions

View File

@ -670,7 +670,7 @@ class NetworkSession{
}
$this->sendDataPacket(ServerToClientHandshakePacket::create($handshakeJwt), true); //make sure this gets sent before encryption is enabled
$this->cipher = new EncryptionContext($encryptionKey, EncryptionContext::ENCRYPTION_SCHEME);
$this->cipher = EncryptionContext::fakeGCM($encryptionKey);
$this->setHandler(new HandshakePacketHandler(function() : void{
$this->onServerLoginSuccess();

View File

@ -32,7 +32,6 @@ use function strlen;
use function substr;
class EncryptionContext{
public const ENCRYPTION_SCHEME = "AES-256-GCM";
private const CHECKSUM_ALGO = "sha256";
/** @var bool */
@ -51,16 +50,40 @@ class EncryptionContext{
/** @var int */
private $encryptCounter = 0;
public function __construct(string $encryptionKey, string $algorithm){
public function __construct(string $encryptionKey, string $algorithm, string $iv){
$this->key = $encryptionKey;
$this->decryptCipher = new Cipher($algorithm);
$ivLength = $this->decryptCipher->getIVLength();
$this->decryptCipher->decryptInit($this->key, substr($this->key, 0, $ivLength));
$this->decryptCipher->decryptInit($this->key, $iv);
$this->encryptCipher = new Cipher($algorithm);
$ivLength = $this->encryptCipher->getIVLength();
$this->encryptCipher->encryptInit($this->key, substr($this->key, 0, $ivLength));
$this->encryptCipher->encryptInit($this->key, $iv);
}
/**
* Returns an EncryptionContext suitable for decrypting Minecraft packets from 1.16.200 and up.
*
* MCPE uses GCM, but without the auth tag, which defeats the whole purpose of using GCM.
* GCM is just a wrapper around CTR which adds the auth tag, so CTR can replace GCM for this case.
* However, since GCM passes only the first 12 bytes of the IV followed by 0002, we must do the same for
* compatibility with MCPE.
* In PM, we could skip this and just use GCM directly (since we use OpenSSL), but this way is more portable, and
* better for developers who come digging in the PM code looking for answers.
*/
public static function fakeGCM(string $encryptionKey) : self{
return new EncryptionContext(
$encryptionKey,
"AES-256-CTR",
substr($encryptionKey, 0, 12) . "\x00\x00\x00\x02"
);
}
public static function cfb8(string $encryptionKey) : self{
return new EncryptionContext(
$encryptionKey,
"AES-256-CFB8",
substr($encryptionKey, 0, 16)
);
}
/**