index/src/Security/CSRFP.php

105 lines
3.1 KiB
PHP
Raw Normal View History

2022-09-13 13:13:11 +00:00
<?php
// CSRFP.php
// Created: 2021-06-11
// Updated: 2023-07-11
2022-09-13 13:13:11 +00:00
namespace Index\Security;
use Index\Serialisation\Serialiser;
2022-09-13 13:13:11 +00:00
/**
* Contains a mechanism for validating requests.
2022-09-13 13:13:11 +00:00
*/
class CSRFP {
private const TOLERANCE = 30 * 60;
private const EPOCH = 1682985600;
private const HASH_ALGO = 'sha3-256';
private const TIMESTAMP_LENGTH = 4;
private const NONCE_LENGTH = 8;
private const HASH_LENGTH = 32;
2022-09-13 13:13:11 +00:00
private string $secretKey;
private string $identity;
2022-09-13 13:13:11 +00:00
private int $tolerance;
/**
* Creates a new CSRFP instance.
*
* @param string $secretKey Secret key for HMAC hashes.
* @param string $identity Unique identity component for these tokens.
* @param int $tolerance Default amount of time a token should remain valid for.
2022-09-13 13:13:11 +00:00
* @return CSRFP New CSRFP instance.
*/
public function __construct(
string $secretKey,
string $identity,
int $tolerance = self::TOLERANCE
2022-09-13 13:13:11 +00:00
) {
$this->secretKey = $secretKey;
$this->identity = $identity;
2022-09-13 13:13:11 +00:00
$this->tolerance = $tolerance;
}
private static function time(int $time = -1): int {
return ($time < 0 ? time() : $time) - self::EPOCH;
2022-09-13 13:13:11 +00:00
}
private function createHash(int $time, string $nonce): string {
return hash_hmac(self::HASH_ALGO, "{$this->identity}!{$time}!{$nonce}", $this->secretKey, true);
2022-09-13 13:13:11 +00:00
}
/**
* Creates a token string.
2022-09-13 13:13:11 +00:00
*
* @param int $time Timestamp to generate the token for, -1 (default) for now.
2022-09-13 13:13:11 +00:00
* @return string Token string.
*/
public function createToken(int $time = -1): string {
$time = self::time($time);
$nonce = random_bytes(self::NONCE_LENGTH);
$hash = $this->createHash($time, $nonce);
return Serialiser::uriBase64()->serialise(
pack('V', $time) . $nonce . $hash
);
2022-09-13 13:13:11 +00:00
}
/**
* Verifies a token string.
2022-09-13 13:13:11 +00:00
*
* @param string $token Token to test.
* @param int $tolerance Amount of seconds for which the token can remain valid, < 0 for whatever the default value is.
* @param int $time Point in time for which to check validity for this time.
2022-09-13 13:13:11 +00:00
* @return bool true if the token is valid, false if not.
*/
public function verifyToken(string $token, int $tolerance = -1, int $time = -1): bool {
if($tolerance === 0)
return false;
if($tolerance < 0)
$tolerance = $this->tolerance;
$token = Serialiser::uriBase64()->deserialise($token);
if(empty($token))
return false;
$uTime = -1;
extract(unpack('VuTime', $token));
if($uTime < 0)
return false;
$uNonce = substr($token, self::TIMESTAMP_LENGTH, self::NONCE_LENGTH);
if(empty($uNonce))
return false;
$uHash = substr($token, self::TIMESTAMP_LENGTH + self::NONCE_LENGTH, self::HASH_LENGTH);
if(empty($uHash))
return false;
$rTime = self::time($time);
return ($rTime - ($uTime + $tolerance)) < 0
&& hash_equals($this->createHash($uTime, $uNonce), $uHash);
2022-09-13 13:13:11 +00:00
}
}