Skip to content

Commit

Permalink
add test;
Browse files Browse the repository at this point in the history
update README.md;
  • Loading branch information
sometiny committed Oct 11, 2024
1 parent 8be75b0 commit 4a20745
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 43 deletions.
59 changes: 25 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
# totp-php
RFC: [TOTP: Time-Based One-Time Password Algorithm](https://datatracker.ietf.org/doc/html/rfc6238)

### direct-call
### Uri
```php
$uri = new Uri('otpauth://totp/test_account:your-site.com?secret=Y5C4TFC5Q6OZHMXS7NOEDO5AYUP5XWMK&algorithm=sha1&digits=6&period=30');

$secret = $uri->getSecret();

echo 'key = ' . $secret . "\r\n";
// output:
// key = Y5C4TFC5Q6OZHMXS7NOEDO5AYUP5XWMK

$uri = new Uri();
$uri->setLabel('test');
$uri->setType('totp'); //default is 'totp'
$uri->setSecret('Y5C4TFC5Q6OZHMXS7NOEDO5AYUP5XWMK');

echo 'totp uri = ' . $uri . "\r\n";
// output:
// totp uri = otpauth://totp/test?secret=Y5C4TFC5Q6OZHMXS7NOEDO5AYUP5XWMK

```
### 快速调用
默认为30秒步长,6个数字的验证码,使用SHA1算法。
```php
echo TOTP::generate('Y5C4TFC5Q6OZHMXS7NOEDO5AYUP5XWMK', time());
```

### test
### 更多测试
```php
// Seed for HMAC-SHA1 - 20 bytes
$seed = Base32::decode("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ");
Expand Down Expand Up @@ -40,26 +61,7 @@ for ($t = 0; $t < count($testTime); $t++) {
TOTP::compute($seed64, $testTime[$t], 'sha512', 8, $X, $T0));
}

$tests = 'foobar';
for ($i = 1; $i <= mb_strlen($tests, 'utf-8'); $i++){
$str = mb_substr($tests, 0, $i, 'utf-8');
$encoded = Base32::encode($str);
$decoded = Base32::decode($encoded);
$equal = $decoded === $str ? 'yes' : 'no';
echo "{$str} => {$encoded} => {$decoded} => {$equal}\r\n";
}

for ($i = 1; $i <= mb_strlen($tests, 'utf-8'); $i++){
$str = mb_substr($tests, 0, $i, 'utf-8');
$encoded = Base32::encode($str, Base32::base32_encode_lookup_table_hex);
$decoded = Base32::decode($encoded, Base32::base32_decode_lookup_table_hex);
$equal = $decoded === $str ? 'yes' : 'no';
echo "{$str} => {$encoded} => {$decoded} => {$equal}\r\n";
}
```

### test-result
```
/* 输出结果:
time: 59, date: 1970-01-01 00:00:59, code: 94287082, alg: SHA1
time: 59, date: 1970-01-01 00:00:59, code: 46119246, alg: SHA256
time: 59, date: 1970-01-01 00:00:59, code: 90693936, alg: SHA512
Expand All @@ -78,17 +80,6 @@ time: 2000000000, date: 2033-05-18 03:33:20, code: 38618901, alg: SHA512
time: 20000000000, date: 2603-10-11 11:33:20, code: 65353130, alg: SHA1
time: 20000000000, date: 2603-10-11 11:33:20, code: 77737706, alg: SHA256
time: 20000000000, date: 2603-10-11 11:33:20, code: 47863826, alg: SHA512
f => MY====== => f => yes
fo => MZXQ==== => fo => yes
foo => MZXW6=== => foo => yes
foob => MZXW6YQ= => foob => yes
fooba => MZXW6YTB => fooba => yes
foobar => MZXW6YTBOI====== => foobar => yes
f => CO====== => f => yes
fo => CPNG==== => fo => yes
foo => CPNMU=== => foo => yes
foob => CPNMUOG= => foob => yes
fooba => CPNMUOJ1 => fooba => yes
foobar => CPNMUOJ1E8====== => foobar => yes
*/
```

8 changes: 4 additions & 4 deletions src/Base32.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@ public static function encode(string $binary, array $table = self::base32_encode


$a = $x >> 3;
$b = (($x & 0x7) << 8 | $y ) >> 6;
$b = (($x & 0x7) << 8 | $y) >> 6;
$c = $y >> 1 & 0x1f;
$d = (($y & 0x1) << 8 | $z ) >> 4;
$e = (($z & 0xf) << 8 | $m ) >> 7;
$d = (($y & 0x1) << 8 | $z) >> 4;
$e = (($z & 0xf) << 8 | $m) >> 7;

$f = $m >> 2 & 0x1f;

$g = (($m & 0x3) << 8 | $n ) >> 5;
$g = (($m & 0x3) << 8 | $n) >> 5;

$h = $n & 0x1f;

Expand Down
3 changes: 2 additions & 1 deletion src/TOTP.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class TOTP
public const RETURN_LENGTH = 6;

private const DIGITS_POWER = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000];

/**
* make quick totp
* @param string $key
Expand Down Expand Up @@ -90,7 +91,7 @@ public static function hotp(
int $return_length = self::RETURN_LENGTH
): string
{
if(strlen($counter) !== 8) throw new \Exception('invalid counter, length must be 8');
if (strlen($counter) !== 8) throw new \Exception('invalid counter, length must be 8');

$hash = hash_hmac($algorithm, $counter, $key, true);

Expand Down
12 changes: 8 additions & 4 deletions src/Uri.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Uri
private string $secret = '';
private array $label_components = [];

public function __construct(?string $url)
public function __construct(?string $url = null)
{
if (empty($url)) return;
$isMatch = preg_match('#^otpauth://(totp|hotp)/(.+?)\?(.+?)$#', $url, $match);
Expand Down Expand Up @@ -208,11 +208,15 @@ public function getBinarySecret(): string

public function __toString()
{
if (empty($this->label)) throw new \Exception('label for \'hotp|totp\' required');
if (empty($this->secret)) throw new \Exception('secret for \'hotp|totp\' required');
if ($this->type === 'hotp' && empty($this->counter)) throw new \Exception('counter for \'hotp\' required');

$components = ['otpauth://', $this->type, '/', urlencode($this->label), '?secret=', urlencode($this->secret)];
if($this->algorithm !== 'sha1') array_push($components, '&algorithm=', $this->algorithm);
if ($this->algorithm !== 'sha1') array_push($components, '&algorithm=', $this->algorithm);
if (!empty($this->counter)) array_push($components, '&counter=', $this->counter);
if($this->digits !== 6) array_push($components, '&digits=', $this->digits);
if($this->period !== 30) array_push($components, '&period=', $this->period);
if ($this->digits !== 6) array_push($components, '&digits=', $this->digits);
if ($this->period !== 30) array_push($components, '&period=', $this->period);
if (!empty($this->issuer)) array_push($components, '&issuer=', urlencode($this->issuer));
return implode('', $components);
}
Expand Down
86 changes: 86 additions & 0 deletions test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php
include_once './src/Base32.php';
include_once './src/TOTP.php';
include_once './src/Uri.php';
use Jazor\OTP\Uri;
use Jazor\OTP\TOTP;
use Jazor\OTP\Base32;

/**
* parse uri
*/
$uri = new Uri('otpauth://totp/test_account:your-site.com?secret=Y5C4TFC5Q6OZHMXS7NOEDO5AYUP5XWMK&algorithm=sha1&digits=6&period=30');

$secret = $uri->getSecret();

echo 'key = ' . $secret . "\r\n";

//根据key和当前时间计算验证码
echo 'verify code = ' . TOTP::generate($secret, time());
echo "\r\n";

/**
* generate uri
*/
$uri = new Uri();
$uri->setLabel('test');
$uri->setType('totp'); //default is 'totp'
$uri->setSecret('Y5C4TFC5Q6OZHMXS7NOEDO5AYUP5XWMK');

echo 'totp uri = ' . $uri . "\r\n";

//测试
// Seed for HMAC-SHA1 - 20 bytes
$seed = Base32::decode("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ");
// Seed for HMAC-SHA256 - 32 bytes
$seed32 = Base32::decode("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZA====");
// Seed for HMAC-SHA512 - 64 bytes
$seed64 = Base32::decode("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNA=");
$T0 = 0;
$X = 30;
$testTime = [59, 1111111109, 1111111111, 1234567890, 2000000000, 20000000000];

ini_set('date.timezone', 'UTC');
for ($t = 0; $t < count($testTime); $t++) {
echo sprintf(
"time: %s, date: %s, code: %s, alg: SHA1\r\n",
$testTime[$t],
date('Y-m-d H:i:s', $testTime[$t]),
TOTP::compute($seed, $testTime[$t], 'sha1', 8, $X, $T0));


echo sprintf(
"time: %s, date: %s, code: %s, alg: SHA256\r\n",
$testTime[$t],
date('Y-m-d H:i:s', $testTime[$t]),
TOTP::compute($seed32, $testTime[$t], 'sha256', 8, $X, $T0));

echo sprintf(
"time: %s, date: %s, code: %s, alg: SHA512\r\n",
$testTime[$t],
date('Y-m-d H:i:s', $testTime[$t]),
TOTP::compute($seed64, $testTime[$t], 'sha512', 8, $X, $T0));
}

/*
输出:
time: 59, date: 1970-01-01 00:00:59, code: 94287082, alg: SHA1
time: 59, date: 1970-01-01 00:00:59, code: 46119246, alg: SHA256
time: 59, date: 1970-01-01 00:00:59, code: 90693936, alg: SHA512
time: 1111111109, date: 2005-03-18 01:58:29, code: 07081804, alg: SHA1
time: 1111111109, date: 2005-03-18 01:58:29, code: 68084774, alg: SHA256
time: 1111111109, date: 2005-03-18 01:58:29, code: 25091201, alg: SHA512
time: 1111111111, date: 2005-03-18 01:58:31, code: 14050471, alg: SHA1
time: 1111111111, date: 2005-03-18 01:58:31, code: 67062674, alg: SHA256
time: 1111111111, date: 2005-03-18 01:58:31, code: 99943326, alg: SHA512
time: 1234567890, date: 2009-02-13 23:31:30, code: 89005924, alg: SHA1
time: 1234567890, date: 2009-02-13 23:31:30, code: 91819424, alg: SHA256
time: 1234567890, date: 2009-02-13 23:31:30, code: 93441116, alg: SHA512
time: 2000000000, date: 2033-05-18 03:33:20, code: 69279037, alg: SHA1
time: 2000000000, date: 2033-05-18 03:33:20, code: 90698825, alg: SHA256
time: 2000000000, date: 2033-05-18 03:33:20, code: 38618901, alg: SHA512
time: 20000000000, date: 2603-10-11 11:33:20, code: 65353130, alg: SHA1
time: 20000000000, date: 2603-10-11 11:33:20, code: 77737706, alg: SHA256
time: 20000000000, date: 2603-10-11 11:33:20, code: 47863826, alg: SHA512
*/
?>

0 comments on commit 4a20745

Please sign in to comment.