diff --git a/.github/workflows/test-endroid.yml b/.github/workflows/test-endroid.yml index 79958cb..23b3867 100644 --- a/.github/workflows/test-endroid.yml +++ b/.github/workflows/test-endroid.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: php-version: ['8.1', '8.2'] - endroid-version: ["^4"] + endroid-version: ["^3","^4","^5"] steps: - uses: actions/checkout@v3 @@ -25,7 +25,7 @@ jobs: - uses: ramsey/composer-install@v2 - - run: composer require endroid/qrcode:${{ matrix.endroid-version }} + - run: composer require endroid/qrcode:${{ matrix.endroid-version }} -W - run: composer lint-ci - run: composer test testsDependency/EndroidQRCodeTest.php diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 32d4c5b..86d3e30 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -39,7 +39,6 @@ 'semicolon_after_instruction' => true, 'short_scalar_cast' => true, 'simplified_null_return' => true, - 'single_blank_line_before_namespace' => true, 'single_class_element_per_statement' => true, 'single_line_comment_style' => true, 'single_quote' => true, diff --git a/README.md b/README.md index 23800ce..96f566d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ![Logo](https://raw.githubusercontent.com/RobThree/TwoFactorAuth/master/logo.png) PHP library for Two Factor Authentication -[![Build status](https://img.shields.io/github/workflow/status/RobThree/TwoFactorAuth/Test/master?style=flat-square)](https://github.com/RobThree/TwoFactorAuth/actions?query=branch%3Amaster) [![Latest Stable Version](https://img.shields.io/packagist/v/robthree/twofactorauth.svg?style=flat-square)](https://packagist.org/packages/robthree/twofactorauth) [![License](https://img.shields.io/packagist/l/robthree/twofactorauth.svg?style=flat-square)](LICENSE) [![Downloads](https://img.shields.io/packagist/dt/robthree/twofactorauth.svg?style=flat-square)](https://packagist.org/packages/robthree/twofactorauth) [![Code Climate](https://img.shields.io/codeclimate/github/RobThree/TwoFactorAuth.svg?style=flat-square)](https://codeclimate.com/github/RobThree/TwoFactorAuth) [![PayPal donate button](http://img.shields.io/badge/paypal-donate-orange.svg?style=flat-square)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6MB5M2SQLP636 "Keep me off the streets") +[![Build status](https://img.shields.io/github/actions/workflow/status/robthree/twofactorauth/test.yml?branch=master)](https://github.com/RobThree/TwoFactorAuth/actions?query=branch%3Amaster) [![Latest Stable Version](https://img.shields.io/packagist/v/robthree/twofactorauth.svg?style=flat-square)](https://packagist.org/packages/robthree/twofactorauth) [![License](https://img.shields.io/packagist/l/robthree/twofactorauth.svg?style=flat-square)](LICENSE) [![Downloads](https://img.shields.io/packagist/dt/robthree/twofactorauth.svg?style=flat-square)](https://packagist.org/packages/robthree/twofactorauth) [![PayPal donate button](http://img.shields.io/badge/paypal-donate-orange.svg?style=flat-square)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6MB5M2SQLP636 "Keep me off the streets") PHP library for [two-factor (or multi-factor) authentication](http://en.wikipedia.org/wiki/Multi-factor_authentication) using [TOTP](http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm) and [QR-codes](http://en.wikipedia.org/wiki/QR_code). Inspired by, based on but most importantly an *improvement* on '[PHPGangsta/GoogleAuthenticator](https://github.com/PHPGangsta/GoogleAuthenticator)'. There's a [.Net implementation](https://github.com/RobThree/TwoFactorAuth.Net) of this library as well. @@ -8,6 +8,13 @@ PHP library for [two-factor (or multi-factor) authentication](http://en.wikipedi

+## Warning: + +By default, this package uses the `lib/Providers/QRServerProvider.php` as QR code generator. This provider is __not__ suggested for applications where absolute security is needed, because it uses an external service for the QR code generation. + + +You can make use of the included [Endroid](https://robthree.github.io/TwoFactorAuth/qr-codes/endroid.html) or [Bacon](https://robthree.github.io/TwoFactorAuth/qr-codes/bacon.html) providers which generate locally. + ## Requirements * Requires PHP version >=8.1 diff --git a/docs/optional-configuration.md b/docs/optional-configuration.md index 346d6cd..d3e2556 100644 --- a/docs/optional-configuration.md +++ b/docs/optional-configuration.md @@ -7,15 +7,15 @@ title: Optional Configuration The instance (`new TwoFactorAuth()`) can only be configured by the constructor with the following optional arguments -Argument | Default value | Use -------------------|---------------|----- -`$issuer` | `null` | Will be displayed in the users app as the default issuer name when using QR code to import the secret -`$digits` | `6` | The number of digits the resulting codes will be -`$period` | `30` | The number of seconds a code will be valid -`$algorithm` | `'sha1'` | The algorithm used (one of `sha1`, `sha256`, `sha512`, `md5`) -`$qrcodeprovider` | `null` | QR-code provider -`$rngprovider` | `null` | Random Number Generator provider -`$timeprovider` | `null` | Time provider +Argument | Default value | Use +------------------|-------------------|----- +`$issuer` | `null` | Will be displayed in the users app as the default issuer name when using QR code to import the secret +`$digits` | `6` | The number of digits the resulting codes will be +`$period` | `30` | The number of seconds a code will be valid +`$algorithm` | `Algorithm::Sha1` | The algorithm used (one of `Algorithm::Sha1`, `Algorithm::Sha256`, `Algorithm::Sha512`, `Algorithm::Md5`) +`$qrcodeprovider` | `null` | QR-code provider +`$rngprovider` | `null` | Random Number Generator provider +`$timeprovider` | `null` | Time provider **Note:** the default values for `$digits`, `$period`, and `$algorithm` provide the widest variety of support amongst common authenticator apps such as Google Authenticator. If you choose to use different values for these arguments you will likely have to instruct your users to use a specific app which supports your chosen configuration. diff --git a/docs/qr-codes.md b/docs/qr-codes.md index 01e8dad..7236198 100644 --- a/docs/qr-codes.md +++ b/docs/qr-codes.md @@ -20,17 +20,19 @@ You can also specify a size as a third argument which is 200 by default. ## Online Providers -[QRServerProvider](qr-codes/qr-server.html) (default) +[QRServerProvider](qr-codes/qr-server.md) (default) -[ImageChartsQRCodeProvider](qr-codes/image-charts.html) +**Warning:** Whilst it is the default, this provider is not suggested for applications where absolute security is needed, because it uses an external service for the QR code generation. You can make use of the included offline providers listed below which generate locally. -[QRicketProvider](qr-codes/qrickit.html) +[ImageChartsQRCodeProvider](qr-codes/image-charts.md) + +[QRicketProvider](qr-codes/qrickit.md) ## Offline Providers -[EndroidQrCodeProvider](qr-codes/endroid.html) and EndroidQrCodeWithLogoProvider +[EndroidQrCodeProvider](qr-codes/endroid.md) and EndroidQrCodeWithLogoProvider -[BaconQRCodeProvider](qr-codes/bacon.html) +[BaconQRCodeProvider](qr-codes/bacon.md) **Note:** offline providers may have additional PHP requirements in order to function, you should study what is required before trying to make use of them. @@ -50,11 +52,8 @@ use RobThree\Auth\TwoFactorAuth; $qrCodeProvider = new YourChosenProvider(); $tfa = new TwoFactorAuth( - null, - 6, - 30, - 'sha1', - $qrCodeProvider + issuer: "Your Company Or App Name", + qrcodeprovider: $qrCodeProvider ); ``` diff --git a/lib/Providers/Qr/BaconQrCodeProvider.php b/lib/Providers/Qr/BaconQrCodeProvider.php index 2660e71..4b53445 100644 --- a/lib/Providers/Qr/BaconQrCodeProvider.php +++ b/lib/Providers/Qr/BaconQrCodeProvider.php @@ -10,7 +10,6 @@ use BaconQrCode\Renderer\Image\ImagickImageBackEnd; use BaconQrCode\Renderer\Image\SvgImageBackEnd; use BaconQrCode\Renderer\ImageRenderer; - use BaconQrCode\Renderer\RendererStyle\EyeFill; use BaconQrCode\Renderer\RendererStyle\Fill; use BaconQrCode\Renderer\RendererStyle\RendererStyle; @@ -23,10 +22,10 @@ class BaconQrCodeProvider implements IQRCodeProvider * Ensure we using the latest Bacon QR Code and specify default options */ public function __construct( - private int $borderWidth = 4, + private readonly int $borderWidth = 4, private string|array $backgroundColour = '#ffffff', private string|array $foregroundColour = '#000000', - private string $format = 'png', + private string $format = 'png', ) { $this->backgroundColour = $this->handleColour($this->backgroundColour); $this->foregroundColour = $this->handleColour($this->foregroundColour); @@ -54,20 +53,15 @@ public function getMimeType(): string public function getQRCodeImage(string $qrText, int $size): string { - switch ($this->format) { - case 'svg': - $backend = new SvgImageBackEnd(); - break; - case 'eps': - $backend = new EpsImageBackEnd(); - break; - default: - $backend = new ImagickImageBackEnd($this->format); - } + $backend = match ($this->format) { + 'svg' => new SvgImageBackEnd(), + 'eps' => new EpsImageBackEnd(), + default => new ImagickImageBackEnd($this->format), + }; $output = $this->getQRCodeByBackend($qrText, $size, $backend); - if ($this->format == 'svg') { + if ($this->format === 'svg') { $svg = explode("\n", $output); return $svg[1]; } @@ -84,7 +78,7 @@ private function getQRCodeByBackend($qrText, $size, ImageBackEndInterface $backe $rendererStyleArgs = array($size, $this->borderWidth); if (is_array($this->foregroundColour) && is_array($this->backgroundColour)) { - $rendererStyleArgs = array_merge($rendererStyleArgs, array( + $rendererStyleArgs = array(...$rendererStyleArgs, ...array( null, null, Fill::withForegroundColor( @@ -112,7 +106,7 @@ private function getQRCodeByBackend($qrText, $size, ImageBackEndInterface $backe private function handleColour(array|string $colour): array|string { if (is_string($colour) && $colour[0] == '#') { - $hexToRGB = function ($input) { + $hexToRGB = static function ($input) { // ensure input no longer has a # for more predictable division // PHP 8.1 does not like implicitly casting a float to an int $input = trim($input, '#'); @@ -126,7 +120,7 @@ private function handleColour(array|string $colour): array|string // cope with three character hex reference if (strlen($input) == 3) { - array_walk($split, function (&$character) { + array_walk($split, static function (&$character) { $character = str_repeat($character, 2); }); } diff --git a/lib/Providers/Qr/EndroidQrCodeProvider.php b/lib/Providers/Qr/EndroidQrCodeProvider.php index 982b4f9..4d42c18 100755 --- a/lib/Providers/Qr/EndroidQrCodeProvider.php +++ b/lib/Providers/Qr/EndroidQrCodeProvider.php @@ -26,9 +26,12 @@ class EndroidQrCodeProvider implements IQRCodeProvider protected $endroid4 = false; + protected $endroid5 = false; + public function __construct($bgcolor = 'ffffff', $color = '000000', $margin = 0, $errorcorrectionlevel = 'H') { $this->endroid4 = method_exists(QrCode::class, 'create'); + $this->endroid5 = enum_exists(ErrorCorrectionLevel::class); $this->bgcolor = $this->handleColor($bgcolor); $this->color = $this->handleColor($color); @@ -41,19 +44,19 @@ public function getMimeType(): string return 'image/png'; } - public function getQRCodeImage(string $qrtext, int $size): string + public function getQRCodeImage(string $qrText, int $size): string { if (!$this->endroid4) { - return $this->qrCodeInstance($qrtext, $size)->writeString(); + return $this->qrCodeInstance($qrText, $size)->writeString(); } $writer = new PngWriter(); - return $writer->write($this->qrCodeInstance($qrtext, $size))->getString(); + return $writer->write($this->qrCodeInstance($qrText, $size))->getString(); } - protected function qrCodeInstance(string $qrtext, int $size): QrCode + protected function qrCodeInstance(string $qrText, int $size): QrCode { - $qrCode = new QrCode($qrtext); + $qrCode = new QrCode($qrText); $qrCode->setSize($size); $qrCode->setErrorCorrectionLevel($this->errorcorrectionlevel); @@ -64,7 +67,7 @@ protected function qrCodeInstance(string $qrtext, int $size): QrCode return $qrCode; } - private function handleColor(string $color): Color + private function handleColor(string $color): Color|array { $split = str_split($color, 2); $r = hexdec($split[0]); @@ -74,18 +77,34 @@ private function handleColor(string $color): Color return $this->endroid4 ? new Color($r, $g, $b, 0) : array('r' => $r, 'g' => $g, 'b' => $b, 'a' => 0); } - private function handleErrorCorrectionLevel(string $level): ErrorCorrectionLevelInterface + private function handleErrorCorrectionLevel(string $level): ErrorCorrectionLevelInterface|ErrorCorrectionLevel { - switch ($level) { - case 'L': - return $this->endroid4 ? new ErrorCorrectionLevelLow() : ErrorCorrectionLevel::LOW(); - case 'M': - return $this->endroid4 ? new ErrorCorrectionLevelMedium() : ErrorCorrectionLevel::MEDIUM(); - case 'Q': - return $this->endroid4 ? new ErrorCorrectionLevelQuartile() : ErrorCorrectionLevel::QUARTILE(); - case 'H': - default: - return $this->endroid4 ? new ErrorCorrectionLevelHigh() : ErrorCorrectionLevel::HIGH(); + // First check for version 5 (using enums) + if ($this->endroid5) { + return match ($level) { + 'L' => ErrorCorrectionLevel::Low, + 'M' => ErrorCorrectionLevel::Medium, + 'Q' => ErrorCorrectionLevel::Quartile, + default => ErrorCorrectionLevel::High, + }; + } + + // If not check for version 4 (using classes) + if ($this->endroid4) { + return match ($level) { + 'L' => new ErrorCorrectionLevelLow(), + 'M' => new ErrorCorrectionLevelMedium(), + 'Q' => new ErrorCorrectionLevelQuartile(), + default => new ErrorCorrectionLevelHigh(), + }; } + + // Any other version will be using strings + return match ($level) { + 'L' => ErrorCorrectionLevel::LOW(), + 'M' => ErrorCorrectionLevel::MEDIUM(), + 'Q' => ErrorCorrectionLevel::QUARTILE(), + default => ErrorCorrectionLevel::HIGH(), + }; } } diff --git a/lib/Providers/Qr/EndroidQrCodeWithLogoProvider.php b/lib/Providers/Qr/EndroidQrCodeWithLogoProvider.php index 71a5559..0a0b2d2 100755 --- a/lib/Providers/Qr/EndroidQrCodeWithLogoProvider.php +++ b/lib/Providers/Qr/EndroidQrCodeWithLogoProvider.php @@ -25,10 +25,10 @@ public function setLogo($path, $size = null) $this->logoSize = (array)$size; } - public function getQRCodeImage(string $qrtext, int $size): string + public function getQRCodeImage(string $qrText, int $size): string { if (!$this->endroid4) { - return $this->qrCodeInstance($qrtext, $size)->writeString(); + return $this->qrCodeInstance($qrText, $size)->writeString(); } $logo = null; @@ -42,12 +42,12 @@ public function getQRCodeImage(string $qrtext, int $size): string } } $writer = new PngWriter(); - return $writer->write($this->qrCodeInstance($qrtext, $size), $logo)->getString(); + return $writer->write($this->qrCodeInstance($qrText, $size), $logo)->getString(); } - protected function qrCodeInstance(string $qrtext, int $size): QrCode + protected function qrCodeInstance(string $qrText, int $size): QrCode { - $qrCode = parent::qrCodeInstance($qrtext, $size); + $qrCode = parent::qrCodeInstance($qrText, $size); if (!$this->endroid4 && $this->logoPath) { $qrCode->setLogoPath($this->logoPath); diff --git a/lib/Providers/Qr/GoogleChartsQrCodeProvider.php b/lib/Providers/Qr/GoogleChartsQrCodeProvider.php index fd59dff..4dd6c2f 100644 --- a/lib/Providers/Qr/GoogleChartsQrCodeProvider.php +++ b/lib/Providers/Qr/GoogleChartsQrCodeProvider.php @@ -16,18 +16,21 @@ public function getMimeType(): string return 'image/png'; } - public function getQRCodeImage(string $qrtext, int $size): string + public function getQRCodeImage(string $qrText, int $size): string { - return $this->getContent($this->getUrl($qrtext, $size)); + return $this->getContent($this->getUrl($qrText, $size)); } - public function getUrl(string $qrtext, int $size): string + public function getUrl(string $qrText, int $size): string { - return 'https://chart.googleapis.com/chart' - . '?chs=' . $size . 'x' . $size - . '&chld=' . urlencode(strtoupper($this->errorcorrectionlevel) . '|' . $this->margin) - . '&cht=' . 'qr' - . '&choe=' . $this->encoding - . '&chl=' . rawurlencode($qrtext); + $queryParameters = array( + 'chs' => $size . 'x' . $size, + 'chld' => strtoupper($this->errorcorrectionlevel) . '|' . $this->margin, + 'cht' => 'qr', + 'choe' => $this->encoding, + 'chl' => $qrText, + ); + + return 'https://chart.googleapis.com/chart?' . http_build_query($queryParameters); } } diff --git a/lib/Providers/Qr/HandlesDataUri.php b/lib/Providers/Qr/HandlesDataUri.php index 7ca425d..8b07845 100644 --- a/lib/Providers/Qr/HandlesDataUri.php +++ b/lib/Providers/Qr/HandlesDataUri.php @@ -10,7 +10,7 @@ trait HandlesDataUri { /** - * @return array + * @return array|null */ private function DecodeDataUri(string $datauri): ?array { diff --git a/lib/Providers/Qr/IQRCodeProvider.php b/lib/Providers/Qr/IQRCodeProvider.php index dc53365..9240741 100644 --- a/lib/Providers/Qr/IQRCodeProvider.php +++ b/lib/Providers/Qr/IQRCodeProvider.php @@ -9,12 +9,12 @@ interface IQRCodeProvider /** * Generate and return the QR code to embed in a web page * - * @param string $qrtext the value to encode in the QR code + * @param string $qrText the value to encode in the QR code * @param int $size the desired size of the QR code * * @return string file contents of the QR code */ - public function getQRCodeImage(string $qrtext, int $size): string; + public function getQRCodeImage(string $qrText, int $size): string; /** * Returns the appropriate mime type for the QR code diff --git a/lib/Providers/Qr/ImageChartsQRCodeProvider.php b/lib/Providers/Qr/ImageChartsQRCodeProvider.php index d62a96a..2f1079e 100644 --- a/lib/Providers/Qr/ImageChartsQRCodeProvider.php +++ b/lib/Providers/Qr/ImageChartsQRCodeProvider.php @@ -18,16 +18,20 @@ public function getMimeType(): string return 'image/png'; } - public function getQRCodeImage(string $qrtext, int $size): string + public function getQRCodeImage(string $qrText, int $size): string { - return $this->getContent($this->getUrl($qrtext, $size)); + return $this->getContent($this->getUrl($qrText, $size)); } - public function getUrl(string $qrtext, int $size): string + public function getUrl(string $qrText, int $size): string { - return 'https://image-charts.com/chart?cht=qr' - . '&chs=' . ceil($size / 2) . 'x' . ceil($size / 2) - . '&chld=' . $this->errorcorrectionlevel . '|' . $this->margin - . '&chl=' . rawurlencode($qrtext); + $queryParameters = array( + 'cht' => 'qr', + 'chs' => ceil($size / 2) . 'x' . ceil($size / 2), + 'chld' => $this->errorcorrectionlevel . '|' . $this->margin, + 'chl' => $qrText, + ); + + return 'https://image-charts.com/chart?' . http_build_query($queryParameters); } } diff --git a/lib/Providers/Qr/QRServerProvider.php b/lib/Providers/Qr/QRServerProvider.php index b223d91..9244795 100644 --- a/lib/Providers/Qr/QRServerProvider.php +++ b/lib/Providers/Qr/QRServerProvider.php @@ -31,22 +31,25 @@ public function getMimeType(): string throw new QRException(sprintf('Unknown MIME-type: %s', $this->format)); } - public function getQRCodeImage(string $qrtext, int $size): string + public function getQRCodeImage(string $qrText, int $size): string { - return $this->getContent($this->getUrl($qrtext, $size)); + return $this->getContent($this->getUrl($qrText, $size)); } - public function getUrl(string $qrtext, int $size): string + public function getUrl(string $qrText, int $size): string { - return 'https://api.qrserver.com/v1/create-qr-code/' - . '?size=' . $size . 'x' . $size - . '&ecc=' . strtoupper($this->errorcorrectionlevel) - . '&margin=' . $this->margin - . '&qzone=' . $this->qzone - . '&bgcolor=' . $this->decodeColor($this->bgcolor) - . '&color=' . $this->decodeColor($this->color) - . '&format=' . strtolower($this->format) - . '&data=' . rawurlencode($qrtext); + $queryParameters = array( + 'size' => $size . 'x' . $size, + 'ecc' => strtoupper($this->errorcorrectionlevel), + 'margin' => $this->margin, + 'qzone' => $this->qzone, + 'bgcolor' => $this->decodeColor($this->bgcolor), + 'color' => $this->decodeColor($this->color), + 'format' => strtolower($this->format), + 'data' => $qrText, + ); + + return 'https://api.qrserver.com/v1/create-qr-code/?' . http_build_query($queryParameters); } private function decodeColor(string $value): string diff --git a/lib/Providers/Qr/QRicketProvider.php b/lib/Providers/Qr/QRicketProvider.php index f72463d..d976b5f 100644 --- a/lib/Providers/Qr/QRicketProvider.php +++ b/lib/Providers/Qr/QRicketProvider.php @@ -27,19 +27,22 @@ public function getMimeType(): string throw new QRException(sprintf('Unknown MIME-type: %s', $this->format)); } - public function getQRCodeImage(string $qrtext, int $size): string + public function getQRCodeImage(string $qrText, int $size): string { - return $this->getContent($this->getUrl($qrtext, $size)); + return $this->getContent($this->getUrl($qrText, $size)); } - public function getUrl(string $qrtext, int $size): string + public function getUrl(string $qrText, int $size): string { - return 'http://qrickit.com/api/qr' - . '?qrsize=' . (string) $size - . '&e=' . strtolower($this->errorcorrectionlevel) - . '&bgdcolor=' . $this->bgcolor - . '&fgdcolor=' . $this->color - . '&t=' . strtolower($this->format) - . '&d=' . rawurlencode($qrtext); + $queryParameters = array( + 'qrsize' => $size, + 'e' => strtolower($this->errorcorrectionlevel), + 'bgdcolor' => $this->bgcolor, + 'fgdcolor' => $this->color, + 't' => strtolower($this->format), + 'd' => $qrText, + ); + + return 'http://qrickit.com/api/qr?' . http_build_query($queryParameters); } } diff --git a/lib/Providers/Rng/HashRNGProvider.php b/lib/Providers/Rng/HashRNGProvider.php index 6d29755..20241da 100644 --- a/lib/Providers/Rng/HashRNGProvider.php +++ b/lib/Providers/Rng/HashRNGProvider.php @@ -8,7 +8,7 @@ class HashRNGProvider implements IRNGProvider { - public function __construct(private string $algorithm = 'sha256') + public function __construct(private readonly string $algorithm = 'sha256') { $algos = array_values(hash_algos()); if (!in_array($this->algorithm, $algos, true)) { diff --git a/lib/Providers/Rng/OpenSSLRNGProvider.php b/lib/Providers/Rng/OpenSSLRNGProvider.php index ef2aeb1..4063847 100644 --- a/lib/Providers/Rng/OpenSSLRNGProvider.php +++ b/lib/Providers/Rng/OpenSSLRNGProvider.php @@ -6,7 +6,7 @@ class OpenSSLRNGProvider implements IRNGProvider { - public function __construct(private bool $requirestrong = true) + public function __construct(private readonly bool $requirestrong = true) { } diff --git a/lib/Providers/Time/HttpTimeProvider.php b/lib/Providers/Time/HttpTimeProvider.php index 4131fee..55c20f6 100644 --- a/lib/Providers/Time/HttpTimeProvider.php +++ b/lib/Providers/Time/HttpTimeProvider.php @@ -12,21 +12,16 @@ */ class HttpTimeProvider implements ITimeProvider { - /** @var array */ - public array $options; - /** * @param array $options */ public function __construct( public string $url = 'https://google.com', public string $expectedtimeformat = 'D, d M Y H:i:s O+', - array $options = null, + public ?array $options = null, ) { - $this->url = $url; - $this->expectedtimeformat = $expectedtimeformat; - if ($options === null) { - $options = array( + if ($this->options === null) { + $this->options = array( 'http' => array( 'method' => 'HEAD', 'follow_location' => false, @@ -41,7 +36,6 @@ public function __construct( ), ); } - $this->options = $options; } /** @@ -50,7 +44,7 @@ public function __construct( public function getTime() { try { - $context = stream_context_create($this->options); + $context = stream_context_create($this->options); $fd = fopen($this->url, 'rb', false, $context); $headers = stream_get_meta_data($fd); fclose($fd); diff --git a/lib/Providers/Time/NTPTimeProvider.php b/lib/Providers/Time/NTPTimeProvider.php index 20db712..f1db3a3 100644 --- a/lib/Providers/Time/NTPTimeProvider.php +++ b/lib/Providers/Time/NTPTimeProvider.php @@ -47,7 +47,7 @@ public function getTime() // Interpret response $data = unpack('N12', $recv); - $timestamp = (int) sprintf('%u', $data[9]); + $timestamp = (int)sprintf('%u', $data[9]); // NTP is number of seconds since 0000 UT on 1 January 1900 Unix time is seconds since 0000 UT on 1 January 1970 return $timestamp - 2208988800; diff --git a/lib/TwoFactorAuth.php b/lib/TwoFactorAuth.php index ab9ee4b..f3f867e 100644 --- a/lib/TwoFactorAuth.php +++ b/lib/TwoFactorAuth.php @@ -28,13 +28,13 @@ class TwoFactorAuth private static array $_base32lookup = array(); public function __construct( - private ?string $issuer = null, - private int $digits = 6, - private int $period = 30, - private Algorithm $algorithm = Algorithm::Sha1, - private ?IQRCodeProvider $qrcodeprovider = null, - private ?IRNGProvider $rngprovider = null, - private ?ITimeProvider $timeprovider = null + private readonly ?string $issuer = null, + private readonly int $digits = 6, + private readonly int $period = 30, + private readonly Algorithm $algorithm = Algorithm::Sha1, + private ?IQRCodeProvider $qrcodeprovider = null, + private ?IRNGProvider $rngprovider = null, + private ?ITimeProvider $timeprovider = null ) { if ($this->digits <= 0) { throw new TwoFactorAuthException('Digits must be > 0'); @@ -54,7 +54,7 @@ public function __construct( public function createSecret(int $bits = 80, bool $requirecryptosecure = true): string { $secret = ''; - $bytes = (int) ceil($bits / 5); // We use 5 bits of each byte (since we have a 32-character 'alphabet' / BASE32) + $bytes = (int)ceil($bits / 5); // We use 5 bits of each byte (since we have a 32-character 'alphabet' / BASE32) $rngprovider = $this->getRngProvider(); if ($requirecryptosecure && !$rngprovider->isCryptographicallySecure()) { throw new TwoFactorAuthException('RNG provider is not cryptographically secure'); @@ -79,7 +79,7 @@ public function getCode(string $secret, ?int $time = null): string $value = unpack('N', $hashpart); // Unpack binary value $value = $value[1] & 0x7FFFFFFF; // Drop MSB, keep only 31 bits - return str_pad((string) ($value % 10** $this->digits), $this->digits, '0', STR_PAD_LEFT); + return str_pad((string)($value % 10 ** $this->digits), $this->digits, '0', STR_PAD_LEFT); } /** @@ -123,6 +123,7 @@ public function getQRCodeImageAsDataUri(string $label, string $secret, int $size /** * Compare default timeprovider with specified timeproviders and ensure the time is within the specified number of seconds (leniency) * @param array $timeproviders + * @throws TwoFactorAuthException */ public function ensureCorrectTime(?array $timeproviders = null, int $leniency = 5): void { @@ -157,21 +158,15 @@ public function getQRText(string $label, string $secret): string return 'otpauth://totp/' . rawurlencode($label) . '?secret=' . rawurlencode($secret) . '&issuer=' . rawurlencode((string)$this->issuer) - . '&period=' . intval($this->period) + . '&period=' . $this->period . '&algorithm=' . rawurlencode(strtoupper($this->algorithm->value)) - . '&digits=' . intval($this->digits); + . '&digits=' . $this->digits; } - /** - * @throws TwoFactorAuthException - */ public function getQrCodeProvider(): IQRCodeProvider { // Set default QR Code provider if none was specified - if (null === $this->qrcodeprovider) { - return $this->qrcodeprovider = new QRServerProvider(); - } - return $this->qrcodeprovider; + return $this->qrcodeprovider ??= new QRServerProvider(); } /** @@ -179,7 +174,7 @@ public function getQrCodeProvider(): IQRCodeProvider */ public function getRngProvider(): IRNGProvider { - if (null !== $this->rngprovider) { + if ($this->rngprovider !== null) { return $this->rngprovider; } if (function_exists('random_bytes')) { @@ -194,16 +189,10 @@ public function getRngProvider(): IRNGProvider throw new TwoFactorAuthException('Unable to find a suited RNGProvider'); } - /** - * @throws TwoFactorAuthException - */ public function getTimeProvider(): ITimeProvider { // Set default time provider if none was specified - if (null === $this->timeprovider) { - return $this->timeprovider = new LocalMachineTimeProvider(); - } - return $this->timeprovider; + return $this->timeprovider ??= new LocalMachineTimeProvider(); } /** @@ -218,7 +207,8 @@ private function codeEquals(string $safe, string $user): bool // we don't leak information about the difference of the two strings. if (strlen($safe) === strlen($user)) { $result = 0; - for ($i = 0; $i < strlen($safe); $i++) { + $strlen = strlen($safe); + for ($i = 0; $i < $strlen; $i++) { $result |= (ord($safe[$i]) ^ ord($user[$i])); } return $result === 0; @@ -228,21 +218,21 @@ private function codeEquals(string $safe, string $user): bool private function getTime(?int $time = null): int { - return ($time === null) ? $this->getTimeProvider()->getTime() : $time; + return $time ?? $this->getTimeProvider()->getTime(); } private function getTimeSlice(?int $time = null, int $offset = 0): int { - return (int) floor($time / $this->period) + ($offset * $this->period); + return (int)floor($time / $this->period) + ($offset * $this->period); } private function base32Decode(string $value): string { - if (strlen($value) == 0) { + if ($value === '') { return ''; } - if (preg_match('/[^' . preg_quote(self::$_base32dict) . ']/', $value) !== 0) { + if (preg_match('/[^' . preg_quote(self::$_base32dict, '/') . ']/', $value) !== 0) { throw new TwoFactorAuthException('Invalid base32 string'); } diff --git a/tests/Providers/Qr/IQRCodeProviderTest.php b/tests/Providers/Qr/IQRCodeProviderTest.php index bfab066..a9f7e82 100644 --- a/tests/Providers/Qr/IQRCodeProviderTest.php +++ b/tests/Providers/Qr/IQRCodeProviderTest.php @@ -20,9 +20,9 @@ public function testTotpUriIsCorrect(): void $tfa = new TwoFactorAuth('Test&Issuer', 6, 30, Algorithm::Sha1, $qr); $data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE')); - $this->assertEquals('test/test', $data['mimetype']); - $this->assertEquals('base64', $data['encoding']); - $this->assertEquals('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=Test%26Issuer&period=30&algorithm=SHA1&digits=6@200', $data['data']); + $this->assertSame('test/test', $data['mimetype']); + $this->assertSame('base64', $data['encoding']); + $this->assertSame('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=Test%26Issuer&period=30&algorithm=SHA1&digits=6@200', $data['data']); } public function testTotpUriIsCorrectNoIssuer(): void @@ -36,9 +36,9 @@ public function testTotpUriIsCorrectNoIssuer(): void $tfa = new TwoFactorAuth(null, 6, 30, Algorithm::Sha1, $qr); $data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE')); - $this->assertEquals('test/test', $data['mimetype']); - $this->assertEquals('base64', $data['encoding']); - $this->assertEquals('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=&period=30&algorithm=SHA1&digits=6@200', $data['data']); + $this->assertSame('test/test', $data['mimetype']); + $this->assertSame('base64', $data['encoding']); + $this->assertSame('otpauth://totp/Test%26Label?secret=VMR466AB62ZBOKHE&issuer=&period=30&algorithm=SHA1&digits=6@200', $data['data']); } public function testGetQRCodeImageAsDataUriThrowsOnInvalidSize(): void diff --git a/tests/Providers/Qr/TestQrProvider.php b/tests/Providers/Qr/TestQrProvider.php index f7b790c..2f46954 100644 --- a/tests/Providers/Qr/TestQrProvider.php +++ b/tests/Providers/Qr/TestQrProvider.php @@ -8,9 +8,9 @@ class TestQrProvider implements IQRCodeProvider { - public function getQRCodeImage(string $qrtext, int $size): string + public function getQRCodeImage(string $qrText, int $size): string { - return $qrtext . '@' . $size; + return $qrText . '@' . $size; } public function getMimeType(): string diff --git a/tests/Providers/Rng/CSRNGProviderTest.php b/tests/Providers/Rng/CSRNGProviderTest.php index 0d4898b..64f66e8 100644 --- a/tests/Providers/Rng/CSRNGProviderTest.php +++ b/tests/Providers/Rng/CSRNGProviderTest.php @@ -19,7 +19,7 @@ public function testCSRNGProvidersReturnExpectedNumberOfBytes(): void if (function_exists('random_bytes')) { $rng = new CSRNGProvider(); foreach ($this->rngTestLengths as $l) { - $this->assertEquals($l, strlen($rng->getRandomBytes($l))); + $this->assertSame($l, strlen($rng->getRandomBytes($l))); } $this->assertTrue($rng->isCryptographicallySecure()); } else { diff --git a/tests/Providers/Rng/HashRNGProviderTest.php b/tests/Providers/Rng/HashRNGProviderTest.php index d54b9ae..4a71930 100644 --- a/tests/Providers/Rng/HashRNGProviderTest.php +++ b/tests/Providers/Rng/HashRNGProviderTest.php @@ -18,7 +18,7 @@ public function testHashRNGProvidersReturnExpectedNumberOfBytes() { $rng = new HashRNGProvider(); foreach ($this->rngTestLengths as $l) { - $this->assertEquals($l, strlen($rng->getRandomBytes($l))); + $this->assertSame($l, strlen($rng->getRandomBytes($l))); } $this->assertFalse($rng->isCryptographicallySecure()); diff --git a/tests/Providers/Rng/IRNGProviderTest.php b/tests/Providers/Rng/IRNGProviderTest.php index a55a56d..7ff40c8 100644 --- a/tests/Providers/Rng/IRNGProviderTest.php +++ b/tests/Providers/Rng/IRNGProviderTest.php @@ -26,7 +26,7 @@ public function testCreateSecretOverrideSecureDoesNotThrowOnInsecureRNG(): void $rng = new TestRNGProvider(); $tfa = new TwoFactorAuth('Test', 6, 30, Algorithm::Sha1, null, $rng); - $this->assertEquals('ABCDEFGHIJKLMNOP', $tfa->createSecret(80, false)); + $this->assertSame('ABCDEFGHIJKLMNOP', $tfa->createSecret(80, false)); } public function testCreateSecretDoesNotThrowOnSecureRNGProvider(): void @@ -34,7 +34,7 @@ public function testCreateSecretDoesNotThrowOnSecureRNGProvider(): void $rng = new TestRNGProvider(true); $tfa = new TwoFactorAuth('Test', 6, 30, Algorithm::Sha1, null, $rng); - $this->assertEquals('ABCDEFGHIJKLMNOP', $tfa->createSecret()); + $this->assertSame('ABCDEFGHIJKLMNOP', $tfa->createSecret()); } public function testCreateSecretGeneratesDesiredAmountOfEntropy(): void @@ -42,11 +42,11 @@ public function testCreateSecretGeneratesDesiredAmountOfEntropy(): void $rng = new TestRNGProvider(true); $tfa = new TwoFactorAuth('Test', 6, 30, Algorithm::Sha1, null, $rng); - $this->assertEquals('A', $tfa->createSecret(5)); - $this->assertEquals('AB', $tfa->createSecret(6)); - $this->assertEquals('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $tfa->createSecret(128)); - $this->assertEquals('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', $tfa->createSecret(160)); - $this->assertEquals('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', $tfa->createSecret(320)); - $this->assertEquals('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ234567A', $tfa->createSecret(321)); + $this->assertSame('A', $tfa->createSecret(5)); + $this->assertSame('AB', $tfa->createSecret(6)); + $this->assertSame('ABCDEFGHIJKLMNOPQRSTUVWXYZ', $tfa->createSecret(128)); + $this->assertSame('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', $tfa->createSecret(160)); + $this->assertSame('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', $tfa->createSecret(320)); + $this->assertSame('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZ234567A', $tfa->createSecret(321)); } } diff --git a/tests/Providers/Rng/OpenSSLRNGProviderTest.php b/tests/Providers/Rng/OpenSSLRNGProviderTest.php index 18fdb29..368c7cc 100644 --- a/tests/Providers/Rng/OpenSSLRNGProviderTest.php +++ b/tests/Providers/Rng/OpenSSLRNGProviderTest.php @@ -18,7 +18,7 @@ public function testStrongOpenSSLRNGProvidersReturnExpectedNumberOfBytes() { $rng = new OpenSSLRNGProvider(true); foreach ($this->rngTestLengths as $l) { - $this->assertEquals($l, strlen($rng->getRandomBytes($l))); + $this->assertSame($l, strlen($rng->getRandomBytes($l))); } $this->assertTrue($rng->isCryptographicallySecure()); @@ -31,7 +31,7 @@ public function testNonStrongOpenSSLRNGProvidersReturnExpectedNumberOfBytes() { $rng = new OpenSSLRNGProvider(false); foreach ($this->rngTestLengths as $l) { - $this->assertEquals($l, strlen($rng->getRandomBytes($l))); + $this->assertSame($l, strlen($rng->getRandomBytes($l))); } $this->assertFalse($rng->isCryptographicallySecure()); diff --git a/tests/Providers/Rng/TestRNGProvider.php b/tests/Providers/Rng/TestRNGProvider.php index 78ceec1..c166c66 100644 --- a/tests/Providers/Rng/TestRNGProvider.php +++ b/tests/Providers/Rng/TestRNGProvider.php @@ -8,7 +8,7 @@ class TestRNGProvider implements IRNGProvider { - public function __construct(private bool $isSecure = false) + public function __construct(private readonly bool $isSecure = false) { } diff --git a/tests/TwoFactorAuthTest.php b/tests/TwoFactorAuthTest.php index c24eb69..0fa9f11 100644 --- a/tests/TwoFactorAuthTest.php +++ b/tests/TwoFactorAuthTest.php @@ -7,6 +7,8 @@ use PHPUnit\Framework\TestCase; use ReflectionMethod; use RobThree\Auth\Algorithm; +use RobThree\Auth\Providers\Time\HttpTimeProvider; +use RobThree\Auth\Providers\Time\NTPTimeProvider; use RobThree\Auth\TwoFactorAuth; use RobThree\Auth\TwoFactorAuthException; @@ -29,19 +31,19 @@ public function testConstructorThrowsOnInvalidPeriod(): void public function testGetCodeReturnsCorrectResults(): void { $tfa = new TwoFactorAuth('Test'); - $this->assertEquals('543160', $tfa->getCode('VMR466AB62ZBOKHE', 1426847216)); - $this->assertEquals('538532', $tfa->getCode('VMR466AB62ZBOKHE', 0)); + $this->assertSame('543160', $tfa->getCode('VMR466AB62ZBOKHE', 1426847216)); + $this->assertSame('538532', $tfa->getCode('VMR466AB62ZBOKHE', 0)); } public function testEnsureAllTimeProvidersReturnCorrectTime(): void { $tfa = new TwoFactorAuth('Test', 6, 30, Algorithm::Sha1); $tfa->ensureCorrectTime(array( - new \RobThree\Auth\Providers\Time\NTPTimeProvider(), // Uses pool.ntp.org by default + new NTPTimeProvider(), // Uses pool.ntp.org by default //new \RobThree\Auth\Providers\Time\NTPTimeProvider('time.google.com'), // Somehow time.google.com and time.windows.com make travis timeout?? - new \RobThree\Auth\Providers\Time\HttpTimeProvider(), // Uses google.com by default + new HttpTimeProvider(), // Uses google.com by default //new \RobThree\Auth\Providers\Time\HttpTimeProvider('https://github.com'), // github.com will periodically report times that are off by more than 5 sec - new \RobThree\Auth\Providers\Time\HttpTimeProvider('https://yahoo.com'), + new HttpTimeProvider('https://yahoo.com'), )); $this->expectNotToPerformAssertions(); } @@ -50,19 +52,19 @@ public function testVerifyCodeWorksCorrectly(): void { $tfa = new TwoFactorAuth('Test', 6, 30); $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847190)); - $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 + 29)); //Test discrepancy - $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 + 30)); //Test discrepancy - $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 - 1)); //Test discrepancy + $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 + 29)); //Test discrepancy + $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 + 30)); //Test discrepancy + $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 0, 1426847190 - 1)); //Test discrepancy - $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 + 0)); //Test discrepancy - $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 + 35)); //Test discrepancy - $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 - 35)); //Test discrepancy + $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205)); //Test discrepancy + $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 + 35)); //Test discrepancy + $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 - 35)); //Test discrepancy - $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 + 65)); //Test discrepancy - $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 - 65)); //Test discrepancy + $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 + 65)); //Test discrepancy + $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 1, 1426847205 - 65)); //Test discrepancy - $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 2, 1426847205 + 65)); //Test discrepancy - $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 2, 1426847205 - 65)); //Test discrepancy + $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 2, 1426847205 + 65)); //Test discrepancy + $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 2, 1426847205 - 65)); //Test discrepancy } public function testVerifyCorrectTimeSliceIsReturned(): void @@ -72,23 +74,23 @@ public function testVerifyCorrectTimeSliceIsReturned(): void // We test with discrepancy 3 (so total of 7 codes: c-3, c-2, c-1, c, c+1, c+2, c+3 // Ensure each corresponding timeslice is returned correctly $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '534113', 3, 1426847190, $timeslice1)); - $this->assertEquals(47561570, $timeslice1); + $this->assertSame(47561570, $timeslice1); $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '819652', 3, 1426847190, $timeslice2)); - $this->assertEquals(47561571, $timeslice2); + $this->assertSame(47561571, $timeslice2); $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '915954', 3, 1426847190, $timeslice3)); - $this->assertEquals(47561572, $timeslice3); + $this->assertSame(47561572, $timeslice3); $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '543160', 3, 1426847190, $timeslice4)); - $this->assertEquals(47561573, $timeslice4); + $this->assertSame(47561573, $timeslice4); $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '348401', 3, 1426847190, $timeslice5)); - $this->assertEquals(47561574, $timeslice5); + $this->assertSame(47561574, $timeslice5); $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '648525', 3, 1426847190, $timeslice6)); - $this->assertEquals(47561575, $timeslice6); + $this->assertSame(47561575, $timeslice6); $this->assertTrue($tfa->verifyCode('VMR466AB62ZBOKHE', '170645', 3, 1426847190, $timeslice7)); - $this->assertEquals(47561576, $timeslice7); + $this->assertSame(47561576, $timeslice7); // Incorrect code should return false and a 0 timeslice $this->assertFalse($tfa->verifyCode('VMR466AB62ZBOKHE', '111111', 3, 1426847190, $timeslice8)); - $this->assertEquals(0, $timeslice8); + $this->assertSame(0, $timeslice8); } public function testGetCodeThrowsOnInvalidBase32String1(): void @@ -125,16 +127,15 @@ public function testKnownBase32DecodeTestVectors(): void $tfa = new TwoFactorAuth('Test'); $method = new ReflectionMethod(TwoFactorAuth::class, 'base32Decode'); - $method->setAccessible(true); // Test vectors from: https://tools.ietf.org/html/rfc4648#page-12 - $this->assertEquals('', $method->invoke($tfa, '')); - $this->assertEquals('f', $method->invoke($tfa, 'MY======')); - $this->assertEquals('fo', $method->invoke($tfa, 'MZXQ====')); - $this->assertEquals('foo', $method->invoke($tfa, 'MZXW6===')); - $this->assertEquals('foob', $method->invoke($tfa, 'MZXW6YQ=')); - $this->assertEquals('fooba', $method->invoke($tfa, 'MZXW6YTB')); - $this->assertEquals('foobar', $method->invoke($tfa, 'MZXW6YTBOI======')); + $this->assertSame('', $method->invoke($tfa, '')); + $this->assertSame('f', $method->invoke($tfa, 'MY======')); + $this->assertSame('fo', $method->invoke($tfa, 'MZXQ====')); + $this->assertSame('foo', $method->invoke($tfa, 'MZXW6===')); + $this->assertSame('foob', $method->invoke($tfa, 'MZXW6YQ=')); + $this->assertSame('fooba', $method->invoke($tfa, 'MZXW6YTB')); + $this->assertSame('foobar', $method->invoke($tfa, 'MZXW6YTBOI======')); } public function testKnownBase32DecodeUnpaddedTestVectors(): void @@ -146,16 +147,15 @@ public function testKnownBase32DecodeUnpaddedTestVectors(): void $tfa = new TwoFactorAuth('Test'); $method = new ReflectionMethod(TwoFactorAuth::class, 'base32Decode'); - $method->setAccessible(true); // Test vectors from: https://tools.ietf.org/html/rfc4648#page-12 - $this->assertEquals('', $method->invoke($tfa, '')); - $this->assertEquals('f', $method->invoke($tfa, 'MY')); - $this->assertEquals('fo', $method->invoke($tfa, 'MZXQ')); - $this->assertEquals('foo', $method->invoke($tfa, 'MZXW6')); - $this->assertEquals('foob', $method->invoke($tfa, 'MZXW6YQ')); - $this->assertEquals('fooba', $method->invoke($tfa, 'MZXW6YTB')); - $this->assertEquals('foobar', $method->invoke($tfa, 'MZXW6YTBOI')); + $this->assertSame('', $method->invoke($tfa, '')); + $this->assertSame('f', $method->invoke($tfa, 'MY')); + $this->assertSame('fo', $method->invoke($tfa, 'MZXQ')); + $this->assertSame('foo', $method->invoke($tfa, 'MZXW6')); + $this->assertSame('foob', $method->invoke($tfa, 'MZXW6YQ')); + $this->assertSame('fooba', $method->invoke($tfa, 'MZXW6YTB')); + $this->assertSame('foobar', $method->invoke($tfa, 'MZXW6YTBOI')); } public function testKnownTestVectors_sha1(): void @@ -163,12 +163,12 @@ public function testKnownTestVectors_sha1(): void //Known test vectors for SHA1: https://tools.ietf.org/html/rfc6238#page-15 $secret = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'; //== base32encode('12345678901234567890') $tfa = new TwoFactorAuth('Test', 8, 30, Algorithm::Sha1); - $this->assertEquals('94287082', $tfa->getCode($secret, 59)); - $this->assertEquals('07081804', $tfa->getCode($secret, 1111111109)); - $this->assertEquals('14050471', $tfa->getCode($secret, 1111111111)); - $this->assertEquals('89005924', $tfa->getCode($secret, 1234567890)); - $this->assertEquals('69279037', $tfa->getCode($secret, 2000000000)); - $this->assertEquals('65353130', $tfa->getCode($secret, 20000000000)); + $this->assertSame('94287082', $tfa->getCode($secret, 59)); + $this->assertSame('07081804', $tfa->getCode($secret, 1111111109)); + $this->assertSame('14050471', $tfa->getCode($secret, 1111111111)); + $this->assertSame('89005924', $tfa->getCode($secret, 1234567890)); + $this->assertSame('69279037', $tfa->getCode($secret, 2000000000)); + $this->assertSame('65353130', $tfa->getCode($secret, 20000000000)); } public function testKnownTestVectors_sha256(): void @@ -176,12 +176,12 @@ public function testKnownTestVectors_sha256(): void //Known test vectors for SHA256: https://tools.ietf.org/html/rfc6238#page-15 $secret = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA'; //== base32encode('12345678901234567890123456789012') $tfa = new TwoFactorAuth('Test', 8, 30, Algorithm::Sha256); - $this->assertEquals('46119246', $tfa->getCode($secret, 59)); - $this->assertEquals('68084774', $tfa->getCode($secret, 1111111109)); - $this->assertEquals('67062674', $tfa->getCode($secret, 1111111111)); - $this->assertEquals('91819424', $tfa->getCode($secret, 1234567890)); - $this->assertEquals('90698825', $tfa->getCode($secret, 2000000000)); - $this->assertEquals('77737706', $tfa->getCode($secret, 20000000000)); + $this->assertSame('46119246', $tfa->getCode($secret, 59)); + $this->assertSame('68084774', $tfa->getCode($secret, 1111111109)); + $this->assertSame('67062674', $tfa->getCode($secret, 1111111111)); + $this->assertSame('91819424', $tfa->getCode($secret, 1234567890)); + $this->assertSame('90698825', $tfa->getCode($secret, 2000000000)); + $this->assertSame('77737706', $tfa->getCode($secret, 20000000000)); } public function testKnownTestVectors_sha512(): void @@ -189,11 +189,11 @@ public function testKnownTestVectors_sha512(): void //Known test vectors for SHA512: https://tools.ietf.org/html/rfc6238#page-15 $secret = 'GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA'; //== base32encode('1234567890123456789012345678901234567890123456789012345678901234') $tfa = new TwoFactorAuth('Test', 8, 30, Algorithm::Sha512); - $this->assertEquals('90693936', $tfa->getCode($secret, 59)); - $this->assertEquals('25091201', $tfa->getCode($secret, 1111111109)); - $this->assertEquals('99943326', $tfa->getCode($secret, 1111111111)); - $this->assertEquals('93441116', $tfa->getCode($secret, 1234567890)); - $this->assertEquals('38618901', $tfa->getCode($secret, 2000000000)); - $this->assertEquals('47863826', $tfa->getCode($secret, 20000000000)); + $this->assertSame('90693936', $tfa->getCode($secret, 59)); + $this->assertSame('25091201', $tfa->getCode($secret, 1111111109)); + $this->assertSame('99943326', $tfa->getCode($secret, 1111111111)); + $this->assertSame('93441116', $tfa->getCode($secret, 1234567890)); + $this->assertSame('38618901', $tfa->getCode($secret, 2000000000)); + $this->assertSame('47863826', $tfa->getCode($secret, 20000000000)); } } diff --git a/testsDependency/BaconQRCodeTest.php b/testsDependency/BaconQRCodeTest.php index 4c224c9..2a23061 100644 --- a/testsDependency/BaconQRCodeTest.php +++ b/testsDependency/BaconQRCodeTest.php @@ -22,7 +22,7 @@ public function testDependency(): void $tfa = new TwoFactorAuth('Test&Issuer', 6, 30, Algorithm::Sha1, $qr); $data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE')); - $this->assertEquals('image/svg+xml', $data['mimetype']); + $this->assertSame('image/svg+xml', $data['mimetype']); } public function testBadTextColour(): void diff --git a/testsDependency/EndroidQRCodeTest.php b/testsDependency/EndroidQRCodeTest.php index 4b84dd2..6bd59fe 100644 --- a/testsDependency/EndroidQRCodeTest.php +++ b/testsDependency/EndroidQRCodeTest.php @@ -19,8 +19,8 @@ public function testDependency(): void $qr = new EndroidQrCodeProvider(); $tfa = new TwoFactorAuth('Test&Issuer', 6, 30, Algorithm::Sha1, $qr); $data = $this->DecodeDataUri($tfa->getQRCodeImageAsDataUri('Test&Label', 'VMR466AB62ZBOKHE')); - $this->assertEquals('image/png', $data['mimetype']); - $this->assertEquals('base64', $data['encoding']); + $this->assertSame('image/png', $data['mimetype']); + $this->assertSame('base64', $data['encoding']); $this->assertNotEmpty($data['data']); } }