diff --git a/src/Cipher/AdvancedEncryptionStandardCipher.php b/src/Cipher/AdvancedEncryptionStandardCipher.php index cb6dad6..301a384 100644 --- a/src/Cipher/AdvancedEncryptionStandardCipher.php +++ b/src/Cipher/AdvancedEncryptionStandardCipher.php @@ -6,21 +6,20 @@ use IlicMiljan\SecureProps\Cipher\Exception\FailedDecryptingValue; use IlicMiljan\SecureProps\Cipher\Exception\FailedEncryptingValue; use IlicMiljan\SecureProps\Cipher\Exception\FailedGeneratingInitializationVector; -use InvalidArgumentException; +use IlicMiljan\SecureProps\Cipher\Exception\InvalidKeyLength; use SensitiveParameter; class AdvancedEncryptionStandardCipher implements Cipher { private const CIPHER = 'AES-256-GCM'; private const TAG_LENGTH = 16; + private const KEY_LENGTH = 32; public function __construct( #[SensitiveParameter] private string $key ) { - if (strlen($key) !== 32) { - throw new InvalidArgumentException('Key must be 32 bytes (256 bits) long.'); - } + $this->validateKey($key); } /** @@ -101,4 +100,11 @@ public function calculateInitializationVectorLength(string $cipher): int return $ivLength; } + + public function validateKey(string $key): void + { + if (strlen($key) !== self::KEY_LENGTH) { + throw new InvalidKeyLength(self::KEY_LENGTH); + } + } } diff --git a/src/Cipher/Exception/InvalidKeyLength.php b/src/Cipher/Exception/InvalidKeyLength.php new file mode 100644 index 0000000..59cbbc7 --- /dev/null +++ b/src/Cipher/Exception/InvalidKeyLength.php @@ -0,0 +1,25 @@ +expectedLength; + } +} diff --git a/src/Exception/EncryptionServiceException.php b/src/Exception/EncryptionServiceException.php new file mode 100644 index 0000000..e64fc6c --- /dev/null +++ b/src/Exception/EncryptionServiceException.php @@ -0,0 +1,9 @@ +type; + } +} diff --git a/src/Exception/ValueMustBeString.php b/src/Exception/ValueMustBeString.php new file mode 100644 index 0000000..be517bf --- /dev/null +++ b/src/Exception/ValueMustBeString.php @@ -0,0 +1,25 @@ +type; + } +} diff --git a/src/ObjectEncryptionService.php b/src/ObjectEncryptionService.php index 6e22326..c4420eb 100644 --- a/src/ObjectEncryptionService.php +++ b/src/ObjectEncryptionService.php @@ -5,9 +5,11 @@ use IlicMiljan\SecureProps\Attribute\Encrypted; use IlicMiljan\SecureProps\Cipher\Cipher; use IlicMiljan\SecureProps\Cipher\Exception\CipherException; +use IlicMiljan\SecureProps\Exception\EncryptionServiceException; +use IlicMiljan\SecureProps\Exception\ValueMustBeObject; +use IlicMiljan\SecureProps\Exception\ValueMustBeString; use IlicMiljan\SecureProps\Reader\Exception\ReaderException; use IlicMiljan\SecureProps\Reader\ObjectPropertiesReader; -use InvalidArgumentException; use ReflectionProperty; use SensitiveParameter; @@ -23,13 +25,14 @@ public function __construct( * @param mixed $value * @return object * + * @throws EncryptionServiceException * @throws CipherException * @throws ReaderException */ public function encrypt(#[SensitiveParameter] mixed $value): object { if (!is_object($value)) { - throw new InvalidArgumentException('Value must be object.'); + throw new ValueMustBeObject(gettype($value)); } $encryptedProperties = $this->objectPropertiesReader->getPropertiesWithAttribute($value, Encrypted::class); @@ -50,13 +53,14 @@ public function encrypt(#[SensitiveParameter] mixed $value): object * * @return object * + * @throws EncryptionServiceException * @throws CipherException * @throws ReaderException */ public function decrypt(#[SensitiveParameter] mixed $value): object { if (!is_object($value)) { - throw new InvalidArgumentException('Value must be object.'); + throw new ValueMustBeObject(gettype($value)); } $encryptedProperties = $this->objectPropertiesReader->getPropertiesWithAttribute($value, Encrypted::class); @@ -83,7 +87,7 @@ private function updatePropertyValue(ReflectionProperty $property, object $objec } if (!is_string($currentValue)) { - throw new InvalidArgumentException('Value must be string.'); + throw new ValueMustBeString(gettype($currentValue)); } $property->setValue($object, $callback($currentValue)); diff --git a/src/StringEncryptionService.php b/src/StringEncryptionService.php index 396138a..0eba0f5 100644 --- a/src/StringEncryptionService.php +++ b/src/StringEncryptionService.php @@ -4,7 +4,8 @@ use IlicMiljan\SecureProps\Cipher\Cipher; use IlicMiljan\SecureProps\Cipher\Exception\CipherException; -use InvalidArgumentException; +use IlicMiljan\SecureProps\Exception\EncryptionServiceException; +use IlicMiljan\SecureProps\Exception\ValueMustBeString; use SensitiveParameter; class StringEncryptionService implements EncryptionService @@ -16,13 +17,16 @@ public function __construct( /** * @param mixed $value + * * @return string + * + * @throws EncryptionServiceException * @throws CipherException */ public function encrypt(#[SensitiveParameter] mixed $value): string { if (!is_string($value)) { - throw new InvalidArgumentException('Value must be string.'); + throw new ValueMustBeString(gettype($value)); } return $this->cipher->encrypt($value); @@ -31,12 +35,14 @@ public function encrypt(#[SensitiveParameter] mixed $value): string /** * @param mixed $value * @return string + * + * @throws EncryptionServiceException * @throws CipherException */ public function decrypt(#[SensitiveParameter] mixed $value): string { if (!is_string($value)) { - throw new InvalidArgumentException('Value must be string.'); + throw new ValueMustBeString(gettype($value)); } return $this->cipher->decrypt($value); diff --git a/tests/Cipher/AdvancedEncryptionStandardCipherTest.php b/tests/Cipher/AdvancedEncryptionStandardCipherTest.php index 4c32a89..8e4d09d 100644 --- a/tests/Cipher/AdvancedEncryptionStandardCipherTest.php +++ b/tests/Cipher/AdvancedEncryptionStandardCipherTest.php @@ -3,7 +3,7 @@ namespace IlicMiljan\SecureProps\Tests\Cipher; use IlicMiljan\SecureProps\Cipher\AdvancedEncryptionStandardCipher; -use InvalidArgumentException; +use IlicMiljan\SecureProps\Cipher\Exception\InvalidKeyLength; use PHPUnit\Framework\TestCase; class AdvancedEncryptionStandardCipherTest extends TestCase @@ -17,7 +17,7 @@ protected function setUp(): void public function testConstructWithInvalidKeyLengthThrowsException(): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(InvalidKeyLength::class); new AdvancedEncryptionStandardCipher(openssl_random_pseudo_bytes(16)); } diff --git a/tests/Cipher/Exception/InvalidKeyLengthTest.php b/tests/Cipher/Exception/InvalidKeyLengthTest.php new file mode 100644 index 0000000..86186ff --- /dev/null +++ b/tests/Cipher/Exception/InvalidKeyLengthTest.php @@ -0,0 +1,47 @@ +expectedLength = 32; + } + + public function testCanBeCreated(): void + { + $exception = new InvalidKeyLength($this->expectedLength); + + $this->assertInstanceOf(InvalidKeyLength::class, $exception); + } + + public function testReturnsExpectedLength(): void + { + $exception = new InvalidKeyLength($this->expectedLength); + + $this->assertEquals($this->expectedLength, $exception->getExpectedLength()); + } + + public function testPreviousExceptionIsStored(): void + { + $previous = new RuntimeException('Previous exception'); + $exception = new InvalidKeyLength($this->expectedLength, $previous); + + $this->assertSame($previous, $exception->getPrevious()); + } + + public function testImplementsCipherExceptionInterface(): void + { + $exception = new InvalidKeyLength($this->expectedLength); + + $this->assertInstanceOf(CipherException::class, $exception); + } +} diff --git a/tests/Exception/ValueMustBeObjectTest.php b/tests/Exception/ValueMustBeObjectTest.php new file mode 100644 index 0000000..3008f29 --- /dev/null +++ b/tests/Exception/ValueMustBeObjectTest.php @@ -0,0 +1,47 @@ +type = 'string'; + } + + public function testCanBeCreated(): void + { + $exception = new ValueMustBeObject($this->type); + + $this->assertInstanceOf(ValueMustBeObject::class, $exception); + } + + public function testReturnsType(): void + { + $exception = new ValueMustBeObject($this->type); + + $this->assertEquals($this->type, $exception->getType()); + } + + public function testPreviousExceptionIsStored(): void + { + $previous = new RuntimeException('Previous exception'); + $exception = new ValueMustBeObject($this->type, $previous); + + $this->assertSame($previous, $exception->getPrevious()); + } + + public function testImplementsEncryptionServiceExceptionInterface(): void + { + $exception = new ValueMustBeObject($this->type); + + $this->assertInstanceOf(EncryptionServiceException::class, $exception); + } +} diff --git a/tests/Exception/ValueMustBeStringTest.php b/tests/Exception/ValueMustBeStringTest.php new file mode 100644 index 0000000..f6dc2f2 --- /dev/null +++ b/tests/Exception/ValueMustBeStringTest.php @@ -0,0 +1,47 @@ +type = 'object'; + } + + public function testCanBeCreated(): void + { + $exception = new ValueMustBeString($this->type); + + $this->assertInstanceOf(ValueMustBeString::class, $exception); + } + + public function testReturnsType(): void + { + $exception = new ValueMustBeString($this->type); + + $this->assertEquals($this->type, $exception->getType()); + } + + public function testPreviousExceptionIsStored(): void + { + $previous = new RuntimeException('Previous exception'); + $exception = new ValueMustBeString($this->type, $previous); + + $this->assertSame($previous, $exception->getPrevious()); + } + + public function testImplementsEncryptionServiceExceptionInterface(): void + { + $exception = new ValueMustBeString($this->type); + + $this->assertInstanceOf(EncryptionServiceException::class, $exception); + } +} diff --git a/tests/ObjectEncryptionServiceTest.php b/tests/ObjectEncryptionServiceTest.php index d2713ea..ea5b63c 100644 --- a/tests/ObjectEncryptionServiceTest.php +++ b/tests/ObjectEncryptionServiceTest.php @@ -5,10 +5,11 @@ use IlicMiljan\SecureProps\Attribute\Encrypted; use IlicMiljan\SecureProps\Cipher\Cipher; use IlicMiljan\SecureProps\Cipher\Exception\CipherException; +use IlicMiljan\SecureProps\Exception\ValueMustBeObject; +use IlicMiljan\SecureProps\Exception\ValueMustBeString; use IlicMiljan\SecureProps\ObjectEncryptionService; use IlicMiljan\SecureProps\Reader\Exception\ReaderException; use IlicMiljan\SecureProps\Reader\ObjectPropertiesReader; -use InvalidArgumentException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use ReflectionException; @@ -171,9 +172,9 @@ public function testDecryptSkipsNullProperties(): void * @throws ReaderException * @throws CipherException */ - public function testEncryptThrowsInvalidArgumentExceptionForNonObject(): void + public function testEncryptThrowsValueMustBeObjectExceptionForNonObject(): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(ValueMustBeObject::class); $this->service->encrypt('notAnObject'); } @@ -182,9 +183,9 @@ public function testEncryptThrowsInvalidArgumentExceptionForNonObject(): void * @throws CipherException * @throws ReaderException */ - public function testDecryptThrowsInvalidArgumentExceptionForNonObject(): void + public function testDecryptThrowsValueMustBeObjectExceptionForNonObject(): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(ValueMustBeObject::class); $this->service->decrypt('notAnObject'); } @@ -194,7 +195,7 @@ public function testDecryptThrowsInvalidArgumentExceptionForNonObject(): void * @throws ReaderException * @throws CipherException */ - public function testEncryptThrowsInvalidArgumentExceptionForNonString(): void + public function testEncryptThrowsValueMustBeStringExceptionForNonString(): void { $object = new class { #[Encrypted] @@ -208,7 +209,7 @@ public function testEncryptThrowsInvalidArgumentExceptionForNonString(): void ->method('getPropertiesWithAttribute') ->willReturn([$reflectionProperty]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ValueMustBeString::class); $this->service->encrypt($object); } @@ -218,7 +219,7 @@ public function testEncryptThrowsInvalidArgumentExceptionForNonString(): void * @throws CipherException * @throws ReaderException */ - public function testDecryptThrowsInvalidArgumentExceptionForNonString(): void + public function testDecryptThrowsValueMustBeStringExceptionForNonString(): void { $object = new class { #[Encrypted] @@ -232,7 +233,7 @@ public function testDecryptThrowsInvalidArgumentExceptionForNonString(): void ->method('getPropertiesWithAttribute') ->willReturn([$reflectionProperty]); - $this->expectException(InvalidArgumentException::class); + $this->expectException(ValueMustBeString::class); $this->service->decrypt($object); } diff --git a/tests/StringEncryptionServiceTest.php b/tests/StringEncryptionServiceTest.php index 496a0ea..b4a8136 100644 --- a/tests/StringEncryptionServiceTest.php +++ b/tests/StringEncryptionServiceTest.php @@ -4,8 +4,8 @@ use IlicMiljan\SecureProps\Cipher\Cipher; use IlicMiljan\SecureProps\Cipher\Exception\CipherException; +use IlicMiljan\SecureProps\Exception\ValueMustBeString; use IlicMiljan\SecureProps\StringEncryptionService; -use InvalidArgumentException; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -56,9 +56,9 @@ public function testDecryptSuccess(): void /** * @throws CipherException */ - public function testEncryptThrowsInvalidArgumentExceptionForNonString(): void + public function testEncryptThrowsValueMustBeStringExceptionForNonString(): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(ValueMustBeString::class); $this->service->encrypt(['plainText']); } @@ -66,9 +66,9 @@ public function testEncryptThrowsInvalidArgumentExceptionForNonString(): void /** * @throws CipherException */ - public function testDecryptThrowsInvalidArgumentExceptionForNonString(): void + public function testDecryptThrowsValueMustBeStringExceptionForNonString(): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(ValueMustBeString::class); $this->service->decrypt(['encryptedText']); }