v1.3.0
AtheraStudio SyncDrop
- <?php
- declare(strict_types=1);
-
- defined('SD_BOOT') || exit;
-
- const SD_B32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
-
- function sd_b32_encode(string $raw): string
- {
- $bits = '';
- foreach (str_split($raw) as $ch) $bits .= str_pad(decbin(ord($ch)), 8, '0', STR_PAD_LEFT);
- $out = '';
- foreach (str_split($bits, 5) as $chunk) $out .= SD_B32[bindec(str_pad($chunk, 5, '0'))];
- return $out;
- }
-
- function sd_b32_decode(string $text): string
- {
- $text = strtoupper(preg_replace('/[^A-Za-z2-7]/', '', $text));
- $bits = '';
- foreach (str_split($text) as $ch) {
- $pos = strpos(SD_B32, $ch);
- if ($pos === false) return '';
- $bits .= str_pad(decbin($pos), 5, '0', STR_PAD_LEFT);
- }
- $out = '';
- foreach (str_split($bits, 8) as $byte) {
- if (strlen($byte) === 8) $out .= chr(bindec($byte));
- }
- return $out;
- }
-
- function sd_totp_secret(): string
- {
- return sd_b32_encode(random_bytes(20));
- }
-
- function sd_hotp(string $key, int $counter, int $digits = 6): string
- {
- $hash = hash_hmac('sha1', pack('N2', ($counter >> 32) & 0xFFFFFFFF, $counter & 0xFFFFFFFF), $key, true);
- $offset = ord($hash[19]) & 0x0F;
- $value = ((ord($hash[$offset]) & 0x7F) << 24) | (ord($hash[$offset + 1]) << 16) | (ord($hash[$offset + 2]) << 8) | ord($hash[$offset + 3]);
- return str_pad((string)($value % (10 ** $digits)), $digits, '0', STR_PAD_LEFT);
- }
-
- function sd_totp_match(string $secret, string $code, int $afterStep = 0, ?int $now = null): ?int
- {
- $code = preg_replace('/\D/', '', $code);
- if (strlen($code) !== 6) return null;
- $key = sd_b32_decode($secret);
- if ($key === '') return null;
- $step = intdiv($now ?? time(), 30);
- foreach ([0, -1, 1] as $drift) {
- $s = $step + $drift;
- if ($s <= $afterStep) continue;
- if (hash_equals(sd_hotp($key, $s), $code)) return $s;
- }
- return null;
- }
-
- function sd_totp_uri(string $secret, string $account): string
- {
- $label = rawurlencode(SD_NAME) . ':' . rawurlencode($account);
- return 'otpauth://totp/' . $label . '?secret=' . $secret . '&issuer=' . rawurlencode(SD_NAME) . '&algorithm=SHA1&digits=6&period=30';
- }
-
- function sd_recovery_codes(int $count = 10): array
- {
- $codes = [];
- for ($i = 0; $i < $count; $i++) {
- $raw = strtolower(sd_b32_encode(random_bytes(8)));
- $codes[] = substr($raw, 0, 4) . '-' . substr($raw, 4, 4) . '-' . substr($raw, 8, 4);
- }
- return $codes;
- }
-
- function sd_recovery_normalize(string $code): string
- {
- return strtolower(preg_replace('/[^A-Za-z2-7]/', '', $code));
- }
-