Files
E-Vote/app/Services/PhysicalAttendanceGateCode.php
T
ISMAIL MASSERAN 3775c4a876 Allowance check
2026-04-14 02:34:00 +00:00

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));
}
}