From 9009ef1a4cc64f49baf9633056f7cdc809868b91 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 2 May 2025 13:55:10 +0200 Subject: [PATCH] fix(loginflow): Fix type error when password could not be decrypted Signed-off-by: Joas Schilling --- core/Service/LoginFlowV2Service.php | 14 +++++--- .../Service/LoginFlowV2ServiceUnitTest.php | 33 +++++++++++++++++-- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/core/Service/LoginFlowV2Service.php b/core/Service/LoginFlowV2Service.php index 74fe7fa0ac917..13bd18e0ffa0d 100644 --- a/core/Service/LoginFlowV2Service.php +++ b/core/Service/LoginFlowV2Service.php @@ -63,8 +63,12 @@ public function poll(string $pollToken): LoginFlowV2Credentials { try { // Decrypt the apptoken $privateKey = $this->crypto->decrypt($data->getPrivateKey(), $pollToken); - $appPassword = $this->decryptPassword($data->getAppPassword(), $privateKey); - } catch (\Exception $e) { + } catch (\Exception) { + throw new LoginFlowV2NotFoundException('Apptoken could not be decrypted'); + } + + $appPassword = $this->decryptPassword($data->getAppPassword(), $privateKey); + if ($appPassword === null) { throw new LoginFlowV2NotFoundException('Apptoken could not be decrypted'); } @@ -251,10 +255,10 @@ private function encryptPassword(string $password, string $publicKey): string { return $encryptedPassword; } - private function decryptPassword(string $encryptedPassword, string $privateKey): string { + private function decryptPassword(string $encryptedPassword, string $privateKey): ?string { $encryptedPassword = base64_decode($encryptedPassword); - openssl_private_decrypt($encryptedPassword, $password, $privateKey, OPENSSL_PKCS1_OAEP_PADDING); + $success = openssl_private_decrypt($encryptedPassword, $password, $privateKey, OPENSSL_PKCS1_OAEP_PADDING); - return $password; + return $success ? $password : null; } } diff --git a/tests/Core/Service/LoginFlowV2ServiceUnitTest.php b/tests/Core/Service/LoginFlowV2ServiceUnitTest.php index 8118106c7228e..9baf0f5c859f8 100644 --- a/tests/Core/Service/LoginFlowV2ServiceUnitTest.php +++ b/tests/Core/Service/LoginFlowV2ServiceUnitTest.php @@ -128,11 +128,14 @@ private function getOpenSSLEncryptedPublicAndPrivateKey(string $appPassword): ar /* * Tests for poll */ - - public function testPollApptokenCouldNotBeDecrypted(): void { + public function testPollPrivateKeyCouldNotBeDecrypted(): void { $this->expectException(LoginFlowV2NotFoundException::class); $this->expectExceptionMessage('Apptoken could not be decrypted'); + $this->crypto->expects($this->once()) + ->method('decrypt') + ->willThrowException(new \Exception('HMAC mismatch')); + /* * Cannot be mocked, because functions like getLoginName are magic functions. * To be able to set internal properties, we have to use the real class here. @@ -150,6 +153,32 @@ public function testPollApptokenCouldNotBeDecrypted(): void { $this->subjectUnderTest->poll(''); } + public function testPollApptokenCouldNotBeDecrypted(): void { + $this->expectException(LoginFlowV2NotFoundException::class); + $this->expectExceptionMessage('Apptoken could not be decrypted'); + + /* + * Cannot be mocked, because functions like getLoginName are magic functions. + * To be able to set internal properties, we have to use the real class here. + */ + [$encrypted, $privateKey,] = $this->getOpenSSLEncryptedPublicAndPrivateKey('test'); + $loginFlowV2 = new LoginFlowV2(); + $loginFlowV2->setLoginName('test'); + $loginFlowV2->setServer('test'); + $loginFlowV2->setAppPassword('broken#' . $encrypted); + $loginFlowV2->setPrivateKey('encrypted(test)'); + + $this->crypto->expects($this->once()) + ->method('decrypt') + ->willReturn($privateKey); + + $this->mapper->expects($this->once()) + ->method('getByPollToken') + ->willReturn($loginFlowV2); + + $this->subjectUnderTest->poll('test'); + } + public function testPollInvalidToken(): void { $this->expectException(LoginFlowV2NotFoundException::class); $this->expectExceptionMessage('Invalid token');