diff --git a/src/Base32.php b/src/Base32.php index b4f2de1452acf79b4df5c2e4bf48c5d11c1ddbae..2c3ee61dc700f459da63d3e99e8ec4da84ce4aa1 100644 --- a/src/Base32.php +++ b/src/Base32.php @@ -2,8 +2,11 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; +use InvalidArgumentException; +use RangeException; + /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -182,6 +185,31 @@ abstract class Base32 implements EncoderInterface return \pack('C', $src + $diff); } + /** + * @param string $encodedString + * @return string + */ + public static function decodeNoPadding(string $encodedString, bool $upper = false): string + { + $srcLen = Binary::safeStrlen($encodedString); + if ($srcLen === 0) { + return ''; + } + if (($srcLen & 7) === 0) { + for ($j = 0; $j < 7; ++$j) { + if ($encodedString[$srcLen - 1] === '=') { + throw new InvalidArgumentException( + "decodeNoPadding() doesn't tolerate padding" + ); + } + } + } + return static::doDecode( + $encodedString, + $upper, + true + ); + } /** * Base32 decoding @@ -287,6 +315,9 @@ abstract class Base32 implements EncoderInterface (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff ); $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8; + if ($strictPadding) { + $err |= ($c6 << 5) & 0xff; + } } elseif ($i + 5 < $srcLen) { /** @var int $c1 */ $c1 = static::$method($chunk[2]); @@ -324,6 +355,9 @@ abstract class Base32 implements EncoderInterface (($c3 << 4) | ($c4 >> 1) ) & 0xff ); $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8; + if ($strictPadding) { + $err |= ($c4 << 7) & 0xff; + } } elseif ($i + 3 < $srcLen) { /** @var int $c1 */ $c1 = static::$method($chunk[2]); @@ -338,6 +372,9 @@ abstract class Base32 implements EncoderInterface (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff ); $err |= ($c0 | $c1 | $c2 | $c3) >> 8; + if ($strictPadding) { + $err |= ($c3 << 4) & 0xff; + } } elseif ($i + 2 < $srcLen) { /** @var int $c1 */ $c1 = static::$method($chunk[2]); @@ -350,6 +387,9 @@ abstract class Base32 implements EncoderInterface (($c1 << 6) | ($c2 << 1) ) & 0xff ); $err |= ($c0 | $c1 | $c2) >> 8; + if ($strictPadding) { + $err |= ($c2 << 6) & 0xff; + } } elseif ($i + 1 < $srcLen) { /** @var int $c1 */ $c1 = static::$method($chunk[2]); @@ -359,6 +399,9 @@ abstract class Base32 implements EncoderInterface (($c0 << 3) | ($c1 >> 2) ) & 0xff ); $err |= ($c0 | $c1) >> 8; + if ($strictPadding) { + $err |= ($c1 << 6) & 0xff; + } } else { $dest .= \pack( 'C', @@ -369,7 +412,7 @@ abstract class Base32 implements EncoderInterface } $check = ($err === 0); if (!$check) { - throw new \RangeException( + throw new RangeException( 'Base32::doDecode() only expects characters in the correct base32 alphabet' ); } diff --git a/src/Base32Hex.php b/src/Base32Hex.php index 68fdad52c8941e563ae489d4123bd4802151d325..b868dd0483122c33a4bc682c67010f189f34e7d4 100644 --- a/src/Base32Hex.php +++ b/src/Base32Hex.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/Base64.php b/src/Base64.php index 6cf44bffafa153eb4f58d38228d07abb8501f97d..5422aa47ebb75e7690722daab8e7b194ed93d117 100644 --- a/src/Base64.php +++ b/src/Base64.php @@ -2,8 +2,11 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; +use InvalidArgumentException; +use RangeException; + /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -141,12 +144,12 @@ abstract class Base64 implements EncoderInterface } } if (($srcLen & 3) === 1) { - throw new \RangeException( + throw new RangeException( 'Incorrect padding' ); } if ($encodedString[$srcLen - 1] === '=') { - throw new \RangeException( + throw new RangeException( 'Incorrect padding' ); } @@ -208,13 +211,43 @@ abstract class Base64 implements EncoderInterface } $check = ($err === 0); if (!$check) { - throw new \RangeException( + throw new RangeException( 'Base64::decode() only expects characters in the correct base64 alphabet' ); } return $dest; } + /** + * @param string $encodedString + * @return string + */ + public static function decodeNoPadding(string $encodedString): string + { + $srcLen = Binary::safeStrlen($encodedString); + if ($srcLen === 0) { + return ''; + } + if (($srcLen & 3) === 0) { + if ($encodedString[$srcLen - 1] === '=') { + throw new InvalidArgumentException( + "decodeNoPadding() doesn't tolerate padding" + ); + } + if (($srcLen & 3) > 1) { + if ($encodedString[$srcLen - 2] === '=') { + throw new InvalidArgumentException( + "decodeNoPadding() doesn't tolerate padding" + ); + } + } + } + return static::decode( + $encodedString, + true + ); + } + /** * Uses bitwise operators instead of table-lookups to turn 6-bit integers * into 8-bit integers. diff --git a/src/Base64DotSlash.php b/src/Base64DotSlash.php index 8ad2e2bf155c773207a818acf4c01892bdaac3e1..5e98a8f7930f0bf84f0b117a120f842d94846d1a 100644 --- a/src/Base64DotSlash.php +++ b/src/Base64DotSlash.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/Base64DotSlashOrdered.php b/src/Base64DotSlashOrdered.php index dd1459e8508ff864cfa4191ccbc9ee2de6c42e11..9780b14bb0ce4451657079927ad15cf081310a05 100644 --- a/src/Base64DotSlashOrdered.php +++ b/src/Base64DotSlashOrdered.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/Base64UrlSafe.php b/src/Base64UrlSafe.php index 1a4107527a794458655113dd7c0dd780071584a6..8192c63d585f45a28f88dee1ed2a77614ee4f1f9 100644 --- a/src/Base64UrlSafe.php +++ b/src/Base64UrlSafe.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/Binary.php b/src/Binary.php index add0522896ce1b39f14fbdea8868f7e1f43f1c13..4a365721b520c80b3f32bda82e5f36b7887494be 100644 --- a/src/Binary.php +++ b/src/Binary.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/EncoderInterface.php b/src/EncoderInterface.php index 7aeee55a8604d5c9f1def288ce0ff6db3006ea61..9cafbf96c839a163e99c2bd86bc7ad925f4b5c15 100644 --- a/src/EncoderInterface.php +++ b/src/EncoderInterface.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/Encoding.php b/src/Encoding.php index 896a6684179e61c9c846a67264ca94d0ba30ad30..1336935c871895d744a2fbf8bff45a90d2aad884 100644 --- a/src/Encoding.php +++ b/src/Encoding.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/Hex.php b/src/Hex.php index 4c2732862dcbb607f756b41e8bd8e90142cdc07c..a242b94ac5e02dbadf4c204af1ef13fa3b624558 100644 --- a/src/Hex.php +++ b/src/Hex.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/src/RFC4648.php b/src/RFC4648.php index 492cad00ef4776bfbcd097eb53c00c0697c945a6..5ceda31fd932cff0356aabde1469dc57d23ebe34 100644 --- a/src/RFC4648.php +++ b/src/RFC4648.php @@ -3,7 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime; /** - * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. + * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/tests/Base32HexTest.php b/tests/Base32HexTest.php index be83b7a6e9da0af2d2adae0094bcd309d5d4ae1f..009b5b97de9e11c93e737de41bd4c2b2cd01d3b7 100644 --- a/tests/Base32HexTest.php +++ b/tests/Base32HexTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime\Tests; +use InvalidArgumentException; use ParagonIE\ConstantTime\Base32Hex; use PHPUnit\Framework\TestCase; @@ -50,4 +51,11 @@ class Base32HexTest extends TestCase } } } + + public function testDecodeNoPadding() + { + Base32Hex::decodeNoPadding('aaaqe'); + $this->expectException(InvalidArgumentException::class); + Base32Hex::decodeNoPadding('aaaqe==='); + } } diff --git a/tests/Base32Test.php b/tests/Base32Test.php index 6eb07defd9164faa9e87ae077fd8a9408d08d2d7..a3f053b8f0a8c9f85ebcb649667c1b9bf3a91c3d 100644 --- a/tests/Base32Test.php +++ b/tests/Base32Test.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime\Tests; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use ParagonIE\ConstantTime\Base32; @@ -51,4 +52,31 @@ class Base32Test extends TestCase } } } + + public function canonProvider() + { + return [ + ['me', 'mf'], + ['mfra', 'mfrb'], + ['mfrgg', 'mfrgh'], + ['mfrggza', 'mfrggzb'] + ]; + } + + /** + * @dataProvider canonProvider + */ + public function testCanonicalBase32(string $canonical, string $munged) + { + Base32::decode($canonical); + $this->expectException(\RangeException::class); + Base32::decodeNoPadding($munged); + } + + public function testDecodeNoPadding() + { + Base32::decodeNoPadding('aaaqe'); + $this->expectException(InvalidArgumentException::class); + Base32::decodeNoPadding('aaaqe==='); + } } diff --git a/tests/Base64DotSlashOrderedTest.php b/tests/Base64DotSlashOrderedTest.php index b0d47e8986eccafaeb2ba802454f2d3386f5ea7b..614dabe0b716b4e8a40f98148516d2da062c39f2 100644 --- a/tests/Base64DotSlashOrderedTest.php +++ b/tests/Base64DotSlashOrderedTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime\Tests; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use ParagonIE\ConstantTime\Base64DotSlashOrdered; use RangeException; @@ -38,6 +39,14 @@ class Base64DotSlashOrderedTest extends TestCase } } } + + public function testDecodeNoPadding() + { + Base64DotSlashOrdered::decodeNoPadding('..'); + $this->expectException(InvalidArgumentException::class); + Base64DotSlashOrdered::decodeNoPadding('..=='); + } + /** * @dataProvider canonicalDataProvider */ diff --git a/tests/Base64DotSlashTest.php b/tests/Base64DotSlashTest.php index ca4de2871fe67ca7cb8412bc8ef7466e25391821..2e61f23bd887ec5cb3e11d0c10df6a88230478ba 100644 --- a/tests/Base64DotSlashTest.php +++ b/tests/Base64DotSlashTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime\Tests; +use InvalidArgumentException; use ParagonIE\ConstantTime\Base64DotSlash; use PHPUnit\Framework\TestCase; use RangeException; @@ -39,6 +40,13 @@ class Base64DotSlashTest extends TestCase } } + public function testDecodeNoPadding() + { + Base64DotSlash::decodeNoPadding('..'); + $this->expectException(InvalidArgumentException::class); + Base64DotSlash::decodeNoPadding('..=='); + } + /** * @dataProvider canonicalDataProvider */ @@ -56,6 +64,7 @@ class Base64DotSlashTest extends TestCase $this->expectException(RangeException::class); Base64DotSlash::decode($x, true); } + protected function getNextChar(string $c): string { return strtr( diff --git a/tests/Base64Test.php b/tests/Base64Test.php index c96468600754e3eab84daa9495a64fd30b1c1b92..7d97fae3d2720150088cdbba2aa83c215519a5c1 100644 --- a/tests/Base64Test.php +++ b/tests/Base64Test.php @@ -2,6 +2,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime\Tests; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use ParagonIE\ConstantTime\Base64; use RangeException; @@ -94,6 +95,13 @@ class Base64Test extends TestCase Base64::decode('00==', true); } + public function testDecodeNoPadding() + { + Base64::decodeNoPadding('0w'); + $this->expectException(InvalidArgumentException::class); + Base64::decodeNoPadding('0w=='); + } + /** * @dataProvider canonicalDataProvider */ diff --git a/tests/Base64UrlSafeTest.php b/tests/Base64UrlSafeTest.php index f0d41238b3c874c8cd5087b3d8d35d9f754e4ff1..831f814ee3ec2a8769b1969d773028350b404579 100644 --- a/tests/Base64UrlSafeTest.php +++ b/tests/Base64UrlSafeTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace ParagonIE\ConstantTime\Tests; use Exception; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use ParagonIE\ConstantTime\Base64UrlSafe; use ParagonIE\ConstantTime\Binary; @@ -64,6 +65,13 @@ class Base64UrlSafeTest extends TestCase ); } + public function testDecodeNoPadding() + { + Base64UrlSafe::decodeNoPadding('0w'); + $this->expectException(InvalidArgumentException::class); + Base64UrlSafe::decodeNoPadding('0w=='); + } + /** * @dataProvider canonicalDataProvider */