Skip to content

Commit

Permalink
Add abstract Key class
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalandan committed Jan 7, 2025
1 parent 9b9209e commit cd10d3a
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 0 deletions.
60 changes: 60 additions & 0 deletions src/Nexus/Encryption/Key.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

/**
* This file is part of the Nexus framework.
*
* (c) John Paul E. Balandan, CPA <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace Nexus\Encryption;

abstract class Key
{
private ?string $keyString;

public function __construct(Secret $key)
{
$this->keyString = $key->reveal();
}

public function __destruct()
{
sodium_memzero($this->keyString);
}

public function __clone(): void
{
throw new \BadMethodCallException(\sprintf('Cannot clone a %s object.', basename(static::class)));
}

/**
* @return array<string, string>
*/
public function __debugInfo(): array
{
return ['keyString' => '[redacted]'];
}

public function __serialize(): never
{
throw new \BadMethodCallException(\sprintf('Cannot serialise a %s object.', static::class));
}

/**
* @param array<string, mixed> $data
*/
public function __unserialize(array $data): never
{
throw new \BadMethodCallException(\sprintf('Cannot unserialise a %s object.', static::class)); // @codeCoverageIgnore
}

public function getKeyString(): string
{
return $this->keyString ?? '';
}
}
90 changes: 90 additions & 0 deletions tests/Encryption/KeyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

declare(strict_types=1);

/**
* This file is part of the Nexus framework.
*
* (c) John Paul E. Balandan, CPA <[email protected]>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace Nexus\Tests\Encryption;

use Nexus\Encryption\Key;
use Nexus\Encryption\Secret;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

/**
* @internal
*/
#[CoversClass(Key::class)]
#[Group('unit-test')]
final class KeyTest extends TestCase
{
private Key&MockObject $key;

protected function setUp(): void
{
parent::setUp();

$this->key = $this->getMockBuilder(Key::class)
->setConstructorArgs([new Secret(random_bytes(32))])
->onlyMethods([])
->getMock()
;
}

public function testCannotCloneKey(): void
{
$this->expectException(\BadMethodCallException::class);
$this->expectExceptionMessage(\sprintf('Cannot clone a %s object.', \get_class($this->key)));

clone $this->key; // @phpstan-ignore expr.resultUnused
}

public function testCannotSerialiseKey(): void
{
$this->expectException(\BadMethodCallException::class);
$this->expectExceptionMessage(\sprintf('Cannot serialise a %s object.', \get_class($this->key)));

serialize($this->key);
}

public function testHidesKeyStringFromDump(): void
{
$secret = new Secret(random_bytes(32));
$this->key = $this->getMockBuilder(Key::class)
->setConstructorArgs([$secret])
->onlyMethods([])
->getMock()
;

ob_start();
var_dump($this->key);
$dump = (string) ob_get_clean();
$print = print_r($this->key, true);

self::assertStringNotContainsString($secret->reveal(), $dump);
self::assertStringContainsString('[redacted]', $dump);
self::assertStringNotContainsString($secret->reveal(), $print);
self::assertStringContainsString('[redacted]', $print);
}

public function testGetKeyString(): void
{
$secret = new Secret(random_bytes(32));
$this->key = $this->getMockBuilder(Key::class)
->setConstructorArgs([$secret])
->onlyMethods([])
->getMock()
;

self::assertSame($secret->reveal(), $this->key->getKeyString());
}
}

0 comments on commit cd10d3a

Please sign in to comment.