Skip to content

Commit

Permalink
:octocat: use chillerlan/php-standard-utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
codemasher committed Nov 12, 2024
1 parent f8c4dbe commit 7333842
Show file tree
Hide file tree
Showing 12 changed files with 52 additions and 207 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "https://getcomposer.org/schema.json",
"name": "chillerlan/php-oauth",
"description": "A fully transparent, framework agnostic PSR-18 OAuth client.",
"homepage": "https://github.com/chillerlan/php-oauth",
Expand Down Expand Up @@ -36,6 +37,7 @@
"ext-sodium": "*",
"chillerlan/php-http-message-utils": "^2.2.2",
"chillerlan/php-settings-container": "^3.2.1",
"chillerlan/php-standard-utilities": "^1.0",
"psr/http-client": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"psr/log": "^1.1 || ^2.0 || ^3.0"
Expand Down
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ parameters:
count: 1
path: src/Core/OAuthProvider.php

-
message: "#^Parameter \\#1 \\$content of method Psr\\\\Http\\\\Message\\\\StreamFactoryInterface\\:\\:createStream\\(\\) expects string, string\\|false given\\.$#"
count: 1
path: src/Core/OAuthProvider.php

-
message: "#^Parameter \\#1 \\$length of function random_bytes expects int\\<1, max\\>, int given\\.$#"
count: 1
Expand Down
1 change: 0 additions & 1 deletion phpstan.dist.neon
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ parameters:
# see https://github.com/phpstan/phpstan-src/blob/1.8.x/build/ignore-by-php-version.neon.php
includes:
- phpstan-baseline.neon
- .phpstan/ignore-by-php-version.php
- vendor/phpstan/phpstan/conf/bleedingEdge.neon
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
- vendor/chillerlan/php-settings-container/rules-magic-access.neon
6 changes: 3 additions & 3 deletions src/Core/OAuth1Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

use chillerlan\HTTP\Utils\{MessageUtil, QueryUtil};
use chillerlan\OAuth\Providers\ProviderException;
use chillerlan\Utilities\Str;
use Psr\Http\Message\{RequestInterface, ResponseInterface, UriInterface};
use function array_merge, hash_hmac, implode, in_array, sodium_bin2base64, sprintf, strtoupper, time;
use const SODIUM_BASE64_VARIANT_ORIGINAL;
use function array_merge, hash_hmac, implode, in_array, sprintf, strtoupper, time;

/**
* Implements an abstract OAuth1 (1.0a) provider with all methods required by the OAuth1Interface.
Expand Down Expand Up @@ -186,7 +186,7 @@ protected function getSignature(

$hash = hash_hmac('sha1', implode('&', $data), implode('&', $key), true);

return sodium_bin2base64($hash, SODIUM_BASE64_VARIANT_ORIGINAL);
return Str::base64encode($hash);
}

/**
Expand Down
14 changes: 7 additions & 7 deletions src/Core/OAuthProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@
use chillerlan\OAuth\Providers\ProviderException;
use chillerlan\OAuth\Storage\{MemoryStorage, OAuthStorageInterface};
use chillerlan\Settings\SettingsContainerInterface;
use chillerlan\Utilities\Str;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\{
RequestFactoryInterface, RequestInterface, ResponseInterface,
StreamFactoryInterface, StreamInterface, UriFactoryInterface
};
use Psr\Log\{LoggerInterface, NullLogger};
use ReflectionClass, UnhandledMatchError;
use function array_merge, array_shift, explode, implode, in_array, is_array, is_string,
json_encode, ltrim, random_bytes, rtrim, sodium_bin2hex, sodium_bin2base64,
sprintf, str_contains, str_starts_with, strip_tags, strtolower;
use const PHP_QUERY_RFC1738, SODIUM_BASE64_VARIANT_ORIGINAL;
use ReflectionClass;
use function array_merge, array_shift, explode, implode, in_array, is_array, is_string, ltrim,
random_bytes, rtrim, sodium_bin2hex, sprintf, str_contains, str_starts_with, strip_tags, strtolower;
use const PHP_QUERY_RFC1738;

/**
* Implements an abstract OAuth provider with all methods required by the OAuthInterface.
Expand Down Expand Up @@ -271,7 +271,7 @@ protected function cleanBodyParams(iterable $params):array{
* Adds an "Authorization: Basic <base64(key:secret)>" header to the given request
*/
protected function addBasicAuthHeader(RequestInterface $request):RequestInterface{
$auth = sodium_bin2base64(sprintf('%s:%s', $this->options->key, $this->options->secret), SODIUM_BASE64_VARIANT_ORIGINAL);
$auth = Str::base64encode(sprintf('%s:%s', $this->options->key, $this->options->secret));

return $request->withHeader('Authorization', sprintf('Basic %s', $auth));
}
Expand Down Expand Up @@ -363,7 +363,7 @@ final protected function setRequestBody(StreamInterface|array|string $body, Requ

$body = match($contentType){
'application/x-www-form-urlencoded' => QueryUtil::build($body, PHP_QUERY_RFC1738),
'application/json', 'application/vnd.api+json' => json_encode($body),
'application/json', 'application/vnd.api+json' => Str::jsonEncode($body, 0),
default => throw new ProviderException(
sprintf('invalid content-type "%s" for the given array body', $contentType),
),
Expand Down
29 changes: 4 additions & 25 deletions src/Core/PKCETrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
namespace chillerlan\OAuth\Core;

use chillerlan\OAuth\Providers\ProviderException;
use function hash;
use function random_int;
use function sodium_bin2base64;
use const PHP_VERSION_ID;
use chillerlan\Utilities\{Crypto, Str};
use const SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING;

/**
Expand Down Expand Up @@ -78,27 +75,9 @@ final public function setCodeVerifier(array $params):array{
*
* @see \chillerlan\OAuth\Core\PKCE::generateVerifier()
* @see \chillerlan\OAuth\Core\OAuth2Provider::setCodeChallenge()
*
* @noinspection PhpFullyQualifiedNameUsageInspection
* @SuppressWarnings(PHPMD.MissingImport)
*/
final public function generateVerifier(int $length):string{

// use the Randomizer if available
// https://github.com/phpstan/phpstan/issues/7843
if(PHP_VERSION_ID >= 80300){
$randomizer = new \Random\Randomizer(new \Random\Engine\Secure);

return $randomizer->getBytesFromString(PKCE::VERIFIER_CHARSET, $length);
}

$str = '';

for($i = 0; $i < $length; $i++){
$str .= PKCE::VERIFIER_CHARSET[random_int(0, 65)];
}

return $str;
return Crypto::randomString($length, PKCE::VERIFIER_CHARSET);
}

/**
Expand All @@ -114,12 +93,12 @@ final public function generateChallenge(string $verifier, string $challengeMetho
}

$verifier = match($challengeMethod){
PKCE::CHALLENGE_METHOD_S256 => hash('sha256', $verifier, true),
PKCE::CHALLENGE_METHOD_S256 => Crypto::sha256($verifier, true),
// no other hash methods yet
default => throw new ProviderException('invalid PKCE challenge method'), // @codeCoverageIgnore
};

return sodium_bin2base64($verifier, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING);
return Str::base64encode($verifier, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING);
}

}
90 changes: 2 additions & 88 deletions src/Core/Utilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,49 +13,29 @@

namespace chillerlan\OAuth\Core;

use chillerlan\Utilities\File;
use DirectoryIterator;
use InvalidArgumentException;
use ReflectionClass;
use RuntimeException;
use function hash;
use function random_bytes;
use function realpath;
use function sodium_base642bin;
use function sodium_bin2base64;
use function sodium_bin2hex;
use function sodium_crypto_secretbox;
use function sodium_crypto_secretbox_keygen;
use function sodium_crypto_secretbox_open;
use function sodium_hex2bin;
use function sodium_memzero;
use function substr;
use function trim;
use const SODIUM_BASE64_VARIANT_ORIGINAL;
use const SODIUM_CRYPTO_SECRETBOX_NONCEBYTES;

/**
* Common utilities for use with the OAuth providers
*/
class Utilities{

final public const ENCRYPT_FORMAT_BINARY = 0b00;
final public const ENCRYPT_FORMAT_BASE64 = 0b01;
final public const ENCRYPT_FORMAT_HEX = 0b10;

/**
* Fetches a list of provider classes in the given directory
*
* @return array<string, array<string, string>>
*/
public static function getProviders(string|null $providerDir = null, string|null $namespace = null):array{
$providerDir = realpath(($providerDir ?? __DIR__.'/../Providers'));
$providerDir = File::realpath(($providerDir ?? __DIR__.'/../Providers'));
$namespace = trim(($namespace ?? 'chillerlan\\OAuth\\Providers'), '\\');
$providers = [];

if($providerDir === false){
throw new InvalidArgumentException('invalid $providerDir');
}

foreach(new DirectoryIterator($providerDir) as $e){

if($e->getExtension() !== 'php'){
Expand All @@ -79,70 +59,4 @@ public static function getProviders(string|null $providerDir = null, string|null
return $providers;
}

/**
* Creates a new cryptographically secure random encryption key (in hexadecimal format)
*/
public static function createEncryptionKey():string{
return sodium_bin2hex(sodium_crypto_secretbox_keygen());
}

/**
* encrypts the given $data with $key, $format output [binary, base64, hex]
*
* @see \sodium_crypto_secretbox()
* @see \sodium_bin2base64()
* @see \sodium_bin2hex()
*/
public static function encrypt(string $data, string $keyHex, int $format = self::ENCRYPT_FORMAT_HEX):string{
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$box = sodium_crypto_secretbox($data, $nonce, sodium_hex2bin($keyHex));

$out = match($format){
self::ENCRYPT_FORMAT_BINARY => $nonce.$box,
self::ENCRYPT_FORMAT_BASE64 => sodium_bin2base64($nonce.$box, SODIUM_BASE64_VARIANT_ORIGINAL),
self::ENCRYPT_FORMAT_HEX => sodium_bin2hex($nonce.$box),
default => throw new InvalidArgumentException('invalid format'), // @codeCoverageIgnore
};

sodium_memzero($data);
sodium_memzero($keyHex);
sodium_memzero($nonce);
sodium_memzero($box);

return $out;
}

/**
* decrypts the given $encrypted data with $key from $format input [binary, base64, hex]
*
* @see \sodium_crypto_secretbox_open()
* @see \sodium_base642bin()
* @see \sodium_hex2bin()
*/
public static function decrypt(string $encrypted, string $keyHex, int $format = self::ENCRYPT_FORMAT_HEX):string{

$bin = match($format){
self::ENCRYPT_FORMAT_BINARY => $encrypted,
self::ENCRYPT_FORMAT_BASE64 => sodium_base642bin($encrypted, SODIUM_BASE64_VARIANT_ORIGINAL),
self::ENCRYPT_FORMAT_HEX => sodium_hex2bin($encrypted),
default => throw new InvalidArgumentException('invalid format'), // @codeCoverageIgnore
};

$nonce = substr($bin, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$box = substr($bin, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$data = sodium_crypto_secretbox_open($box, $nonce, sodium_hex2bin($keyHex));

sodium_memzero($encrypted);
sodium_memzero($keyHex);
sodium_memzero($bin);
sodium_memzero($nonce);
sodium_memzero($box);

if($data === false){
throw new RuntimeException('decryption failed'); // @codeCoverageIgnore
}

return $data;
}

}
7 changes: 4 additions & 3 deletions src/OAuthOptionsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
namespace chillerlan\OAuth;

use chillerlan\OAuth\Storage\OAuthStorageException;
use function is_dir, is_writable, max, min, preg_match, realpath, sprintf, trim;
use chillerlan\Utilities\{Directory, File};
use function max, min, preg_match, sprintf, trim;

/**
* The settings for the OAuth provider
Expand Down Expand Up @@ -126,9 +127,9 @@ protected function set_storageEncryptionKey(string $storageEncryptionKey):void{
* sets and verifies the file storage path
*/
protected function set_fileStoragePath(string $fileStoragePath):void{
$path = realpath(trim($fileStoragePath));
$path = File::realpath(trim($fileStoragePath));

if($path === false || !is_dir($path) || !is_writable($path)){
if(!Directory::isWritable($path) || !Directory::isReadable($path)){
throw new OAuthStorageException(sprintf('invalid storage path "%s"', $fileStoragePath));
}

Expand Down
Loading

0 comments on commit 7333842

Please sign in to comment.