it-swarm.com.de

Implementierung von Google Authenticator in Python

Ich versuche, Einmalpasswörter zu verwenden, die mit Google Authenticator-Anwendung generiert werden können.

Was macht Google Authenticator?

Grundsätzlich implementiert Google Authenticator zwei Arten von Passwörtern:

  • [~ # ~] hotp [~ # ~] - HMAC-basiertes Einmalkennwort, dh das Kennwort wird bei jedem Anruf entsprechend geändert bis RFC4226 und
  • [~ # ~] totp [~ # ~] - Zeitbasiertes Einmalkennwort, das sich alle 30 Sekunden ändert (soweit Ich kenne).

Google Authenticator ist auch als Open Source hier verfügbar: code.google.com/p/google-authenticator

Aktueller Code

Ich suchte nach vorhandenen Lösungen zum Generieren von HOTP- und TOTP-Passwörtern, fand aber nicht viel. Der Code, den ich habe, ist der folgende Ausschnitt, der für das Generieren von HOTP verantwortlich ist:

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

Das Problem, mit dem ich konfrontiert bin, ist, dass das Passwort, das ich mit dem obigen Code erzeuge, nicht mit dem Passwort übereinstimmt, das mit der Google Authenticator-App für Android erstellt wurde. Obwohl ich mehrfach versucht habe intervals_no Werte (genau die ersten 10000, beginnend mit intervals_no = 0), wobei secret gleich dem Schlüssel ist, der in der GA App angegeben ist.

Fragen habe ich

Meine Fragen sind:

  1. Was mache ich falsch?
  2. Wie kann ich in Python HOTP und/oder TOTP generieren?
  3. Gibt es dafür bereits Python Bibliotheken?

Fazit: Bitte geben Sie mir Hinweise, die mir bei der Implementierung der Google Authenticator-Authentifizierung in meinem Python Code helfen.

97
Tadeck

Ich wollte ein Kopfgeld auf meine Frage setzen, aber es ist mir gelungen, eine Lösung zu finden. Mein Problem schien mit dem falschen Wert der Taste secret verbunden zu sein (es muss der richtige Parameter für die Funktion base64.b32decode() sein).

Im Folgenden poste ich eine vollständige funktionierende Lösung mit einer Erklärung zur Verwendung.

Code

Der folgende Code reicht aus. Ich habe es auch als separates Modul namens onetimepass auf GitHub hochgeladen (hier verfügbar: 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)

Es hat zwei Funktionen:

  • get_hotp_token() generiert ein einmaliges Token (das nach einmaliger Verwendung ungültig werden sollte),
  • get_totp_token() generiert Token basierend auf der Zeit (alle 30 Sekunden geändert),

Parameter

Wenn es um Parameter geht:

  • secret ist ein geheimer Wert, der dem Server (obiges Skript) und dem Client (Google Authenticator, indem er in der Anwendung als Kennwort angegeben wird) bekannt ist.
  • intervals_no Ist die Zahl, die nach jeder Token-Generierung inkremenet wird (dies sollte wahrscheinlich auf dem Server behoben werden, indem eine endliche Anzahl von Ganzzahlen nach der letzten erfolgreichen Überprüfung in der Vergangenheit überprüft wird).

Wie man es benutzt

  1. Generieren Sie secret (es muss der richtige Parameter für base64.b32decode() sein) - vorzugsweise 16 Zeichen (keine = - Zeichen), da dies sicherlich sowohl für das Skript als auch für Google Authenticator funktioniert hat.
  2. Verwenden Sie get_hotp_token(), wenn Einmalpasswörter nach jeder Verwendung ungültig gemacht werden sollen. In Google Authenticator basiert diese Art von Passwörtern auf dem Zähler. Um es auf dem Server zu überprüfen, müssen Sie mehrere Werte von intervals_no Überprüfen (da Sie keine Garantie haben, dass der Benutzer den Pass zwischen den Anforderungen aus irgendeinem Grund nicht generiert hat), jedoch nicht weniger als den letzten funktionierenden intervals_no Wert (also solltest du ihn wahrscheinlich irgendwo speichern).
  3. Verwenden Sie get_totp_token(), wenn ein Token in 30-Sekunden-Intervallen ausgeführt werden soll. Sie müssen sicherstellen, dass auf beiden Systemen die richtige Uhrzeit eingestellt ist (dh, beide Systeme generieren zu jedem Zeitpunkt den gleichen Unix-Zeitstempel).
  4. Schützen Sie sich vor Brute-Force-Angriffen. Wenn ein zeitbasiertes Kennwort verwendet wird, besteht eine Wahrscheinlichkeit von 1000000, dass das Kennwort erraten wird, wenn 1000000 Werte in weniger als 30 Sekunden versucht werden. Bei HMAC-basierten Passwörtern (HOTPs) scheint es noch schlimmer zu sein.

Beispiel

Wenn Sie den folgenden Code für ein einmaliges HMAC-basiertes Kennwort verwenden:

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

sie erhalten folgendes Ergebnis:

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

dies entspricht den Tokens, die von der Google Authenticator-App generiert wurden (außer wenn sie kürzer als 6 Zeichen sind, fügt die App am Anfang Nullen hinzu, um eine Länge von 6 Zeichen zu erreichen).

146
Tadeck

Ich wollte ein python script, um ein TOTP-Passwort zu generieren. Also schrieb ich das python script. Dies ist meine Implementierung. Ich habe dieses info auf Wikipedia und etwas Wissen über HOTP und TOTP, um dieses Skript zu schreiben.

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)
6
Anish Shah