Skip to content

Commit

Permalink
Safe and Assertion removed (#164)
Browse files Browse the repository at this point in the history
Safe and Assertion removed
  • Loading branch information
Spomky committed Oct 11, 2022
1 parent 5a25140 commit 8cf16dc
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 56 deletions.
4 changes: 2 additions & 2 deletions Makefile
Expand Up @@ -12,7 +12,7 @@ ci-rector: vendor ## Check all files using Rector (CI/CD)
vendor/bin/rector process --ansi --dry-run

ci-mu: vendor ## Mutation tests (CI/CD)
vendor/bin/infection --logger-github --git-diff-filter=AM -s --threads=$(nproc) --min-msi=70 --min-covered-msi=50 --test-framework-options="--exclude-group=Performance"
vendor/bin/infection --logger-github -s --threads=$$(nproc) --min-msi=70 --min-covered-msi=50 --test-framework-options="--exclude-group=Performance"

########################
# Everyday #
Expand Down Expand Up @@ -44,7 +44,7 @@ rector: vendor ## Check all files using Rector
########################

mu: vendor ## Mutation tests
vendor/bin/infection -s --threads=$(nproc) --min-msi=70 --min-covered-msi=50 --test-framework-options="--exclude-group=Performance"
vendor/bin/infection -s --threads=$$(nproc) --min-msi=70 --min-covered-msi=50 --test-framework-options="--exclude-group=Performance"

cc: vendor ## Show test coverage rates (HTML)
vendor/bin/phpunit --coverage-html ./build
Expand Down
10 changes: 3 additions & 7 deletions composer.json
Expand Up @@ -18,23 +18,19 @@
"require": {
"php": "^8.1",
"ext-mbstring": "*",
"paragonie/constant_time_encoding": "^2.0",
"beberlei/assert": "^3.0",
"thecodingmachine/safe": "^1.0|^2.0"
"paragonie/constant_time_encoding": "^2.0"
},
"require-dev": {
"ekino/phpstan-banned-code": "^1.0",
"infection/infection": "^0.26",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-beberlei-assert": "^1.0",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
"rector/rector": "^0.13",
"rector/rector": "^0.14",
"symfony/phpunit-bridge": "^6.0",
"symplify/easy-coding-standard": "^11.0",
"thecodingmachine/phpstan-safe-rule": "^1.0"
"symplify/easy-coding-standard": "^11.0"
},
"autoload": {
"psr-4": { "OTPHP\\": "src/" }
Expand Down
2 changes: 0 additions & 2 deletions phpstan.neon
Expand Up @@ -10,7 +10,5 @@ includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
- vendor/thecodingmachine/phpstan-safe-rule/phpstan-safe-rule.neon
- vendor/phpstan/phpstan-beberlei-assert/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/ekino/phpstan-banned-code/extension.neon
7 changes: 4 additions & 3 deletions src/Factory.php
Expand Up @@ -4,7 +4,6 @@

namespace OTPHP;

use Assert\Assertion;
use function count;
use InvalidArgumentException;
use Throwable;
Expand All @@ -20,7 +19,7 @@ public static function loadFromProvisioningUri(string $uri): OTPInterface
{
try {
$parsed_url = Url::fromString($uri);
Assertion::eq('otpauth', $parsed_url->getScheme());
$parsed_url->getScheme() === 'otpauth' || throw new InvalidArgumentException('Invalid scheme.');
} catch (Throwable $throwable) {
throw new InvalidArgumentException('Not a valid OTP provisioning URI', $throwable->getCode(), $throwable);
}
Expand Down Expand Up @@ -51,7 +50,9 @@ private static function populateOTP(OTPInterface $otp, Url $data): void
}

if ($otp->getIssuer() !== null) {
Assertion::eq($result[0], $otp->getIssuer(), 'Invalid OTP: invalid issuer in parameter');
$result[0] === $otp->getIssuer() || throw new InvalidArgumentException(
'Invalid OTP: invalid issuer in parameter'
);
$otp->setIssuerIncludedAsParameter(true);
}
$otp->setIssuer($result[0]);
Expand Down
9 changes: 5 additions & 4 deletions src/HOTP.php
Expand Up @@ -4,7 +4,8 @@

namespace OTPHP;

use Assert\Assertion;
use InvalidArgumentException;
use function is_int;

/**
* @see \OTPHP\Test\HOTPTest
Expand All @@ -29,7 +30,7 @@ public static function create(
public function getCounter(): int
{
$value = $this->getParameter('counter');
Assertion::integer($value, 'Invalid "counter" parameter.');
is_int($value) || throw new InvalidArgumentException('Invalid "counter" parameter.');

return $value;
}
Expand All @@ -46,7 +47,7 @@ public function getProvisioningUri(): string
*/
public function verify(string $otp, null|int $counter = null, null|int $window = null): bool
{
Assertion::greaterOrEqualThan($counter, 0, 'The counter must be at least 0.');
$counter >= 0 || throw new InvalidArgumentException('The counter must be at least 0.');

if ($counter === null) {
$counter = $this->getCounter();
Expand All @@ -69,7 +70,7 @@ protected function getParameterMap(): array
{
return [...parent::getParameterMap(), ...[
'counter' => static function ($value): int {
Assertion::greaterOrEqualThan((int) $value, 0, 'Counter must be at least 0.');
(int) $value >= 0 || throw new InvalidArgumentException('Counter must be at least 0.');

return (int) $value;
},
Expand Down
15 changes: 8 additions & 7 deletions src/OTP.php
Expand Up @@ -4,13 +4,13 @@

namespace OTPHP;

use Assert\Assertion;
use function chr;
use function count;
use Exception;
use InvalidArgumentException;
use function is_string;
use ParagonIE\ConstantTime\Base32;
use RuntimeException;
use function Safe\unpack;
use const STR_PAD_LEFT;

abstract class OTP implements OTPInterface
Expand Down Expand Up @@ -42,11 +42,12 @@ public function at(int $input): string
protected function generateOTP(int $input): string
{
$hash = hash_hmac($this->getDigest(), $this->intToByteString($input), $this->getDecodedSecret(), true);

$hmac = array_values(unpack('C*', $hash));
$unpacked = unpack('C*', $hash);
$unpacked !== false || throw new InvalidArgumentException('Invalid data.');
$hmac = array_values($unpacked);

$offset = ($hmac[count($hmac) - 1] & 0xF);
$code = ($hmac[$offset + 0] & 0x7F) << 24 | ($hmac[$offset + 1] & 0xFF) << 16 | ($hmac[$offset + 2] & 0xFF) << 8 | ($hmac[$offset + 3] & 0xFF);
$code = ($hmac[$offset] & 0x7F) << 24 | ($hmac[$offset + 1] & 0xFF) << 16 | ($hmac[$offset + 2] & 0xFF) << 8 | ($hmac[$offset + 3] & 0xFF);
$otp = $code % (10 ** $this->getDigits());

return str_pad((string) $otp, $this->getDigits(), '0', STR_PAD_LEFT);
Expand Down Expand Up @@ -76,8 +77,8 @@ protected function filterOptions(array &$options): void
protected function generateURI(string $type, array $options): string
{
$label = $this->getLabel();
Assertion::string($label, 'The label is not set.');
Assertion::false($this->hasColon($label), 'Label must not contain a colon.');
is_string($label) || throw new InvalidArgumentException('The label is not set.');
$this->hasColon($label) === false || throw new InvalidArgumentException('Label must not contain a colon.');
$options = [...$options, ...$this->getParameters()];
$this->filterOptions($options);
$params = str_replace(['+', '%7E'], ['%20', '~'], http_build_query($options));
Expand Down
25 changes: 17 additions & 8 deletions src/ParameterTrait.php
Expand Up @@ -5,8 +5,10 @@
namespace OTPHP;

use function array_key_exists;
use Assert\Assertion;
use function in_array;
use InvalidArgumentException;
use function is_int;
use function is_string;
use ParagonIE\ConstantTime\Base32;

trait ParameterTrait
Expand Down Expand Up @@ -39,7 +41,7 @@ public function getParameters(): array
public function getSecret(): string
{
$value = $this->getParameter('secret');
Assertion::string($value, 'Invalid "secret" parameter.');
is_string($value) || throw new InvalidArgumentException('Invalid "secret" parameter.');

return $value;
}
Expand Down Expand Up @@ -77,15 +79,15 @@ public function setIssuerIncludedAsParameter(bool $issuer_included_as_parameter)
public function getDigits(): int
{
$value = $this->getParameter('digits');
Assertion::integer($value, 'Invalid "digits" parameter.');
is_int($value) || throw new InvalidArgumentException('Invalid "digits" parameter.');

return $value;
}

public function getDigest(): string
{
$value = $this->getParameter('algorithm');
Assertion::string($value, 'Invalid "algorithm" parameter.');
is_string($value) || throw new InvalidArgumentException('Invalid "algorithm" parameter.');

return $value;
}
Expand Down Expand Up @@ -127,7 +129,9 @@ protected function getParameterMap(): array
{
return [
'label' => function ($value) {
Assertion::false($this->hasColon($value), 'Label must not contain a colon.');
$this->hasColon($value) === false || throw new InvalidArgumentException(
'Label must not contain a colon.'
);

return $value;
},
Expand All @@ -140,17 +144,22 @@ protected function getParameterMap(): array
},
'algorithm' => static function ($value): string {
$value = mb_strtolower($value);
Assertion::inArray($value, hash_algos(), sprintf('The "%s" digest is not supported.', $value));
in_array($value, hash_algos(), true) || throw new InvalidArgumentException(sprintf(
'The "%s" digest is not supported.',
$value
));

return $value;
},
'digits' => static function ($value): int {
Assertion::greaterThan($value, 0, 'Digits must be at least 1.');
$value > 0 || throw new InvalidArgumentException('Digits must be at least 1.');

return (int) $value;
},
'issuer' => function ($value) {
Assertion::false($this->hasColon($value), 'Issuer must not contain a colon.');
$this->hasColon($value) === false || throw new InvalidArgumentException(
'Issuer must not contain a colon.'
);

return $value;
},
Expand Down
19 changes: 12 additions & 7 deletions src/TOTP.php
Expand Up @@ -4,7 +4,8 @@

namespace OTPHP;

use Assert\Assertion;
use InvalidArgumentException;
use function is_int;

/**
* @see \OTPHP\Test\TOTPTest
Expand All @@ -31,15 +32,15 @@ public static function create(
public function getPeriod(): int
{
$value = $this->getParameter('period');
Assertion::integer($value, 'Invalid "period" parameter.');
is_int($value) || throw new InvalidArgumentException('Invalid "period" parameter.');

return $value;
}

public function getEpoch(): int
{
$value = $this->getParameter('epoch');
Assertion::integer($value, 'Invalid "epoch" parameter.');
is_int($value) || throw new InvalidArgumentException('Invalid "epoch" parameter.');

return $value;
}
Expand Down Expand Up @@ -68,14 +69,16 @@ public function now(): string
public function verify(string $otp, null|int $timestamp = null, null|int $leeway = null): bool
{
$timestamp ??= time();
Assertion::greaterOrEqualThan($timestamp, 0, 'Timestamp must be at least 0.');
$timestamp >= 0 || throw new InvalidArgumentException('Timestamp must be at least 0.');

if ($leeway === null) {
return $this->compareOTP($this->at($timestamp), $otp);
}

$leeway = abs($leeway);
Assertion::lessThan($leeway, $this->getPeriod(), 'The leeway must be lower than the TOTP period');
$leeway < $this->getPeriod() || throw new InvalidArgumentException(
'The leeway must be lower than the TOTP period'
);

return $this->compareOTP($this->at($timestamp - $leeway), $otp)
|| $this->compareOTP($this->at($timestamp), $otp)
Expand Down Expand Up @@ -110,12 +113,14 @@ protected function getParameterMap(): array
parent::getParameterMap(),
[
'period' => static function ($value): int {
Assertion::greaterThan((int) $value, 0, 'Period must be at least 1.');
(int) $value > 0 || throw new InvalidArgumentException('Period must be at least 1.');

return (int) $value;
},
'epoch' => static function ($value): int {
Assertion::greaterOrEqualThan((int) $value, 0, 'Epoch must be greater than or equal to 0.');
(int) $value >= 0 || throw new InvalidArgumentException(
'Epoch must be greater than or equal to 0.'
);

return (int) $value;
},
Expand Down
32 changes: 20 additions & 12 deletions src/Url.php
Expand Up @@ -4,8 +4,9 @@

namespace OTPHP;

use Assert\Assertion;
use function Safe\parse_url;
use function array_key_exists;
use InvalidArgumentException;
use function is_string;

/**
* @internal
Expand Down Expand Up @@ -53,19 +54,26 @@ public function getQuery(): array
public static function fromString(string $uri): self
{
$parsed_url = parse_url($uri);
Assertion::isArray($parsed_url, 'Not a valid OTP provisioning URI');
$parsed_url !== false || throw new InvalidArgumentException('Invalid URI.');
foreach (['scheme', 'host', 'path', 'query'] as $key) {
Assertion::keyExists($parsed_url, $key, 'Not a valid OTP provisioning URI');
Assertion::string($parsed_url[$key], 'Not a valid OTP provisioning URI');
array_key_exists($key, $parsed_url) || throw new InvalidArgumentException(
'Not a valid OTP provisioning URI'
);
is_string($parsed_url[$key]) || throw new InvalidArgumentException('Not a valid OTP provisioning URI');
}
$scheme = $parsed_url['scheme'];
Assertion::eq('otpauth', $scheme, 'Not a valid OTP provisioning URI');
$host = $parsed_url['host'];
$path = $parsed_url['path'];
$query = $parsed_url['query'];
$scheme = $parsed_url['scheme'] ?? null;
$host = $parsed_url['host'] ?? null;
$path = $parsed_url['path'] ?? null;
$query = $parsed_url['query'] ?? null;
$scheme === 'otpauth' || throw new InvalidArgumentException('Not a valid OTP provisioning URI');
is_string($host) || throw new InvalidArgumentException('Invalid URI.');
is_string($path) || throw new InvalidArgumentException('Invalid URI.');
is_string($query) || throw new InvalidArgumentException('Invalid URI.');
$parsedQuery = [];
parse_str((string) $query, $parsedQuery);
Assertion::keyExists($parsedQuery, 'secret', 'Not a valid OTP provisioning URI');
parse_str($query, $parsedQuery);
array_key_exists('secret', $parsedQuery) || throw new InvalidArgumentException(
'Not a valid OTP provisioning URI'
);
$secret = $parsedQuery['secret'];
unset($parsedQuery['secret']);

Expand Down
2 changes: 0 additions & 2 deletions tests/HOTPTest.php
Expand Up @@ -4,7 +4,6 @@

namespace OTPHP\Test;

use Assert\Assertion;
use InvalidArgumentException;
use OTPHP\HOTP;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -188,7 +187,6 @@ private function createHOTP(
$otp = HOTP::create($secret, $counter, $digest, $digits);
$otp->setLabel($label);
$otp->setIssuer($issuer);
Assertion::isInstanceOf($otp, HOTP::class);

return $otp;
}
Expand Down
2 changes: 0 additions & 2 deletions tests/TOTPTest.php
Expand Up @@ -4,7 +4,6 @@

namespace OTPHP\Test;

use Assert\Assertion;
use InvalidArgumentException;
use OTPHP\TOTP;
use OTPHP\TOTPInterface;
Expand Down Expand Up @@ -406,7 +405,6 @@ private function createTOTP(
$otp = TOTP::create($secret, $period, $digest, $digits, $epoch);
$otp->setLabel($label);
$otp->setIssuer($issuer);
Assertion::isInstanceOf($otp, TOTP::class);

return $otp;
}
Expand Down

0 comments on commit 8cf16dc

Please sign in to comment.