97 lines
2.3 KiB
PHP
97 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
class PhysicalAttendanceGateCode
|
|
{
|
|
/** @var string */
|
|
protected $secret;
|
|
|
|
/** @var int */
|
|
protected $period;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->secret = (string) config('physical_attendance_gate.secret');
|
|
$this->period = max(30, (int) config('physical_attendance_gate.period_seconds', 60));
|
|
}
|
|
|
|
public function isConfigured()
|
|
{
|
|
return $this->secret !== '';
|
|
}
|
|
|
|
/**
|
|
* Current code and countdown for the counter display.
|
|
*
|
|
* @param int $electionId
|
|
* @return array{code:string,seconds_remaining:int,period_seconds:int}
|
|
*/
|
|
public function currentPayload($electionId)
|
|
{
|
|
$slot = $this->currentSlot();
|
|
$code = $this->codeForSlot((int) $electionId, $slot);
|
|
$expiresAt = ($slot + 1) * $this->period;
|
|
$secondsRemaining = max(0, $expiresAt - time());
|
|
|
|
return [
|
|
'code' => $code,
|
|
'seconds_remaining' => $secondsRemaining,
|
|
'period_seconds' => $this->period,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Accept current, previous, or next slot to tolerate minor clock skew.
|
|
*
|
|
* @param int $electionId
|
|
* @param string $input
|
|
* @return bool
|
|
*/
|
|
public function validates($electionId, $input)
|
|
{
|
|
if (!$this->isConfigured()) {
|
|
return false;
|
|
}
|
|
|
|
$normalized = strtoupper(preg_replace('/\s+/', '', (string) $input));
|
|
|
|
if (strlen($normalized) < 4) {
|
|
return false;
|
|
}
|
|
|
|
$slot = $this->currentSlot();
|
|
|
|
foreach ([$slot - 1, $slot, $slot + 1] as $s) {
|
|
if ($s < 0) {
|
|
continue;
|
|
}
|
|
if (hash_equals($this->codeForSlot((int) $electionId, $s), $normalized)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected function currentSlot()
|
|
{
|
|
return intdiv(time(), $this->period);
|
|
}
|
|
|
|
/**
|
|
* Deterministic code for one election and time slot.
|
|
*
|
|
* @param int $electionId
|
|
* @param int $slot
|
|
* @return string
|
|
*/
|
|
protected function codeForSlot($electionId, $slot)
|
|
{
|
|
$material = (string) $electionId.'|'.(string) $slot;
|
|
$hex = hash_hmac('sha256', $material, $this->secret);
|
|
|
|
return strtoupper(substr($hex, 0, 8));
|
|
}
|
|
}
|