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()
     {