如何在 python 中将 base32 与 hotp(一次性密码)结合使用?
How to use base32 in combination with hotp (one time passwords) in python?
为了大学练习,我想在 python 中开发一个简单的 hotp 服务器-客户端系统。在这种情况下,客户端向服务器发送一个密码和一个一次性密码。服务器知道秘密,计算当前 hotp 并比较它接收到的值。到目前为止,一切都很好。对于明文,这非常有效,计算值与我使用 iOS App "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
首先,重要的是要指出 secret.encode('UTF-8')
的结果与 base64.b32encode(bSecret)
(就此而言 base64.b64encode(bSecret)
)的结果具有完全相同的类型——它们都是return bytes
个对象。另外值得注意的是Python中的implementation of hmac
没有提到base64/base32编码。所以简短的回答是,只有当共享秘密是 base64/UTF-8 编码的 blob 时,807244
的预期结果才有效。
这个简短的片段表明,你真的可以给 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,我很乐意修改此答案。
发现错误:
秘密必须是 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)
为了大学练习,我想在 python 中开发一个简单的 hotp 服务器-客户端系统。在这种情况下,客户端向服务器发送一个密码和一个一次性密码。服务器知道秘密,计算当前 hotp 并比较它接收到的值。到目前为止,一切都很好。对于明文,这非常有效,计算值与我使用 iOS App "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
首先,重要的是要指出 secret.encode('UTF-8')
的结果与 base64.b32encode(bSecret)
(就此而言 base64.b64encode(bSecret)
)的结果具有完全相同的类型——它们都是return bytes
个对象。另外值得注意的是Python中的implementation of hmac
没有提到base64/base32编码。所以简短的回答是,只有当共享秘密是 base64/UTF-8 编码的 blob 时,807244
的预期结果才有效。
这个简短的片段表明,你真的可以给 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,我很乐意修改此答案。
发现错误: 秘密必须是 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)