Google Authenticator application を使用して生成できるワンタイムパスワードを使用しようとしています。
基本的に、Google認証システムは2種類のパスワードを実装しています。
Google Authenticatorは、ここでオープンソースとしても利用できます: code.google.com/p/google-authenticator
HOTPおよびTOTPパスワードを生成する既存のソリューションを探していましたが、あまり見つかりませんでした。私が持っているコードは、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向けGoogle認証アプリを使用して生成したパスワードと同じではないことです。複数のintervals_no
値(intervals_no = 0
で始まる最初の10000個)を試しましたが、secret
はGA app 。
私の質問は:
要約すると、Pythonコード内でGoogle認証システム認証を実装するのに役立つ手がかりを教えてください。
私は自分の質問に報奨金を設定したかったのですが、ソリューションの作成に成功しました。私の問題は、secret
キーの誤った値に関連しているように見えました(base64.b32decode()
関数の正しいパラメーターでなければなりません)。
以下に、使用方法に関する説明を含む完全な実用的なソリューションを投稿します。
次のコードで十分です。また、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)
_
次の2つの機能があります。
get_hotp_token()
はワンタイムトークンを生成します(1回使用すると無効になります)。get_totp_token()
は、時間に基づいてトークンを生成します(30秒間隔で変更されます)。パラメータに関しては:
secret
は、サーバー(上記のスクリプト)とクライアント(アプリケーション内でパスワードとして提供するGoogle認証システム)が知っている秘密の値です。intervals_no
_は、トークンの各生成後にインクレメントされた数です(これはおそらく、最後に成功した整数を過去にチェックした後、有限数の整数をチェックすることでサーバー上で解決されるはずです)secret
(base64.b32decode()
の正しいパラメーターである必要があります)-16文字(_=
_記号なし)を生成します。これは、スクリプトとGoogle認証システムの両方で確実に機能するためです。get_hotp_token()
を使用します。 Google Authenticatorで、このタイプのパスワードはカウンターに基づいていると述べました。サーバー上でチェックするには、いくつかの_intervals_no
_の値をチェックする必要があります(何らかの理由でユーザーがリクエスト間のパスを生成しなかったことを確認できないため)。ただし、最後に機能する_intervals_no
_値(したがって、おそらくどこかに格納する必要があります)。get_totp_token()
を使用します。両方のシステムに正しい時刻が設定されていることを確認する必要があります(つまり、両方のシステムが特定の瞬間に同じUnixタイムスタンプを生成することを意味します)。ワンタイム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認証システムアプリによって生成されたトークンに対応します(6記号より短い場合を除き、アプリは先頭にゼロを追加して6文字の長さに到達します)。
TOTPパスワードを生成するためのpythonスクリプトが必要でした。そこで、pythonスクリプトを作成しました。これが私の実装です。私はウィキペディアでこの info と、このスクリプトを書くための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)