Google has started offering two-factor authentication for Google logins, using Google Authenticator. They have applications available for iPhone, Android, and Blackberry that give time-based passwords based on the proposed TOTP (Time-based One Time Password) draft standard.
The Google code provides a command line program that can generate secret keys as well as a PAM module, but it turns out to be very little code to authenticate a TOTP, thereby providing two-factor authentication to your website very easily.
To give the user the key, you’ll need to generate a cryptographically-secure 10 byte random key, presented to the user as a base32 16-character string. They can either enter this string directly, or you can use Google charts to provide a barcode that they can scan into the Google Authenticator application:
def get_barcode_image(username, domain, secretkey): url = "https://www.google.com/chart" url += "?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/" url += username + "@" + domain + "%3Fsecret%3D" + secretkey return url
For an example of what a code looks like, click here, or, look below:
After the user has a secret key from you and has entered it into Google Authenticator either by typing it in directly or scanning in the barcode, you have to be able to verify the key during login (for example). The code to authenticate is only a few lines in Python:
import time import struct import hmac import hashlib import base64 def authenticate(secretkey, code_attempt): tm = int(time.time() / 30) secretkey = base64.b32decode(secretkey) # try 30 seconds behind and ahead as well for ix in [-1, 0, 1]: # convert timestamp to raw bytes b = struct.pack(">q", tm + ix) # generate HMAC-SHA1 from timestamp based on secret key hm = hmac.HMAC(secretkey, b, hashlib.sha1).digest() # extract 4 bytes from digest based on LSB offset = ord(hm[-1]) & 0x0F truncatedHash = hm[offset:offset+4] # get the code from it code = struct.unpack(">L", truncatedHash) code &= 0x7FFFFFFF; code %= 1000000; if ("%06d" % code) == str(code_attempt): return True return False
Updated 2015-10-17: All snippets in this article are under CC0 1.0 Universal License. Feel free to use as long as no liability is assumed. Buy me a coffee next time you see me, if you’re feeling any latent obligation!
Comments are moderated whenever I remember that I have a blog.