-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
381 additions
and
0 deletions.
There are no files selected for viewing
56 changes: 56 additions & 0 deletions
56
lib/vendor/bacon/bacon-qr-code/src/Renderer/Eye/PointyEye.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
declare(strict_types = 1); | ||
|
||
namespace BaconQrCode\Renderer\Eye; | ||
|
||
use BaconQrCode\Renderer\Path\Path; | ||
|
||
/** | ||
* Renders the outer eye as solid with a curved corner and inner eye as a circle. | ||
*/ | ||
final class PointyEye implements EyeInterface | ||
{ | ||
/** | ||
* @var self|null | ||
*/ | ||
private static $instance; | ||
|
||
private function __construct() | ||
{ | ||
} | ||
|
||
public static function instance() : self | ||
{ | ||
return self::$instance ?: self::$instance = new self(); | ||
} | ||
|
||
public function getExternalPath() : Path | ||
{ | ||
return (new Path()) | ||
->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() | ||
; | ||
} | ||
} |
238 changes: 238 additions & 0 deletions
238
lib/vendor/bacon/bacon-qr-code/src/Renderer/GDLibRenderer.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace BaconQrCode\Renderer; | ||
|
||
use BaconQrCode\Encoder\ByteMatrix; | ||
use BaconQrCode\Encoder\MatrixUtil; | ||
use BaconQrCode\Encoder\QrCode; | ||
use BaconQrCode\Exception\InvalidArgumentException; | ||
use BaconQrCode\Exception\RuntimeException; | ||
use BaconQrCode\Renderer\Color\Alpha; | ||
use BaconQrCode\Renderer\Color\ColorInterface; | ||
use BaconQrCode\Renderer\RendererStyle\EyeFill; | ||
use BaconQrCode\Renderer\RendererStyle\Fill; | ||
use GdImage; | ||
|
||
final class GDLibRenderer implements RendererInterface | ||
{ | ||
private ?GdImage $image; | ||
|
||
/** | ||
* @var array<string, int> | ||
*/ | ||
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]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace RobThree\Auth; | ||
|
||
/** | ||
* List of supported cryptographic algorithms | ||
*/ | ||
enum Algorithm: string | ||
{ | ||
case Md5 = 'md5'; | ||
case Sha1 = 'sha1'; | ||
case Sha256 = 'sha256'; | ||
case Sha512 = 'sha512'; | ||
} |