<?php
# Time-based One-Time Password algorithm (RFC 6238)
function calc_totp(
string $secretkey,
int $codelen = 6,
int $epoch = 0,
int $timestep = 30,
?int $timestamp = null,
$hashfunc = "sha1"
): string {
if ($timestamp === null) {
$timestamp = time();
}
assert(is_int($epoch));
assert(is_int($timestep));
assert(is_int($timestamp));
return calc_hotp(
$secretkey,
pack("N*", 0, $timestamp - $epoch - ($timestamp - $epoch) % $timestep),
$codelen,
$hashfunc
);
}
# HMAC-based One-Time Password algorithm (RFC 4226)
function calc_hotp(
string $secretkey,
string $counter,
int $codelen = 6,
$hashfunc = "sha1"
): string {
assert(is_string($secretkey));
assert(is_string($counter));
assert(is_int($codelen) && 1 <= $codelen && $codelen <= 9);
$hash = hash_hmac($hashfunc, $counter, $secretkey, true);
$offset = ord(substr($hash, -1)) & 0xf;
$val = unpack("N*", substr($hash, $offset, 4))[1] & 0x7fffffff;
return str_pad((string)($val % (10 ** $codelen)), $codelen, "0", STR_PAD_LEFT);
}