mirror of
https://github.com/openvk/commitcaptcha.git
synced 2024-11-14 19:19:09 +03:00
121 lines
3.8 KiB
PHP
121 lines
3.8 KiB
PHP
<?php declare(strict_types=1);
|
|
namespace commitcaptcha;
|
|
use Nette\Utils\Image;
|
|
use Chandler\Session\Session;
|
|
use Chandler\Patterns\TSimpleSingleton;
|
|
|
|
class CaptchaManager
|
|
{
|
|
private $session;
|
|
|
|
private function __construct()
|
|
{
|
|
$this->session = Session::i();
|
|
}
|
|
|
|
private function generateColor(bool $maxOutRed = false): array
|
|
{
|
|
return [
|
|
"red" => $maxOutRed ? 255 : random_int(0, 150),
|
|
"green" => random_int(0, 100),
|
|
"blue" => random_int(0, 150),
|
|
"alpha" => 0,
|
|
];
|
|
}
|
|
|
|
private function generateCode(): string
|
|
{
|
|
return mb_strtoupper(str_replace("/", "+", base64_encode(openssl_random_pseudo_bytes(6))));
|
|
}
|
|
|
|
private function applyNoiseOnImage(Image $image, \Closure $fun): void
|
|
{
|
|
$_noise = (function() use ($image) {
|
|
foreach(range(1, 33) as $x)
|
|
foreach(range(1, 13) as $y)
|
|
$image->setPixel($x * 5, $y * 5, $this->generateColor());
|
|
});
|
|
|
|
$_noise();
|
|
$fun();
|
|
$_noise();
|
|
}
|
|
|
|
private function applyLinesOnImage(Image $image, \Closure $fun): void
|
|
{
|
|
$_lines = (function() use ($image) {
|
|
foreach(range(1, random_int(2, 6)) as $i)
|
|
$image->line(random_int(0, 15), random_int(0, 65), random_int(150, 165), random_int(0, 65), $this->generateColor());
|
|
});
|
|
|
|
$_lines();
|
|
$fun();
|
|
$_lines();
|
|
}
|
|
|
|
private function generateCaptchaImage(string $code): Image
|
|
{
|
|
$image = Image::fromBlank(165, 65, $this->generateColor());
|
|
|
|
$this->applyNoiseOnImage($image, function() use ($image, $code) {
|
|
$this->applyLinesOnImage($image, function() use ($image, $code) {
|
|
$length = iconv_strlen($code);
|
|
$offset = 165 / $length;
|
|
|
|
for($i = 0; $i < $length; $i++) {
|
|
$letter = $code[$i];
|
|
$font = __DIR__ . "/data/san-francissco.ttf";
|
|
$x = (int) ceil(0 + ($offset * $i));
|
|
$y = random_int(45, 55);
|
|
|
|
$image->ttfText(random_int(18, 28), random_int(-10, 10), $x, $y, $this->generateColor(true), $font, $letter);
|
|
}
|
|
});
|
|
});
|
|
|
|
return $image;
|
|
}
|
|
|
|
function getImage(): Image
|
|
{
|
|
$code = $this->generateCode();
|
|
$hash = hash("crc32b", mb_strtolower($code));
|
|
$image = $this->generateCaptchaImage($code);
|
|
|
|
$nonce = bin2hex(openssl_random_pseudo_bytes(SODIUM_CRYPTO_STREAM_NONCEBYTES / 2));
|
|
$key = substr(CHANDLER_ROOT_CONF["security"]["secret"], 0, SODIUM_CRYPTO_STREAM_KEYBYTES);
|
|
$encHash = sodium_crypto_stream_xor($hash, $nonce, $key);
|
|
$this->session->set("captcha", implode(":", [
|
|
time(),
|
|
$nonce,
|
|
base64_encode($encHash),
|
|
]));
|
|
|
|
return $image;
|
|
}
|
|
|
|
function verifyCaptcha(?string $input = NULL): bool
|
|
{
|
|
if(!COMMITCAPTCHA_ROOT_CONF["commitcaptcha"]["enable"])
|
|
return true;
|
|
|
|
if(!$input)
|
|
$input = $_POST["captcha"];
|
|
|
|
$data = $this->session->get("captcha");
|
|
if(!$data)
|
|
return false;
|
|
|
|
$this->session->set("captcha", "");
|
|
|
|
[$time, $nonce, $encHash] = explode(":", $data);
|
|
if((time() - $time) > 3600)
|
|
return false;
|
|
|
|
$key = substr(CHANDLER_ROOT_CONF["security"]["secret"], 0, SODIUM_CRYPTO_STREAM_KEYBYTES);
|
|
$hash = sodium_crypto_stream_xor(base64_decode($encHash), $nonce, $key);
|
|
return hash_equals(hash("crc32b", mb_strtolower($input)), $hash);
|
|
}
|
|
|
|
use TSimpleSingleton;
|
|
}
|