From 43dc722daa4dbeca9a27c018f9fa68b5f8eeef41 Mon Sep 17 00:00:00 2001 From: Artur Date: Fri, 20 Sep 2024 00:45:58 +0200 Subject: [PATCH] Update vendor libs --- .../src/Renderer/Eye/PointyEye.php | 56 +++++ .../src/Renderer/GDLibRenderer.php | 238 ++++++++++++++++++ .../robthree/twofactorauth/CHANGELOG.md | 71 ++++++ .../robthree/twofactorauth/lib/Algorithm.php | 16 ++ 4 files changed, 381 insertions(+) create mode 100644 lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/PointyEye.php create mode 100644 lib/vendor/bacon/bacon-qr-code/src/Renderer/GDLibRenderer.php create mode 100644 lib/vendor/robthree/twofactorauth/CHANGELOG.md create mode 100644 lib/vendor/robthree/twofactorauth/lib/Algorithm.php diff --git a/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/PointyEye.php b/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/PointyEye.php new file mode 100644 index 00000000..39c7d23a --- /dev/null +++ b/lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/PointyEye.php @@ -0,0 +1,56 @@ +move(-3.5, 3.5) + ->line(-3.5, 0) + ->ellipticArc(3.5, 3.5, 0, false, true, 0, -3.5) + ->line(3.5, -3.5) + ->line(3.5, 3.5) + ->close() + ->move(2.5, 0) + ->ellipticArc(2.5, 2.5, 0, false, true, 0, 2.5) + ->ellipticArc(2.5, 2.5, 0, false, true, -2.5, 0) + ->ellipticArc(2.5, 2.5, 0, false, true, 0, -2.5) + ->ellipticArc(2.5, 2.5, 0, false, true, 2.5, 0) + ->close() + ; + } + + public function getInternalPath() : Path + { + return (new Path()) + ->move(1.5, 0) + ->ellipticArc(1.5, 1.5, 0., false, true, 0., 1.5) + ->ellipticArc(1.5, 1.5, 0., false, true, -1.5, 0.) + ->ellipticArc(1.5, 1.5, 0., false, true, 0., -1.5) + ->ellipticArc(1.5, 1.5, 0., false, true, 1.5, 0.) + ->close() + ; + } +} diff --git a/lib/vendor/bacon/bacon-qr-code/src/Renderer/GDLibRenderer.php b/lib/vendor/bacon/bacon-qr-code/src/Renderer/GDLibRenderer.php new file mode 100644 index 00000000..51164b9b --- /dev/null +++ b/lib/vendor/bacon/bacon-qr-code/src/Renderer/GDLibRenderer.php @@ -0,0 +1,238 @@ + + */ + private array $colors; + + public function __construct( + private int $size, + private int $margin = 4, + private string $imageFormat = 'png', + private int $compressionQuality = 9, + private ?Fill $fill = null + ) { + if (! extension_loaded('gd') || ! function_exists('gd_info')) { + throw new RuntimeException('You need to install the GD extension to use this back end'); + } + + if ($this->fill === null) { + $this->fill = Fill::default(); + } + if ($this->fill->hasGradientFill()) { + throw new InvalidArgumentException('GDLibRenderer does not support gradients'); + } + } + + /** + * @throws InvalidArgumentException if matrix width doesn't match height + */ + public function render(QrCode $qrCode): string + { + $matrix = $qrCode->getMatrix(); + $matrixSize = $matrix->getWidth(); + + if ($matrixSize !== $matrix->getHeight()) { + throw new InvalidArgumentException('Matrix must have the same width and height'); + } + + MatrixUtil::removePositionDetectionPatterns($matrix); + $this->newImage(); + $this->draw($matrix); + + return $this->renderImage(); + } + + private function newImage(): void + { + $img = imagecreatetruecolor($this->size, $this->size); + if ($img === false) { + throw new RuntimeException('Failed to create image of that size'); + } + + $this->image = $img; + imagealphablending($this->image, false); + imagesavealpha($this->image, true); + + + $bg = $this->getColor($this->fill->getBackgroundColor()); + imagefilledrectangle($this->image, 0, 0, $this->size, $this->size, $bg); + imagealphablending($this->image, true); + } + + private function draw(ByteMatrix $matrix): void + { + $matrixSize = $matrix->getWidth(); + + $pointsOnSide = $matrix->getWidth() + $this->margin * 2; + $pointInPx = $this->size / $pointsOnSide; + + $this->drawEye(0, 0, $pointInPx, $this->fill->getTopLeftEyeFill()); + $this->drawEye($matrixSize - 7, 0, $pointInPx, $this->fill->getTopRightEyeFill()); + $this->drawEye(0, $matrixSize - 7, $pointInPx, $this->fill->getBottomLeftEyeFill()); + + $rows = $matrix->getArray()->toArray(); + $color = $this->getColor($this->fill->getForegroundColor()); + for ($y = 0; $y < $matrixSize; $y += 1) { + for ($x = 0; $x < $matrixSize; $x += 1) { + if (! $rows[$y][$x]) { + continue; + } + + $points = $this->normalizePoints([ + ($this->margin + $x) * $pointInPx, ($this->margin + $y) * $pointInPx, + ($this->margin + $x + 1) * $pointInPx, ($this->margin + $y) * $pointInPx, + ($this->margin + $x + 1) * $pointInPx, ($this->margin + $y + 1) * $pointInPx, + ($this->margin + $x) * $pointInPx, ($this->margin + $y + 1) * $pointInPx, + ]); + imagefilledpolygon($this->image, $points, $color); + } + } + } + + private function drawEye(int $xOffset, int $yOffset, float $pointInPx, EyeFill $eyeFill): void + { + $internalColor = $this->getColor($eyeFill->inheritsInternalColor() + ? $this->fill->getForegroundColor() + : $eyeFill->getInternalColor()); + + $externalColor = $this->getColor($eyeFill->inheritsExternalColor() + ? $this->fill->getForegroundColor() + : $eyeFill->getExternalColor()); + + for ($y = 0; $y < 7; $y += 1) { + for ($x = 0; $x < 7; $x += 1) { + if ((($y === 1 || $y === 5) && $x > 0 && $x < 6) || (($x === 1 || $x === 5) && $y > 0 && $y < 6)) { + continue; + } + + $points = $this->normalizePoints([ + ($this->margin + $x + $xOffset) * $pointInPx, ($this->margin + $y + $yOffset) * $pointInPx, + ($this->margin + $x + $xOffset + 1) * $pointInPx, ($this->margin + $y + $yOffset) * $pointInPx, + ($this->margin + $x + $xOffset + 1) * $pointInPx, ($this->margin + $y + $yOffset + 1) * $pointInPx, + ($this->margin + $x + $xOffset) * $pointInPx, ($this->margin + $y + $yOffset + 1) * $pointInPx, + ]); + + if ($y > 1 && $y < 5 && $x > 1 && $x < 5) { + imagefilledpolygon($this->image, $points, $internalColor); + } else { + imagefilledpolygon($this->image, $points, $externalColor); + } + } + } + } + + /** + * Normalize points will trim right and bottom line by 1 pixel. + * Otherwise pixels of neighbors are overlapping which leads to issue with transparency and small QR codes. + */ + private function normalizePoints(array $points): array + { + $maxX = $maxY = 0; + for ($i = 0; $i < count($points); $i += 2) { + // Do manual round as GD just removes decimal part + $points[$i] = $newX = round($points[$i]); + $points[$i + 1] = $newY = round($points[$i + 1]); + + $maxX = max($maxX, $newX); + $maxY = max($maxY, $newY); + } + + // Do trimming only if there are 4 points (8 coordinates), assumes this is square. + + for ($i = 0; $i < count($points); $i += 2) { + $points[$i] = min($points[$i], $maxX - 1); + $points[$i + 1] = min($points[$i + 1], $maxY - 1); + } + + return $points; + } + + private function renderImage(): string + { + ob_start(); + $quality = $this->compressionQuality; + switch ($this->imageFormat) { + case 'png': + if ($quality > 9 || $quality < 0) { + $quality = 9; + } + imagepng($this->image, null, $quality); + break; + + case 'gif': + imagegif($this->image, null); + break; + + case 'jpeg': + case 'jpg': + if ($quality > 100 || $quality < 0) { + $quality = 85; + } + imagejpeg($this->image, null, $quality); + break; + default: + ob_end_clean(); + throw new InvalidArgumentException( + 'Supported image formats are jpeg, png and gif, got: ' . $this->imageFormat + ); + } + + imagedestroy($this->image); + $this->colors = []; + $this->image = null; + + return ob_get_clean(); + } + + private function getColor(ColorInterface $color): int + { + $alpha = 100; + + if ($color instanceof Alpha) { + $alpha = $color->getAlpha(); + $color = $color->getBaseColor(); + } + + $rgb = $color->toRgb(); + + $colorKey = sprintf('%02X%02X%02X%02X', $rgb->getRed(), $rgb->getGreen(), $rgb->getBlue(), $alpha); + + if (! isset($this->colors[$colorKey])) { + $colorId = imagecolorallocatealpha( + $this->image, + $rgb->getRed(), + $rgb->getGreen(), + $rgb->getBlue(), + (int)((100 - $alpha) / 100 * 127) // Alpha for GD is in range 0 (opaque) - 127 (transparent) + ); + + if ($colorId === false) { + throw new RuntimeException('Failed to create color: #' . $colorKey); + } + + $this->colors[$colorKey] = $colorId; + } + + return $this->colors[$colorKey]; + } +} diff --git a/lib/vendor/robthree/twofactorauth/CHANGELOG.md b/lib/vendor/robthree/twofactorauth/CHANGELOG.md new file mode 100644 index 00000000..5646a201 --- /dev/null +++ b/lib/vendor/robthree/twofactorauth/CHANGELOG.md @@ -0,0 +1,71 @@ +# RobThree\TwoFactorAuth changelog + +# Version 3.x + +## Breaking changes + +### PHP Version + +Version 3.x requires at least PHP 8.2. + +### Constructor signature change + +In order to ensure users of this library make a conscious choice of QR Code Provider, the QR Code Provider is now a mandatory argument, in first place. + +If you didn't provide one explicitly before, you can get the old behavior with: + +~~~php +use RobThree\Auth\TwoFactorAuth; +use RobThree\Auth\Providers\Qr\QRServerProvider; +$tfa = new TwoFactorAuth(new QRServerProvider()); +~~~ + +If you provided one before, the order of the parameters have been changed, so simply move the QRCodeProvider argument to the first place or use named arguments. + +Documentation on selecting a QR Code Provider is available here: [QR Code Provider documentation](https://robthree.github.io/TwoFactorAuth/qr-codes.html). + +### Default secret length + +The default secret length has been increased from 80 bits to 160 bits (RFC4226) PR [#117](https://github.com/RobThree/TwoFactorAuth/pull/117). This might cause an issue in your application if you were previously storing secrets in a column with restricted size. This change doesn't impact existing secrets, only new ones will get longer. + +Previously a secret was 16 characters, now it needs to be stored in a 32 characters width column. + +You can keep the old behavior by setting `80` as argument to `createSecret()` (not recommended, see [#117](https://github.com/RobThree/TwoFactorAuth/pull/117) for further discussion). + +## Other changes + +* The new PHP attribute [SensitiveParameter](https://www.php.net/manual/en/class.sensitiveparameter.php) was added to the code, to prevent accidental leak of secrets in stack traces. +* Likely not breaking anything, but now all external QR Code providers use HTTPS with a verified certificate. PR [#126](https://github.com/RobThree/TwoFactorAuth/pull/126). +* The CSPRNG is now exclusively using `random_bytes()` PHP function. Previously a fallback to `openssl` or non cryptographically secure PRNG existed, they have been removed. PR [#122](https://github.com/RobThree/TwoFactorAuth/pull/122). +* If an external QR code provider is used and the HTTP request results in an error, it will throw a `QRException`. Previously the error was ignored. PR [#130](https://github.com/RobThree/TwoFactorAuth/pull/130), fixes [#129](https://github.com/RobThree/TwoFactorAuth/issues/129). + +# Version 2.x + +## Breaking changes + +### PHP Version + +Version 2.x requires at least PHP 8.1. + +### Constructor signature + +With version 2.x, the `algorithm` parameter of `RobThree\Auth\TwoFactorAuth` constructor is now an `enum`. + +On version 1.x: + +~~~php +use RobThree\Auth\TwoFactorAuth; + +$lib = new TwoFactorAuth('issuer-name', 6, 30, 'sha1'); +~~~ + +On version 2.x, simple change the algorithm from a `string` to the correct `enum`: + +~~~php +use RobThree\Auth\TwoFactorAuth; +use RobThree\Auth\Algorithm; + +$lib = new TwoFactorAuth('issuer-name', 6, 30, Algorithm::Sha1); +~~~ + +See the [Algorithm.php](./lib/Algorithm.php) file to see available algorithms. diff --git a/lib/vendor/robthree/twofactorauth/lib/Algorithm.php b/lib/vendor/robthree/twofactorauth/lib/Algorithm.php new file mode 100644 index 00000000..185b2d80 --- /dev/null +++ b/lib/vendor/robthree/twofactorauth/lib/Algorithm.php @@ -0,0 +1,16 @@ +