diff --git a/composer.json b/composer.json index 583fe366fb1c19cd931132ed30faec5c701a50b9..2fe9717adb7b8bdda10e6f2d0ee198139459b96c 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 dade79a2c201ce89448f890016dae2bfa82f9e06..af5ec45274cf9d52c6c38cb11cb239ff80f23921 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 ed19f336f706dd372b7965d5a093e387ae3f59ca..be83b7a6e9da0af2d2adae0094bcd309d5d4ae1f 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 65e8e7c56b16e1134de1a74de02d5a8967f07fa8..6eb07defd9164faa9e87ae077fd8a9408d08d2d7 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 f7dc828b1cb68e148ca1b8925627a2aeae8eb3c0..b0d47e8986eccafaeb2ba802454f2d3386f5ea7b 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 257a3d5495375a8db4e2870150ad37bc059733c7..ca4de2871fe67ca7cb8412bc8ef7466e25391821 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 16ab47da0e1643de2d264200838591064574786b..339f5d23fe70033f0611f3fcc5cc36dc8e9f72f5 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 136ed611ce8bdde78a9cd420bc5000031706a4fd..f0d41238b3c874c8cd5087b3d8d35d9f754e4ff1 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 0000000000000000000000000000000000000000..971cb6680310f600c4a8e73dbcc6349260f6b6bb --- /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 6f774d8c04b75e0b5eacf32ac42e17cc08e2fd8b..0c2986e54bfbcc0c05f9ce16356049407477e73c 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 a6653de885dfc69f1c66357a3a66be70c9fac797..c19c515c7b4abb1cc78d26598bcb55e17e4ef874 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() {