gateway = new AuthCryptoGateway(); } // --- isConfigured --- public function testIsConfiguredReturnsTrueWhenKeyIsSet(): void { $this->assertTrue($this->gateway->isConfigured()); } // --- encryptString / decryptString round-trip --- public function testEncryptDecryptRoundTrip(): void { $plaintext = 'my-secret-sso-value'; $encrypted = $this->gateway->encryptString($plaintext); $this->assertNotSame($plaintext, $encrypted, 'Encrypted value must differ from plaintext'); $this->assertNotEmpty($encrypted); $decrypted = $this->gateway->decryptString($encrypted); $this->assertSame($plaintext, $decrypted); } public function testEncryptEmptyStringProducesMalformedPayloadOnDecrypt(): void { // AES-256-GCM with empty plaintext yields empty ciphertext, // which Crypto::decryptString rejects as malformed — expected behavior. $encrypted = $this->gateway->encryptString(''); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('malformed'); $this->gateway->decryptString($encrypted); } public function testEncryptDecryptRoundTripWithUnicode(): void { $plaintext = 'Ünïcödé-tëst 🔑'; $encrypted = $this->gateway->encryptString($plaintext); $decrypted = $this->gateway->decryptString($encrypted); $this->assertSame($plaintext, $decrypted); } public function testEncryptDecryptRoundTripWithLongString(): void { $plaintext = str_repeat('long-secret-', 100); $encrypted = $this->gateway->encryptString($plaintext); $decrypted = $this->gateway->decryptString($encrypted); $this->assertSame($plaintext, $decrypted); } // --- encryptString produces unique ciphertext --- public function testEncryptProducesDifferentCiphertextEachTime(): void { $plaintext = 'same-input'; $encrypted1 = $this->gateway->encryptString($plaintext); $encrypted2 = $this->gateway->encryptString($plaintext); $this->assertNotSame($encrypted1, $encrypted2, 'Each encryption should use a unique IV'); } // --- decryptString error handling --- public function testDecryptThrowsOnInvalidBase64(): void { $this->expectException(\RuntimeException::class); $this->gateway->decryptString('not-valid-base64!!!'); } public function testDecryptThrowsOnCorruptedPayload(): void { $this->expectException(\RuntimeException::class); $this->gateway->decryptString(base64_encode('not-json')); } public function testDecryptThrowsOnTamperedCiphertext(): void { $encrypted = $this->gateway->encryptString('test-value'); // Tamper with the base64 payload — change one character in the middle $tampered = substr($encrypted, 0, 10) . 'X' . substr($encrypted, 11); $this->expectException(\RuntimeException::class); $this->gateway->decryptString($tampered); } // --- Delegation to Crypto class (GR-SEC-005) --- public function testEncryptStringReturnsSameFormatAsCryptoClass(): void { $encrypted = $this->gateway->encryptString('test'); // The Crypto class wraps JSON in base64. Verify the outer structure. $decoded = base64_decode($encrypted, true); $this->assertIsString($decoded); $payload = json_decode($decoded, true); $this->assertIsArray($payload); $this->assertArrayHasKey('v', $payload); $this->assertArrayHasKey('iv', $payload); $this->assertArrayHasKey('tag', $payload); $this->assertArrayHasKey('ct', $payload); $this->assertSame(1, $payload['v']); } }