WebOrbiton
v1.3.0

AtheraStudio SyncDrop

81 lines · 2.5 KB
  1. <?php
  2. declare(strict_types=1);
  3. ​
  4. defined('SD_BOOT') || exit;
  5. ​
  6. const SD_B32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
  7. ​
  8. function sd_b32_encode(string $raw): string
  9. {
  10. $bits = '';
  11. foreach (str_split($raw) as $ch) $bits .= str_pad(decbin(ord($ch)), 8, '0', STR_PAD_LEFT);
  12. $out = '';
  13. foreach (str_split($bits, 5) as $chunk) $out .= SD_B32[bindec(str_pad($chunk, 5, '0'))];
  14. return $out;
  15. }
  16. ​
  17. function sd_b32_decode(string $text): string
  18. {
  19. $text = strtoupper(preg_replace('/[^A-Za-z2-7]/', '', $text));
  20. $bits = '';
  21. foreach (str_split($text) as $ch) {
  22. $pos = strpos(SD_B32, $ch);
  23. if ($pos === false) return '';
  24. $bits .= str_pad(decbin($pos), 5, '0', STR_PAD_LEFT);
  25. }
  26. $out = '';
  27. foreach (str_split($bits, 8) as $byte) {
  28. if (strlen($byte) === 8) $out .= chr(bindec($byte));
  29. }
  30. return $out;
  31. }
  32. ​
  33. function sd_totp_secret(): string
  34. {
  35. return sd_b32_encode(random_bytes(20));
  36. }
  37. ​
  38. function sd_hotp(string $key, int $counter, int $digits = 6): string
  39. {
  40. $hash = hash_hmac('sha1', pack('N2', ($counter >> 32) & 0xFFFFFFFF, $counter & 0xFFFFFFFF), $key, true);
  41. $offset = ord($hash[19]) & 0x0F;
  42. $value = ((ord($hash[$offset]) & 0x7F) << 24) | (ord($hash[$offset + 1]) << 16) | (ord($hash[$offset + 2]) << 8) | ord($hash[$offset + 3]);
  43. return str_pad((string)($value % (10 ** $digits)), $digits, '0', STR_PAD_LEFT);
  44. }
  45. ​
  46. function sd_totp_match(string $secret, string $code, int $afterStep = 0, ?int $now = null): ?int
  47. {
  48. $code = preg_replace('/\D/', '', $code);
  49. if (strlen($code) !== 6) return null;
  50. $key = sd_b32_decode($secret);
  51. if ($key === '') return null;
  52. $step = intdiv($now ?? time(), 30);
  53. foreach ([0, -1, 1] as $drift) {
  54. $s = $step + $drift;
  55. if ($s <= $afterStep) continue;
  56. if (hash_equals(sd_hotp($key, $s), $code)) return $s;
  57. }
  58. return null;
  59. }
  60. ​
  61. function sd_totp_uri(string $secret, string $account): string
  62. {
  63. $label = rawurlencode(SD_NAME) . ':' . rawurlencode($account);
  64. return 'otpauth://totp/' . $label . '?secret=' . $secret . '&issuer=' . rawurlencode(SD_NAME) . '&algorithm=SHA1&digits=6&period=30';
  65. }
  66. ​
  67. function sd_recovery_codes(int $count = 10): array
  68. {
  69. $codes = [];
  70. for ($i = 0; $i < $count; $i++) {
  71. $raw = strtolower(sd_b32_encode(random_bytes(8)));
  72. $codes[] = substr($raw, 0, 4) . '-' . substr($raw, 4, 4) . '-' . substr($raw, 8, 4);
  73. }
  74. return $codes;
  75. }
  76. ​
  77. function sd_recovery_normalize(string $code): string
  78. {
  79. return strtolower(preg_replace('/[^A-Za-z2-7]/', '', $code));
  80. }
  81. ​