【问题标题】:How to use base32 in combination with hotp (one time passwords) in python?如何在python中结合使用base32和hotp(一次性密码)?
【发布时间】:2019-10-23 04:23:05
【问题描述】:

为了一个大学练习,我想用 python 开发一个简单的 hotp 服务器-客户端系统。在这种情况下,客户端向服务器发送密码和一次性密码。服务器知道秘密,计算当前的热点并比较它收到的值。到目前为止,一切都很好。使用纯文本可以很好地工作,并且计算的值与我使用 iOS 应用程序“OTP Auth”时得到的值相同。但也有可能结合 base32 计算 OTP。所以我添加了几行将明文编码为base32,但现在输出不正确。

假设我们使用秘密“1234”,所以明文输出将是“110366”。那行得通。但是,如果我将秘密编码为 base32,则输出应该是“807244”,但我的程序计算的是“896513”。有人知道为什么会这样吗?

我已经尝试使用不同的秘密,并在不同的应用程序上进行了检查。总是一样的结果。

import hmac
import hashlib
import array
import base64

counter = 0
digits = 6                      #Anzahl der Zeichen

def hotp(secret, c):
    global digits
    counter = extendCounter(c)
    hmac_sha1 = hmac.new(secret, counter, hashlib.sha1).hexdigest()
    return truncate(hmac_sha1)[-digits:]


def truncate(hmac_sha1):
    offset = int(hmac_sha1[-1], 16)
    binary = int(hmac_sha1[(offset * 2):((offset * 2) + 8)], 16) & 0x7fffffff
    return str(binary)


def extendCounter(long_num):
    byte_array = array.array('B')
    for i in reversed(range(0, 8)):
        byte_array.insert(0, long_num & 0xff)
        long_num >>= 8
    return byte_array


def main():
    secret = "1234"
    bSecret = secret.encode("UTF-8")
    bSecret = base64.b32encode(bSecret)
    otp = hotp(bSecret, counter)
    one_time_password = otp

我希望输出为 807244,但输出为 896513

【问题讨论】:

  • 在您的主函数中,第三行 bSecret base64.b32encode(bSecret) 没有赋值运算符 (=)。您正在测试的代码中是否存在拼写错误?
  • @AndrewF 对不起。那是一个错字。

标签: python python-3.x hmac one-time-password base32


【解决方案1】:

首先,重要的是要指出secret.encode('UTF-8') 的结果与base64.b32encode(bSecret) 的结果具有完全相同的类型(就此而言base64.b64encode(bSecret))——它们都返回bytes 对象。另外值得注意的是,Python 中的implementation of hmac 没有提到base64/base32 编码。所以简短的回答是,您的 807244 的预期结果仅在共享密钥是 base64/UTF-8 编码的 blob 时才有效。

这个快速的 sn-p 表明你真的可以给hotp 任何你喜欢的字节,它会产生一些结果(因为hotp 在例子中被多次调用,counter 被改变了)

# ... everything from your example above ...
secret = "1234"
secret_bytes = secret.encode("UTF-8")
secret_bytes
>>> b'1234'
b32_secret = base64.b32encode(bSecret)
b32_secret
>>> b'GEZDGNA='
b64_secret = base64.b64encode(bSecret)
b64_secret
>>> b'MTIzNA=='
hotp(secret_bytes, counter)  # just a UTF-8 blob works
>>> '110366'
hotp(b32_secret, counter)  # base32/UTF-8 also works
>>> '896513'
hotp(b64_secret, counter)  # base64/UTF-8 works as well
>>> '806744'

如果您有更多详细信息说明您为什么期望 807244 用于 base32/UTF8 blob,我将很乐意修改此答案。

【讨论】:

  • 首先非常感谢您的回答!我预计结果为 807244,因为这就是我的应用程序所说的。我想用移动设备的 HOTP 生成器检查我的结果。为此,我正在使用 iOS 应用程序“OTP Auth”。 “1234”只是一个例子。
  • 我刚刚将结果与安卓应用 FreeOTP 进行了比较。这个应用程序以 -1 作为计数器开始很奇怪,但它给出了相同的结果。
【解决方案2】:

发现错误: 不是将秘密转换为 base32,秘密必须是 Base32 解码值。也不是编码这个值,它必须被解码 ("base64.b32decode(bytes(saved_secret, 'utf-8'))")

所以正确的 main 看起来像这样:

def main():
    secret = "V6X27L5P" #Base32 value
    secret = base64.b32decode(bytes(secret, 'utf-8'))
    one_time_password = hotp(secret, counter)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-17
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 2013-10-15
    • 2013-04-23
    • 1970-01-01
    • 2011-08-22
    相关资源
    最近更新 更多