diff --git a/.gitignore b/.gitignore index 964f854..87b561b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /composer.lock /.idea /.vscode -phpunit.xml \ No newline at end of file +phpunit.xml +.phpunit.result.cache \ No newline at end of file diff --git a/Random.php b/Random.php index 7078010..1440072 100644 --- a/Random.php +++ b/Random.php @@ -45,14 +45,55 @@ public function generate(): string $char = $this->char ?: self::CHAR; $length = $this->length ?: self::LENGTH; - $pieces = []; + $pieces = self::strBaseTime($char, 6); $max = mb_strlen($char, '8bit') - 1; for ($i = 0; $i < $length; ++$i) { - $pieces [] = $char[random_int(0, $max)]; + $pieces .= $char[random_int(0, $max)]; } - return implode('', $pieces); + return substr($pieces, 0, $length); + } + + /** + * @throws Exception + */ + private function strBaseTime(string $char, int $length): string + { + $grpInt = self::grpIntBaseTime($length); + $result = ''; + + for ($i = 0; $i < $length; ++$i) { + $result .= $char[(int)$grpInt[$i]] ?? ''; + } + + return $result; + } + + /** + * @throws Exception + */ + private function grpIntBaseTime(int $length): string + { + $timecodes = time() - 60; + $result = []; + + while ($timecodes !== 0) { + $result[] = chr($timecodes & 0xFF); + $timecodes >>= 8; + } + + $data= str_pad(implode('', array_reverse($result)), 8, "\000", STR_PAD_LEFT); + $hash = hash_hmac('sha256', $data, $data, true); + $unpacked = unpack('C*', $hash); + $unpacked !== false || throw new \Exception('Invalid data.'); + $hmac = array_values($unpacked); + + $offset = ($hmac[count($hmac) - 1] & 0xF); + $code = ($hmac[$offset] & 0x7F) << 24 | ($hmac[$offset + 1] & 0xFF) << 16 | ($hmac[$offset + 2] & 0xFF) << 8 | ($hmac[$offset + 3] & 0xFF); + $otp = $code % (10 ** $length); + + return str_pad((string) $otp, $length, '0', STR_PAD_LEFT); } /** diff --git a/composer.json b/composer.json index fe2ed3b..5ea94d6 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ ], "minimum-stability": "dev", "require": { - "php": ">=8.0" + "php": ">=8.0", + "ext-mbstring": "*" }, "extra": { "branch-alias": {