Google身份validation器在Python中的实现

我正在尝试使用Google身份validation器应用程序生成的一次性密码。

Google身份validation器的function

Google Authenticator基本上实现了两种types的密码:

  • HOTP – 基于HMAC的一次性密码,这意味着每次通话都会改变密码,符合RFC4226 ,
  • TOTP – 基于时间的一次性密码,每30秒改变一次(据我所知)。

Google身份validation器也可在此处以开放源代码的forms获得: code.google.com/p/google-authenticator

当前的代码

我正在寻找现有的解决scheme来生成HOTP和TOTP密码,但没有find太多。 我的代码是负责生成HOTP的以下片段:

import hmac, base64, struct, hashlib, time def get_token(secret, digest_mode=hashlib.sha1, intervals_no=None): if intervals_no == None: intervals_no = int(time.time()) // 30 key = base64.b32decode(secret) msg = struct.pack(">Q", intervals_no) h = hmac.new(key, msg, digest_mode).digest() o = ord(h[19]) & 15 h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000 return h 

我面临的问题是,我使用上面的代码生成的密码是不一样的使用Android的谷歌身份validation器应用程序生成的。 即使我尝试了多个intervals_no值(正好是第一个10000,开始于intervals_no = 0 ), secret等于GA应用程序中提供的密钥。

我有问题

我的问题是:

  1. 我究竟做错了什么?
  2. 如何在Python中生成HOTP和/或TOTP?
  3. 有没有任何现有的Python库?

总结一下:请给我任何线索,帮助我在我的Python代码中实现Google Authenticator身份validation。

我想在我的问题上设置一个奖励,但我成功地创造了解决scheme。 我的问题似乎与不正确的密钥值(它必须是base64.b32decode()函数的正确参数)连接。

下面我发布全面的工作解决scheme,并解释如何使用它。

下面的代码就够了。 我也上传到GitHub作为单独的模块onetimepass (可在这里: https : //github.com/tadeck/onetimepass )。

 import hmac, base64, struct, hashlib, time def get_hotp_token(secret, intervals_no): key = base64.b32decode(secret, True) msg = struct.pack(">Q", intervals_no) h = hmac.new(key, msg, hashlib.sha1).digest() o = ord(h[19]) & 15 h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000 return h def get_totp_token(secret): return get_hotp_token(secret, intervals_no=int(time.time())//30) 

它有两个function:

  • get_hotp_token()生成一次性令牌(一次使用后应该无效),
  • get_totp_token()根据时间生成标记(以30秒为间隔进行更改),

参数

当涉及到参数:

  • secret是服务器(以上脚本)和客户端(Google身份validation器,通过在应用程序内提供密码)已知的秘密值,
  • intervals_no是在每一代令牌之后递增的数字(这应该可能在服务器上通过在过去检查的最后一个成功的一个之后检查有限数量的整数来解决)

如何使用它

  1. 生成secret (它必须是base64.b32decode()正确参数) – 最好是16个字符(no =符号),因为它确实对脚本和Google Authenticator都有效。
  2. 使用get_hotp_token()如果你想在每次使用后get_hotp_token()一次性密码。 在Google身份validation器中,我根据柜台提到的这种types的密码。 为了在服务器上检查它,你将需要检查一些intervals_no值(因为你没有保证用户没有在请求之间产生通道),但是不能小于最后一个工作intervals_no值(因此你应该可能存储在某个地方)。
  3. 使用get_totp_token() ,如果你想要一个令牌工作在30秒间隔。 你必须确保两个系统都有正确的时间设置(这意味着它们在任何给定的时刻都会产生相同的Unix时间戳)。
  4. 一定要保护自己免受暴力攻击。 如果使用基于时间的密码,那么在不到30秒的时间内尝试使用1000000个值,就有100%的机会猜测密码。 对于基于HMAC的passowrds(HOTPs),情况似乎更糟。

当使用下面的代码一次性基于HMAC的密码时:

 secret = 'MZXW633PN5XW6MZX' for i in xrange(1, 10): print i, get_hotp_token(secret, intervals_no=i) 

你会得到以下结果:

 1 448400 2 656122 3 457125 4 35022 5 401553 6 581333 7 16329 8 529359 9 171710 

这与Google Authenticator应用程序生成的令牌相对应(除less于6个符号外,应用程序会将开头添加0来达到6个字符的长度)。

我想要一个Python脚本来生成TOTP密码。 所以,我写了python脚本。 这是我的实现。 我有这个维基百科上的信息和一些关于HOTP和TOTP的知识来写这个脚本。

 import hmac, base64, struct, hashlib, time, array def Truncate(hmac_sha1): """ Truncate represents the function that converts an HMAC-SHA-1 value into an HOTP value as defined in Section 5.3. http://tools.ietf.org/html/rfc4226#section-5.3 """ offset = int(hmac_sha1[-1], 16) binary = int(hmac_sha1[(offset * 2):((offset * 2) + 8)], 16) & 0x7fffffff return str(binary) def _long_to_byte_array(long_num): """ helper function to convert a long number into a byte array """ 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 HOTP(K, C, digits=6): """ HOTP accepts key K and counter C optional digits parameter can control the response length returns the OATH integer code with {digits} length """ C_bytes = _long_to_byte_array(C) hmac_sha1 = hmac.new(key=K, msg=C_bytes, digestmod=hashlib.sha1).hexdigest() return Truncate(hmac_sha1)[-digits:] def TOTP(K, digits=6, window=30): """ TOTP is a time-based variant of HOTP. It accepts only key K, since the counter is derived from the current time optional digits parameter can control the response length optional window parameter controls the time window in seconds returns the OATH integer code with {digits} length """ C = long(time.time() / window) return HOTP(K, C, digits=digits) 
Interesting Posts