120

Google Authenticator アプリケーションを使用して生成できるワンタイム パスワードを使用しようとしています。

Google 認証システムの機能

基本的に、Google Authenticator は 2 種類のパスワードを実装しています。

  • HOTP - HMAC ベースのワンタイム パスワード。RFC4226に準拠して、呼び出しごとにパスワードが変更されることを意味します。
  • TOTP - 時間ベースのワンタイム パスワード。30 秒ごとに変更されます (私の知る限り)。

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 Authenticator アプリを使用して生成したものと同じではないことです。GAアプリ内で提供されたキーと等しい複数のintervals_no値(正確に最初の10000、で始まるintervals_no = 0)を試しましたが。secret

私が持っている質問

私の質問は次のとおりです。

  1. 私は何を間違っていますか?
  2. Python で HOTP や TOTP を生成するにはどうすればよいですか?
  3. このための既存の Python ライブラリはありますか?

要約すると、Python コード内に Google Authenticator 認証を実装するのに役立つ手がかりがあれば教えてください。

4

3 に答える 3

169

質問に報奨金を設定したかったのですが、解決策を作成することに成功しました。私の問題は、secretキーの値が正しくないことに関連しているようです(関数の正しいパラメーターである必要がありますbase64.b32decode())。

以下に、使用方法の説明とともに完全な実用的なソリューションを投稿します。

コード

次のコードで十分です。また、これをonetimepassという別のモジュールとして GitHub にアップロードしました( 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 Authenticator) に知られている秘密の値です。
  • intervals_noトークンが生成されるたびに増加する数です (これは、過去にチェックされた最後の成功した整数の後に有限数の整数をチェックすることによって、おそらくサーバー上で解決されるはずです)。

それの使い方

  1. 生成secret(の正しいパラメーターである必要があります) -スクリプトと Google Authenticator の両方で確実に機能するため、base64.b32decode()できれば 16 文字 (記号なし) です。=
  2. 使用get_hotp_token()するたびにワンタイム パスワードを無効にする場合に使用します。Google Authenticator では、このタイプのパスワードは、カウンターに基づいていると述べました。サーバー上で確認するには、いくつかの値を確認する必要があります(intervals_noユーザーが何らかの理由でリクエスト間のパスを生成しなかったという検疫者がいないため)。intervals_noどこか)。
  3. get_totp_token()トークンを 30 秒間隔で動作させたい場合は、 を使用します。両方のシステムに正しい時刻が設定されていることを確認する必要があります (つまり、両方のシステムが特定の時点で同じ Unix タイムスタンプを生成することを意味します)。
  4. ブルートフォース攻撃から身を守るようにしてください。時間ベースのパスワードが使用されている場合、30 秒以内に 1000000 個の値を試すと、100% の確率でパスワードを推測できます。HMAC ベースのパスワード (HOTP) の場合は、さらに悪いようです。

ワンタイム 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 アプリによって生成されたトークンに対応しています (ただし、記号が 6 文字より短い場合、アプリは先頭にゼロを追加して 6 文字の長さにします)。

于 2011-12-18T04:47:18.907 に答える
8

TOTP パスワードを生成する Python スクリプトが必要でした。ということで、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)
于 2014-04-22T13:52:48.417 に答える