From 74e3151132650bb151b51957165495364b9336e9 Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises <security@paragonie.com> Date: Fri, 10 Jun 2022 02:59:59 -0400 Subject: [PATCH] Overhaul unit tests. Add test for strict padding. --- composer.json | 5 ++++ psalm.xml | 4 ++- tests/Base32HexTest.php | 8 ++++-- tests/Base32Test.php | 8 ++++-- tests/Base64DotSlashOrderedTest.php | 37 ++++++++++++++++++++++++++-- tests/Base64DotSlashTest.php | 37 ++++++++++++++++++++++++++-- tests/Base64Test.php | 38 +++++++++++++++++++++++++++-- tests/Base64UrlSafeTest.php | 37 +++++++++++++++++++++++++++- tests/CanonicalTrait.php | 30 +++++++++++++++++++++++ tests/EncodingTest.php | 19 ++++++++------- tests/RFC4648Test.php | 17 +++++++------ 11 files changed, 211 insertions(+), 29 deletions(-) create mode 100644 tests/CanonicalTrait.php diff --git a/composer.json b/composer.json index 583fe36..2fe9717 100644 --- a/composer.json +++ b/composer.json @@ -47,5 +47,10 @@ "psr-4": { "ParagonIE\\ConstantTime\\": "src/" } + }, + "autoload-dev": { + "psr-4": { + "ParagonIE\\ConstantTime\\Tests\\": "tests/" + } } } diff --git a/psalm.xml b/psalm.xml index dade79a..af5ec45 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,10 +1,12 @@ <?xml version="1.0"?> <psalm useDocblockTypes="true" - totallyTyped="true" > <projectFiles> <directory name="src" /> + <ignoreFiles> + <directory name="tests" /> + </ignoreFiles> </projectFiles> <issueHandlers> <UnnecessaryVarAnnotation errorLevel="info" /> diff --git a/tests/Base32HexTest.php b/tests/Base32HexTest.php index ed19f33..be83b7a 100644 --- a/tests/Base32HexTest.php +++ b/tests/Base32HexTest.php @@ -1,7 +1,11 @@ <?php -use \ParagonIE\ConstantTime\Base32Hex; +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; -class Base32HexTest extends PHPUnit\Framework\TestCase +use ParagonIE\ConstantTime\Base32Hex; +use PHPUnit\Framework\TestCase; + +class Base32HexTest extends TestCase { /** * @covers Base32Hex::encode() diff --git a/tests/Base32Test.php b/tests/Base32Test.php index 65e8e7c..6eb07de 100644 --- a/tests/Base32Test.php +++ b/tests/Base32Test.php @@ -1,7 +1,11 @@ <?php -use \ParagonIE\ConstantTime\Base32; +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; -class Base32Test extends PHPUnit\Framework\TestCase +use PHPUnit\Framework\TestCase; +use ParagonIE\ConstantTime\Base32; + +class Base32Test extends TestCase { /** * @covers Base32::encode() diff --git a/tests/Base64DotSlashOrderedTest.php b/tests/Base64DotSlashOrderedTest.php index f7dc828..b0d47e8 100644 --- a/tests/Base64DotSlashOrderedTest.php +++ b/tests/Base64DotSlashOrderedTest.php @@ -1,8 +1,15 @@ <?php -use \ParagonIE\ConstantTime\Base64DotSlashOrdered; +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; -class Base64DotSlashOrderedTest extends PHPUnit\Framework\TestCase +use PHPUnit\Framework\TestCase; +use ParagonIE\ConstantTime\Base64DotSlashOrdered; +use RangeException; + +class Base64DotSlashOrderedTest extends TestCase { + use CanonicalTrait; + /** * @covers Base64DotSlashOrdered::encode() * @covers Base64DotSlashOrdered::decode() @@ -31,4 +38,30 @@ class Base64DotSlashOrderedTest extends PHPUnit\Framework\TestCase } } } + /** + * @dataProvider canonicalDataProvider + */ + public function testNonCanonical(string $input) + { + $w = Base64DotSlashOrdered::encodeUnpadded($input); + Base64DotSlashOrdered::decode($w); + Base64DotSlashOrdered::decode($w, true); + + // Mess with padding: + $x = $this->increment($w); + Base64DotSlashOrdered::decode($x); + + // Should throw in strict mode: + $this->expectException(RangeException::class); + Base64DotSlashOrdered::decode($x, true); + } + + protected function getNextChar(string $c): string + { + return strtr( + $c, + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./', + 'BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./A' + ); + } } diff --git a/tests/Base64DotSlashTest.php b/tests/Base64DotSlashTest.php index 257a3d5..ca4de28 100644 --- a/tests/Base64DotSlashTest.php +++ b/tests/Base64DotSlashTest.php @@ -1,8 +1,15 @@ <?php -use \ParagonIE\ConstantTime\Base64DotSlash; +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; -class Base64DotSlashTest extends PHPUnit\Framework\TestCase +use ParagonIE\ConstantTime\Base64DotSlash; +use PHPUnit\Framework\TestCase; +use RangeException; + +class Base64DotSlashTest extends TestCase { + use CanonicalTrait; + /** * @covers Base64DotSlash::encode() * @covers Base64DotSlash::decode() @@ -31,4 +38,30 @@ class Base64DotSlashTest extends PHPUnit\Framework\TestCase } } } + + /** + * @dataProvider canonicalDataProvider + */ + public function testNonCanonical(string $input) + { + $w = Base64DotSlash::encodeUnpadded($input); + Base64DotSlash::decode($w); + Base64DotSlash::decode($w, true); + + // Mess with padding: + $x = $this->increment($w); + Base64DotSlash::decode($x); + + // Should throw in strict mode: + $this->expectException(RangeException::class); + Base64DotSlash::decode($x, true); + } + protected function getNextChar(string $c): string + { + return strtr( + $c, + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./', + 'BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./A' + ); + } } diff --git a/tests/Base64Test.php b/tests/Base64Test.php index 16ab47d..339f5d2 100644 --- a/tests/Base64Test.php +++ b/tests/Base64Test.php @@ -1,8 +1,15 @@ <?php -use \ParagonIE\ConstantTime\Base64; +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; -class Base64Test extends PHPUnit\Framework\TestCase +use PHPUnit\Framework\TestCase; +use ParagonIE\ConstantTime\Base64; +use RangeException; + +class Base64Test extends TestCase { + use CanonicalTrait; + /** * @covers Base64::encode() * @covers Base64::decode() @@ -76,4 +83,31 @@ class Base64Test extends PHPUnit\Framework\TestCase ); } } + + /** + * @dataProvider canonicalDataProvider + */ + public function testNonCanonical(string $input) + { + $w = Base64::encodeUnpadded($input); + Base64::decode($w); + Base64::decode($w, true); + + // Mess with padding: + $x = $this->increment($w); + Base64::decode($x); + + // Should throw in strict mode: + $this->expectException(RangeException::class); + Base64::decode($x, true); + } + + protected function getNextChar(string $c): string + { + return strtr( + $c, + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + 'BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/A' + ); + } } diff --git a/tests/Base64UrlSafeTest.php b/tests/Base64UrlSafeTest.php index 136ed61..f0d4123 100644 --- a/tests/Base64UrlSafeTest.php +++ b/tests/Base64UrlSafeTest.php @@ -1,13 +1,21 @@ <?php +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; +use Exception; +use PHPUnit\Framework\TestCase; use ParagonIE\ConstantTime\Base64UrlSafe; use ParagonIE\ConstantTime\Binary; +use RangeException; +use TypeError; /** * Class Base64UrlSafeTest */ -class Base64UrlSafeTest extends PHPUnit\Framework\TestCase +class Base64UrlSafeTest extends TestCase { + use CanonicalTrait; + /** * @covers Base64UrlSafe::encode() * @covers Base64UrlSafe::decode() @@ -55,4 +63,31 @@ class Base64UrlSafeTest extends PHPUnit\Framework\TestCase $enc ); } + + /** + * @dataProvider canonicalDataProvider + */ + public function testNonCanonical(string $input) + { + $w = Base64UrlSafe::encodeUnpadded($input); + Base64UrlSafe::decode($w); + Base64UrlSafe::decode($w, true); + + // Mess with padding: + $x = $this->increment($w); + Base64UrlSafe::decode($x); + + // Should throw in strict mode: + $this->expectException(RangeException::class); + Base64UrlSafe::decode($x, true); + } + + protected function getNextChar(string $c): string + { + return strtr( + $c, + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', + 'BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_A' + ); + } } diff --git a/tests/CanonicalTrait.php b/tests/CanonicalTrait.php new file mode 100644 index 0000000..971cb66 --- /dev/null +++ b/tests/CanonicalTrait.php @@ -0,0 +1,30 @@ +<?php +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; + +use ParagonIE\ConstantTime\Binary; + +/** + * @method getNextChar(string $c): string + */ +trait CanonicalTrait +{ + public function canonicalDataProvider(): array + { + return [ + ['a'], + ['ab'], + ['abcd'], + ["\xff"], + ["\xff\xff"], + ["\xff\xff\xff\xff"] + ]; + } + + protected function increment(string $str): string + { + $i = Binary::safeStrlen($str) - 1; + $c = $this->getNextChar($str[$i]); + return Binary::safeSubstr($str, 0, $i) . $c; + } +} diff --git a/tests/EncodingTest.php b/tests/EncodingTest.php index 6f774d8..0c2986e 100644 --- a/tests/EncodingTest.php +++ b/tests/EncodingTest.php @@ -1,14 +1,15 @@ <?php -use \ParagonIE\ConstantTime\Base32; -use \ParagonIE\ConstantTime\Base32Hex; -use \ParagonIE\ConstantTime\Base64; -use \ParagonIE\ConstantTime\Base64DotSlash; -use \ParagonIE\ConstantTime\Base64DotSlashOrdered; -use \ParagonIE\ConstantTime\Base64UrlSafe; -use \ParagonIE\ConstantTime\Encoding; -use \ParagonIE\ConstantTime\Hex; +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; +use PHPUnit\Framework\TestCase; -class EncodingTest extends PHPUnit\Framework\TestCase +use ParagonIE\ConstantTime\Base32; +use ParagonIE\ConstantTime\Base32Hex; +use ParagonIE\ConstantTime\Base64UrlSafe; +use ParagonIE\ConstantTime\Encoding; +use ParagonIE\ConstantTime\Hex; + +class EncodingTest extends TestCase { public function testBase32Encode() { diff --git a/tests/RFC4648Test.php b/tests/RFC4648Test.php index a6653de..c19c515 100644 --- a/tests/RFC4648Test.php +++ b/tests/RFC4648Test.php @@ -1,18 +1,19 @@ <?php -use \ParagonIE\ConstantTime\Base32; -use \ParagonIE\ConstantTime\Base32Hex; -use \ParagonIE\ConstantTime\Base64; -use \ParagonIE\ConstantTime\Base64DotSlash; -use \ParagonIE\ConstantTime\Base64DotSlashOrdered; -use \ParagonIE\ConstantTime\Encoding; -use \ParagonIE\ConstantTime\Hex; +declare(strict_types=1); +namespace ParagonIE\ConstantTime\Tests; + +use ParagonIE\ConstantTime\Base32; +use ParagonIE\ConstantTime\Base32Hex; +use ParagonIE\ConstantTime\Base64; +use ParagonIE\ConstantTime\Hex; +use PHPUnit\Framework\TestCase; /** * Class RFC4648Test * * @ref https://tools.ietf.org/html/rfc4648#section-10 */ -class RFC4648Test extends PHPUnit\Framework\TestCase +class RFC4648Test extends TestCase { public function testVectorBase64() { -- GitLab