Skip to content

Commit

Permalink
Add Base64UrlSafe utility and refactor code references (#576)
Browse files Browse the repository at this point in the history
* Add Base64UrlSafe utility and refactor code references

This commit adds the utility `Base64UrlSafe` to provide methods for encoding and decoding in Base64UrlSafe format. Simultaneously, the commit also updates multiple files across the library to use this utility instead of the previously used `ParagonIE\ConstantTime\Base64UrlSafe`. This change will ensure consistent use of this utility throughout the project, promoting maintainability and ease of updating in the future, if required.
  • Loading branch information
Spomky authored Jul 2, 2024
1 parent 4a05eb0 commit 1fe7ca8
Show file tree
Hide file tree
Showing 73 changed files with 581 additions and 85 deletions.
308 changes: 294 additions & 14 deletions phpstan-baseline.neon

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Experimental/KeyEncryption/AESCTR.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

use InvalidArgumentException;
use Jose\Component\Core\JWK;
use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyEncryption;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use function in_array;
use function is_string;
Expand Down
2 changes: 1 addition & 1 deletion src/Experimental/KeyEncryption/Chacha20Poly1305.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

use InvalidArgumentException;
use Jose\Component\Core\JWK;
use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\Encryption\Algorithm\KeyEncryption\KeyEncryption;
use LogicException;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use function in_array;
use function is_string;
Expand Down
2 changes: 1 addition & 1 deletion src/Experimental/Signature/Blake2b.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

use InvalidArgumentException;
use Jose\Component\Core\JWK;
use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\Signature\Algorithm\MacAlgorithm;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use function extension_loaded;
use function in_array;
Expand Down
2 changes: 1 addition & 1 deletion src/Library/Console/GeneratorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace Jose\Component\Console;

use InvalidArgumentException;
use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\KeyManagement\JWKFactory;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use function is_bool;
Expand Down
2 changes: 1 addition & 1 deletion src/Library/Core/JWK.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace Jose\Component\Core;

use InvalidArgumentException;
use Jose\Component\Core\Util\Base64UrlSafe;
use JsonSerializable;
use ParagonIE\ConstantTime\Base64UrlSafe;
use function array_key_exists;
use function in_array;
use function is_array;
Expand Down
218 changes: 218 additions & 0 deletions src/Library/Core/Util/Base64UrlSafe.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
<?php

declare(strict_types=1);

namespace Jose\Component\Core\Util;

/**
* 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
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

/**
* @readonly
*/
final class Base64UrlSafe
{
public static function encode(string $binString): string
{
return static::doEncode($binString, true);
}

public static function encodeUnpadded(string $src): string
{
return static::doEncode($src, false);
}

public static function decode(string $encodedString, bool $strictPadding = false): string
{
$srcLen = self::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}

if ($strictPadding) {
if (($srcLen & 3) === 0) {
if ($encodedString[$srcLen - 1] === '=') {
$srcLen--;
if ($encodedString[$srcLen - 1] === '=') {
$srcLen--;
}
}
}
if (($srcLen & 3) === 1) {
throw new RangeException('Incorrect padding');
}
if ($encodedString[$srcLen - 1] === '=') {
throw new RangeException('Incorrect padding');
}
} else {
$encodedString = rtrim($encodedString, '=');
$srcLen = self::safeStrlen($encodedString);
}

$err = 0;
$dest = '';
for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', self::safeSubstr($encodedString, $i, 4));
$c0 = static::decode6Bits($chunk[1]);
$c1 = static::decode6Bits($chunk[2]);
$c2 = static::decode6Bits($chunk[3]);
$c3 = static::decode6Bits($chunk[4]);

$dest .= pack(
'CCC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff),
((($c2 << 6) | $c3) & 0xff)
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
}

if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', self::safeSubstr($encodedString, $i, $srcLen - $i));
$c0 = static::decode6Bits($chunk[1]);

if ($i + 2 < $srcLen) {
$c1 = static::decode6Bits($chunk[2]);
$c2 = static::decode6Bits($chunk[3]);
$dest .= pack('CC', ((($c0 << 2) | ($c1 >> 4)) & 0xff), ((($c1 << 4) | ($c2 >> 2)) & 0xff));
$err |= ($c0 | $c1 | $c2) >> 8;
if ($strictPadding) {
$err |= ($c2 << 6) & 0xff;
}
} elseif ($i + 1 < $srcLen) {
$c1 = static::decode6Bits($chunk[2]);
$dest .= pack('C', ((($c0 << 2) | ($c1 >> 4)) & 0xff));
$err |= ($c0 | $c1) >> 8;
if ($strictPadding) {
$err |= ($c1 << 4) & 0xff;
}
} elseif ($strictPadding) {
$err |= 1;
}
}
$check = ($err === 0);
if (! $check) {
throw new RangeException('Base64::decode() only expects characters in the correct base64 alphabet');
}
return $dest;
}

public static function decodeNoPadding(string $encodedString): string
{
$srcLen = self::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);
}

private static function doEncode(string $src, bool $pad = true): string
{
$dest = '';
$srcLen = self::safeStrlen($src);
for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', self::safeSubstr($src, $i, 3));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];

$dest .=
static::encode6Bits($b0 >> 2) .
static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
static::encode6Bits($b2 & 63);
}

if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = unpack('C*', self::safeSubstr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
static::encode6Bits($b0 >> 2) .
static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
static::encode6Bits(($b1 << 2) & 63);
if ($pad) {
$dest .= '=';
}
} else {
$dest .=
static::encode6Bits($b0 >> 2) .
static::encode6Bits(($b0 << 4) & 63);
if ($pad) {
$dest .= '==';
}
}
}
return $dest;
}

private static function decode6Bits(int $src): int
{
$ret = -1;
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
$ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63;

return $ret + ((((0x5e - $src) & ($src - 0x60)) >> 8) & 64);
}

private static function encode6Bits(int $src): string
{
$diff = 0x41;
$diff += ((25 - $src) >> 8) & 6;
$diff -= ((51 - $src) >> 8) & 75;
$diff -= ((61 - $src) >> 8) & 13;
$diff += ((62 - $src) >> 8) & 49;

return pack('C', $src + $diff);
}

private static function safeStrlen(string $str): int
{
return mb_strlen($str, '8bit');
}

private static function safeSubstr(string $str, int $start = 0, $length = null): string
{
if ($length === 0) {
return '';
}
return mb_substr($str, $start, $length, '8bit');
}
}
1 change: 0 additions & 1 deletion src/Library/Core/Util/ECKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use InvalidArgumentException;
use Jose\Component\Core\JWK;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use function extension_loaded;
use function is_array;
Expand Down
1 change: 0 additions & 1 deletion src/Library/Core/Util/RSAKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use InvalidArgumentException;
use Jose\Component\Core\JWK;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence;
use SpomkyLabs\Pki\ASN1\Type\Primitive\BitString;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace Jose\Component\Encryption\Algorithm\ContentEncryption;

use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\Encryption\Algorithm\ContentEncryptionAlgorithm;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use function extension_loaded;
use const OPENSSL_RAW_DATA;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace Jose\Component\Encryption\Algorithm\ContentEncryption;

use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\Encryption\Algorithm\ContentEncryptionAlgorithm;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use function extension_loaded;
use const OPENSSL_RAW_DATA;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use AESKW\Wrapper as WrapperInterface;
use InvalidArgumentException;
use Jose\Component\Core\JWK;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Jose\Component\Core\Util\Base64UrlSafe;
use RuntimeException;
use function extension_loaded;
use function in_array;
Expand Down
2 changes: 1 addition & 1 deletion src/Library/Encryption/Algorithm/KeyEncryption/AESKW.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use AESKW\Wrapper as WrapperInterface;
use InvalidArgumentException;
use Jose\Component\Core\JWK;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Jose\Component\Core\Util\Base64UrlSafe;
use RuntimeException;
use function in_array;
use function is_string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
use Brick\Math\BigInteger;
use InvalidArgumentException;
use Jose\Component\Core\JWK;
use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\Core\Util\Ecc\Curve;
use Jose\Component\Core\Util\Ecc\EcDH;
use Jose\Component\Core\Util\Ecc\NistCurve;
use Jose\Component\Core\Util\Ecc\PrivateKey;
use Jose\Component\Core\Util\ECKey;
use Jose\Component\Encryption\Algorithm\KeyEncryption\Util\ConcatKDF;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use Throwable;
use function array_key_exists;
Expand Down
2 changes: 1 addition & 1 deletion src/Library/Encryption/Algorithm/KeyEncryption/Dir.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use InvalidArgumentException;
use Jose\Component\Core\JWK;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Jose\Component\Core\Util\Base64UrlSafe;
use function in_array;
use function is_string;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use AESKW\Wrapper as WrapperInterface;
use InvalidArgumentException;
use Jose\Component\Core\JWK;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Jose\Component\Core\Util\Base64UrlSafe;
use RuntimeException;
use function in_array;
use function is_int;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Jose\Component\Encryption\Algorithm\KeyEncryption\Util;

use InvalidArgumentException;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Jose\Component\Core\Util\Base64UrlSafe;
use const STR_PAD_LEFT;

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Library/Encryption/JWEBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use InvalidArgumentException;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Core\JWK;
use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\Core\Util\JsonConverter;
use Jose\Component\Core\Util\KeyChecker;
use Jose\Component\Encryption\Algorithm\ContentEncryptionAlgorithm;
Expand All @@ -19,7 +20,6 @@
use Jose\Component\Encryption\Compression\CompressionMethod;
use Jose\Component\Encryption\Compression\CompressionMethodManager;
use LogicException;
use ParagonIE\ConstantTime\Base64UrlSafe;
use RuntimeException;
use function array_key_exists;
use function count;
Expand Down
2 changes: 1 addition & 1 deletion src/Library/Encryption/Serializer/CompactSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
namespace Jose\Component\Encryption\Serializer;

use InvalidArgumentException;
use Jose\Component\Core\Util\Base64UrlSafe;
use Jose\Component\Core\Util\JsonConverter;
use Jose\Component\Encryption\JWE;
use Jose\Component\Encryption\Recipient;
use LogicException;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Throwable;
use function count;
use function is_array;
Expand Down
Loading

0 comments on commit 1fe7ca8

Please sign in to comment.