Skip to content

Commit

Permalink
Merge pull request #17 from IlicMiljan/add-tag-aware-cipher
Browse files Browse the repository at this point in the history
Implement `TagAwareCipher`
  • Loading branch information
IlicMiljan authored Mar 15, 2024
2 parents 9c6e941 + 02573a0 commit 37969a8
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 0 deletions.
48 changes: 48 additions & 0 deletions src/Cipher/TagAwareCipher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace IlicMiljan\SecureProps\Cipher;

use IlicMiljan\SecureProps\Encoder\Base64Encoder;
use IlicMiljan\SecureProps\Encoder\Encoder;
use SensitiveParameter;

class TagAwareCipher implements Cipher
{
private Encoder $encoder;

public function __construct(
private Cipher $cipher,
?Encoder $encoder = null
) {
if ($encoder === null) {
$this->encoder = new Base64Encoder();
} else {
$this->encoder = $encoder;
}
}

public function encrypt(#[SensitiveParameter] string $string): string
{
$encryptedString = $this->cipher->encrypt($string);

return $this->encoder->encode('<ENC>' . $encryptedString . '</ENC>');
}

public function decrypt(#[SensitiveParameter] string $string): string
{
$data = $this->encoder->decode($string);

if (!$this->shouldDecrypt($data)) {
return $string;
}

preg_match('/^<ENC>(.*)<\/ENC>$/', $data, $matches);

return $this->cipher->decrypt($matches[1]);
}

private function shouldDecrypt(string $string): bool
{
return preg_match('/^<ENC>(.*)<\/ENC>$/', $string) === 1;
}
}
128 changes: 128 additions & 0 deletions tests/Cipher/TagAwareCipherTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace IlicMiljan\SecureProps\Tests\Cipher;

use IlicMiljan\SecureProps\Cipher\Cipher;
use IlicMiljan\SecureProps\Cipher\Exception\CipherException;
use IlicMiljan\SecureProps\Cipher\TagAwareCipher;
use IlicMiljan\SecureProps\Encoder\Encoder;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class TagAwareCipherTest extends TestCase
{
/**
* @var Cipher&MockObject
*/
private $decoratedCipher;
/**
* @var Encoder&MockObject
*/
private $encoder;
private TagAwareCipher $cipher;
private TagAwareCipher $cipherWithCustomEncoder;

protected function setUp(): void
{
$this->decoratedCipher = $this->createMock(Cipher::class);
$this->encoder = $this->createMock(Encoder::class);

$this->cipher = new TagAwareCipher($this->decoratedCipher);
$this->cipherWithCustomEncoder = new TagAwareCipher($this->decoratedCipher, $this->encoder);
}

/**
* @throws CipherException
*/
public function testEncrypt(): void
{
$this->decoratedCipher->expects($this->once())
->method('encrypt')
->with('plainText')
->willReturn('encryptedText');

$result = $this->cipher->encrypt('plainText');

$this->assertEquals(base64_encode('<ENC>encryptedText</ENC>'), $result);
}

/**
* @throws CipherException
*/
public function testEncryptWithCustomEncoder(): void
{
$encryptedText = 'encryptedText';
$encodedText = 'encodedText';

$this->decoratedCipher->expects($this->once())
->method('encrypt')
->with('plainText')
->willReturn($encryptedText);

$this->encoder->expects($this->once())
->method('encode')
->with($this->stringContains('<ENC>' . $encryptedText . '</ENC>'))
->willReturn($encodedText);


$result = $this->cipherWithCustomEncoder->encrypt('plainText');

$this->assertEquals($encodedText, $result);
}

/**
* @throws CipherException
*/
public function testDecryptWithEncryptedTag(): void
{
$encodedTextWithEncTags = base64_encode('<ENC>encodedTextWithTags</ENC>');
$decryptedText = 'plainText';

$this->decoratedCipher->expects($this->once())
->method('decrypt')
->with($this->equalTo('encodedTextWithTags'))
->willReturn($decryptedText);

$result = $this->cipher->decrypt($encodedTextWithEncTags);

var_dump($result);

$this->assertEquals($decryptedText, $result);
}

/**
* @throws CipherException
*/
public function testDecryptWithCustomEncoderAndEncryptedTag(): void
{
$encodedTextWithEncTags = 'encodedTextWithTags';
$decryptedText = 'plainText';

$this->encoder->expects($this->once())
->method('decode')
->with($encodedTextWithEncTags)
->willReturn('<ENC>encryptedText</ENC>');

$this->decoratedCipher->expects($this->once())
->method('decrypt')
->with($this->equalTo('encryptedText'))
->willReturn($decryptedText);

$result = $this->cipherWithCustomEncoder->decrypt($encodedTextWithEncTags);

$this->assertEquals($decryptedText, $result);
}

/**
* @throws CipherException
*/
public function testDecryptWithCustomEncoderAndWithoutEncryptedTag(): void
{
$this->encoder->expects($this->once())->method('decode');
$this->decoratedCipher->expects($this->never())->method('decrypt');

$result = $this->cipherWithCustomEncoder->decrypt('plainText');

$this->assertEquals('plainText', $result);
}
}

0 comments on commit 37969a8

Please sign in to comment.