Skip to content

Commit 67b5a89

Browse files
committed
Add encoders
1 parent ecd80d5 commit 67b5a89

15 files changed

+535
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Encryption\Encoding;
15+
16+
abstract class AbstractBase64Encoder implements EncoderInterface
17+
{
18+
public function encode(#[\SensitiveParameter] string $binaryString): string
19+
{
20+
return sodium_bin2base64($binaryString, $this->getBase64Variant());
21+
}
22+
23+
public function decode(#[\SensitiveParameter] string $encodedString, string $ignore = ''): string
24+
{
25+
return sodium_base642bin($encodedString, $this->getBase64Variant(), $ignore);
26+
}
27+
28+
/**
29+
* Get the variant of Base64 encoding to use.
30+
*/
31+
abstract protected function getBase64Variant(): int;
32+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Encryption\Encoding;
15+
16+
/**
17+
* Standard (A-Za-z0-9/\+) Base64 encoder.
18+
*/
19+
final class Base64OriginalEncoder extends AbstractBase64Encoder
20+
{
21+
protected function getBase64Variant(): int
22+
{
23+
return SODIUM_BASE64_VARIANT_ORIGINAL;
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Encryption\Encoding;
15+
16+
/**
17+
* Standard (A-Za-z0-9/\+) Base64 encoder without `=` padding characters.
18+
*/
19+
final class Base64OriginalNoPaddingEncoder extends AbstractBase64Encoder
20+
{
21+
protected function getBase64Variant(): int
22+
{
23+
return SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING;
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Encryption\Encoding;
15+
16+
/**
17+
* URL-safe (A-Za-z0-9\-_) Base64 encoder.
18+
*/
19+
final class Base64UrlSafeEncoder extends AbstractBase64Encoder
20+
{
21+
protected function getBase64Variant(): int
22+
{
23+
return SODIUM_BASE64_VARIANT_URLSAFE;
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Encryption\Encoding;
15+
16+
/**
17+
* URL-safe (A-Za-z0-9\-_) Base64 encoder, without `=` padding characters.
18+
*/
19+
final class Base64UrlSafeNoPaddingEncoder extends AbstractBase64Encoder
20+
{
21+
protected function getBase64Variant(): int
22+
{
23+
return SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING;
24+
}
25+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Encryption\Encoding;
15+
16+
interface EncoderInterface
17+
{
18+
/**
19+
* Encodes a binary string into a readable format without cache-timing attacks.
20+
*/
21+
public function encode(#[\SensitiveParameter] string $binaryString): string;
22+
23+
/**
24+
* Converts an encoded string back to its original binary format without cache-timing attacks.
25+
*
26+
* @param string $ignore Characters to ignore when decoding (e.g. whitespace characters)
27+
*/
28+
public function decode(#[\SensitiveParameter] string $encodedString, string $ignore = ''): string;
29+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Encryption\Encoding;
15+
16+
/**
17+
* Encode/decode binary data to/from hexadecimal strings without any side-channel attacks.
18+
*/
19+
final class HexEncoder implements EncoderInterface
20+
{
21+
public function encode(#[\SensitiveParameter] string $binaryString): string
22+
{
23+
return sodium_bin2hex($binaryString);
24+
}
25+
26+
public function decode(#[\SensitiveParameter] string $encodedString, string $ignore = ''): string
27+
{
28+
return sodium_hex2bin($encodedString, $ignore);
29+
}
30+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Encryption\Encoding;
15+
16+
final class NullEncoder implements EncoderInterface
17+
{
18+
public function encode(#[\SensitiveParameter] string $binaryString): string
19+
{
20+
return $binaryString;
21+
}
22+
23+
public function decode(#[\SensitiveParameter] string $encodedString, string $ignore = ''): string
24+
{
25+
return $encodedString;
26+
}
27+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Tests\Encryption\Encoding;
15+
16+
use Nexus\Encryption\Encoding\EncoderInterface;
17+
use PHPUnit\Framework\Attributes\DataProvider;
18+
use PHPUnit\Framework\TestCase;
19+
20+
/**
21+
* @internal
22+
*/
23+
abstract class AbstractEncoderTestCase extends TestCase
24+
{
25+
/**
26+
* @param int<1, 32> $i
27+
*/
28+
#[DataProvider('provideEncodeDecodeCases')]
29+
public function testEncodeDecode(int $i): void
30+
{
31+
$encoder = $this->createEncoder();
32+
$data = random_bytes($i);
33+
34+
$encoded = $encoder->encode($data);
35+
$decoded = $encoder->decode($encoded);
36+
37+
self::assertSame($data, $decoded);
38+
self::assertSame($this->nativeEncode($data), $encoded);
39+
self::assertSame($this->nativeDecode($encoded), $decoded);
40+
}
41+
42+
/**
43+
* @return iterable<string, array{int}>
44+
*/
45+
public static function provideEncodeDecodeCases(): iterable
46+
{
47+
for ($i = 1; $i <= 32; ++$i) {
48+
yield \sprintf('%02d bytes', $i) => [$i];
49+
}
50+
}
51+
52+
abstract protected function createEncoder(): EncoderInterface;
53+
54+
abstract protected function nativeEncode(string $data): string;
55+
56+
abstract protected function nativeDecode(string $encoded): string;
57+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of the Nexus framework.
7+
*
8+
* (c) John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Nexus\Tests\Encryption\Encoding;
15+
16+
use Nexus\Encryption\Encoding\AbstractBase64Encoder;
17+
use Nexus\Encryption\Encoding\Base64OriginalEncoder;
18+
use Nexus\Encryption\Encoding\EncoderInterface;
19+
use PHPUnit\Framework\Attributes\CoversClass;
20+
use PHPUnit\Framework\Attributes\Group;
21+
22+
/**
23+
* @internal
24+
*/
25+
#[CoversClass(AbstractBase64Encoder::class)]
26+
#[CoversClass(Base64OriginalEncoder::class)]
27+
#[Group('unit-test')]
28+
final class Base64OriginalEncoderTest extends AbstractEncoderTestCase
29+
{
30+
protected function createEncoder(): EncoderInterface
31+
{
32+
return new Base64OriginalEncoder();
33+
}
34+
35+
protected function nativeEncode(string $data): string
36+
{
37+
return sodium_bin2base64($data, SODIUM_BASE64_VARIANT_ORIGINAL);
38+
}
39+
40+
protected function nativeDecode(string $encoded): string
41+
{
42+
return sodium_base642bin($encoded, SODIUM_BASE64_VARIANT_ORIGINAL);
43+
}
44+
}

0 commit comments

Comments
 (0)