J'essaie d'utiliser des mots de passe à usage unique qui peuvent être générés à l'aide de application Google Authenticator .
Fondamentalement, Google Authenticator implémente deux types de mots de passe:
Google Authenticator est également disponible en Open Source ici: code.google.com/p/google-authenticator
Je cherchais des solutions existantes pour générer des mots de passe HOTP et TOTP, mais je n'ai pas trouvé grand-chose. Le code que j'ai est l'extrait suivant responsable de la génération de 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
Le problème auquel je suis confronté est que le mot de passe que je génère à l'aide du code ci-dessus n'est pas le même que celui généré à l'aide de l'application Google Authenticator pour Android. Même si j'ai essayé plusieurs intervals_no
valeurs (exactement les premiers 10000, commençant par intervals_no = 0
), secret
étant égal à la clé fournie dans l'application GA.
Mes questions sont:
Pour résumer: veuillez me donner des indices qui m'aideront à implémenter l'authentification Google Authenticator dans mon code Python.
Je voulais mettre une prime sur ma question, mais j'ai réussi à créer une solution. Mon problème semblait être lié à une valeur incorrecte de la touche secret
(il doit s'agir d'un paramètre correct pour la fonction base64.b32decode()
).
Ci-dessous, je poste une solution de travail complète avec des explications sur la façon de l'utiliser.
Le code suivant suffit. Je l'ai également téléchargé sur GitHub en tant que module séparé appelé onetimepass (disponible ici: 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)
Il a deux fonctions:
get_hotp_token()
génère un jeton unique (qui devrait être invalidé après une seule utilisation),get_totp_token()
génère un jeton basé sur le temps (changé par intervalles de 30 secondes),En ce qui concerne les paramètres:
secret
est une valeur secrète connue du serveur (le script ci-dessus) et du client (Google Authenticator, en le fournissant comme mot de passe dans l'application),intervals_no
Est le nombre incrémenté après chaque génération du jeton (cela devrait probablement être résolu sur le serveur en vérifiant un nombre fini d'entiers après la dernière vérification réussie dans le passé)secret
(ce doit être un paramètre correct pour base64.b32decode()
) - de préférence 16 caractères (pas de signe =
), Car cela a sûrement fonctionné pour le script et Google Authenticator.get_hotp_token()
si vous souhaitez que les mots de passe à usage unique soient invalidés après chaque utilisation. Dans Google Authenticator, ce type de mots de passe que j'ai mentionné est basé sur le compteur. Pour le vérifier sur le serveur, vous devrez vérifier plusieurs valeurs de intervals_no
(Car vous n'avez aucune garantie que l'utilisateur n'a pas généré le passage entre les demandes pour une raison quelconque), mais pas moins que le dernier intervals_no
(Vous devriez donc probablement le stocker quelque part).get_totp_token()
, si vous souhaitez qu'un jeton fonctionne à des intervalles de 30 secondes. Vous devez vous assurer que les deux systèmes ont une heure correcte (ce qui signifie qu'ils génèrent tous les deux le même horodatage Unix à un moment donné).Lorsque vous utilisez le code suivant pour un mot de passe unique basé sur HMAC:
secret = 'MZXW633PN5XW6MZX'
for i in xrange(1, 10):
print i, get_hotp_token(secret, intervals_no=i)
vous obtiendrez le résultat suivant:
1 448400
2 656122
3 457125
4 35022
5 401553
6 581333
7 16329
8 529359
9 171710
ce qui correspond aux jetons générés par l'application Google Authenticator (sauf si moins de 6 signes, l'application ajoute des zéros au début pour atteindre une longueur de 6 caractères).
Je voulais un script python pour générer le mot de passe TOTP. Donc, j'ai écrit le script python. Ceci est mon implémentation. J'ai ceci info sur wikipedia et quelques connaissances sur HOTP et TOTP pour écrire ce script.
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)