【问题标题】:What's wrong with my implementation of 2 Factor Authorization?我实施 2 因素授权有什么问题?
【发布时间】:2020-04-30 17:57:46
【问题描述】:

我正在尝试实现我自己的 PHP 函数来为 Google Authenticator 生成代码。我这样做是为了好玩和学习新东西。这是我所做的:

function twoFactorAuthorizationCode(string $secretBase32, int $digitsCount): string {
    $counter = (int) (time() / 30);
    $secret = Base32::decode($secretBase32);
    $hash = hash_hmac('sha1', $counter, $secret, true); // 20 binary characters
    $hexHash = unpack('H*', $hash)[1]; // 40 hex characters
    $offset = hexdec($hexHash[-1]); // last 4 bits of $hash
    $truncatedHash = hexdec(substr($hexHash, $offset * 2, 8)) & 0x7fffffff; // last 31 bits
    $code = $truncatedHash % (10 ** $digitsCount);

    return str_pad($code, $digitsCount, '0', STR_PAD_LEFT);
}

我不确定哪一步是错误的,但它不会产生与 Google Authenticator 相同的结果。显然,我尝试使用时间偏移,以防我的时钟与 Google Authenticator 的时钟不同步。

我不确定的一些事情是:

  • 应该从 Base32 解码秘密,还是应该保留 Base32 字符串?
  • 计数器是 SHA1 哈希的值还是键?

我做了很多实验,但我的算法无法生成有效结果。非常感谢任何建议。

【问题讨论】:

  • 你能用spomky-labs/otphp代替自己写吗?
  • 我正在努力学习:)
  • @AlexHowansky 我终于知道出了什么问题。如果你有兴趣,请看下面我的回答。

标签: php authorization sha1 one-time-password google-authenticator


【解决方案1】:

我通过反复试验找到了答案。所以,问题出在我一直在直接散列的$counter 值中:

$hash = hash_hmac('sha1', $counter, $secret, true);

相反,它应该是由$counter 组成的 64 位二进制字符串:

$packedCounter = pack('J', $counter);
$hash = hash_hmac('sha1', $packedCounter, $secret, true);

说明

假设我们的 Unix 时间戳是 1578977176

这使得计数器如下:(int) (1578977176 / 30) = 52632572

用于散列的值必须是 64 位、大端字节序字符串。这意味着我们需要用零填充它以使其成为 64 位。

52632572 是二进制的11001000110001101111111100。那只是 26 位,所以我们还需要 38 位。我们现在拥有的是:

0000000000000000000000000000000000000011001000110001101111100010.

每个字符都是一个字节,所以我们将它分成 8 个组:

00000000 00000000 00000000 00000000 00000011 00100011 00011011 11100010

我们现在可以通过代码将每个组转换为一个字符:

$packedCounter = chr(0b00000000)
               . chr(0b00000000)
               . chr(0b00000000)
               . chr(0b00000000)
               . chr(0b00000011)
               . chr(0b00100011)
               . chr(0b00011011)
               . chr(0b11100010);

这就是我们要散列的字符串,这正是 pack('J', $string) 所做的。

瞧!

【讨论】:

    猜你喜欢
    • 2016-10-24
    • 2019-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-12
    • 1970-01-01
    • 2015-04-09
    • 2018-06-29
    相关资源
    最近更新 更多