Google Authenticator is an alternative to SMS for 2Step verification, installing an app on Android where the codes will be sent.

It works without any connectivity; it even works on plane mode. This is what I don't get. How is it possible that it works without connectivity? How do the mobile phone and the server sync to know which code is valid at that very moment?

  • 6
    The codes are not "sent". They are made via a seed and counter. – JZeolla May 1 '13 at 19:28
up vote 162 down vote accepted

Google Authenticator supports both the HOTP and TOTP algorithms for generating one-time passwords.

With HOTP, the server and client share a secret value and a counter, which are used to compute a one time password independently on both sides. Whenever a password is generated and used, the counter is incremented on both sides, allowing the server and client to remain in sync.

TOTP essentially uses the same algorithm as HOTP with one major difference. The counter used in TOTP is replaced by the current time. The client and server remain in sync as long as the system times remain the same. This can be done by using the Network Time protocol.

The secret key (as well as the counter in the case of HOTP) has to be communicated to both the server and the client at some point in time. In the case of Google Authenticator, this is done in the form of a QRCode encoded URI. See: KeyUriFormat for more information.

  • 5
    In the case of HOTP, how does Google Authenticator know that I have "used" the password without syncing with the server? What Google Authenticator does is that it continues to flash different keys and I can just use any one them without giving feedback to my mobile. – Mario Awad May 7 '13 at 8:21
  • 3
    @MarioAwad The answer to that can be found on the HOTP RFC, section 7.4. ietf.org/rfc/rfc4226.txt – Ayrx May 7 '13 at 8:24
  • 4
    Thank you for the well defined answer and followup. Quick summary of section 7.4: Resynchronization of the Counter every now and then and a look-ahead window for the counter is what makes things work without requiring instant-sync. – Mario Awad May 7 '13 at 11:03
  • As @TerryChia pointed out, the secret key is in the QR code. Be aware of the sensitivity of the QRCode/Information. I wrote a blog post a while ago netknights.it/en/the-problem-with-the-google-authenticator – cornelinux Apr 20 '17 at 14:46

Working:

Authenticator implements the Time-Based One-Time Password (TOTP) algorithm. It has the following ingredients:

• A shared secret (a sequence of bytes)

• An input derived from the current time

• A signing function

Shared Secret : The shared secret is what you need to obtain to set up the account on your phone. Either you take a photo of a QR code using your phone or you can enter the secret manually.

Input (Current Time) : The input time value you'll simply get from your phone, no further interaction with the server is required once you have obtained the secret. However it is important that your phone's time is accurate as the server will essentially repeat what happens on your phone using the current time as known by the server.

Signing Function : The signing function used is HMAC-SHA1. HMAC stands for Hash-based message authentication code and it is an algorithm that uses a secure one-way hash function (SHA1 in this case) to sign a value. Using an HMAC allows us to verify authenticity - only people knowing the secret can generate the same output for the same input (the current time).

OTP Algorithm:

Pseudo Code:

original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx

secret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))

input = CURRENT_UNIX_TIME() / 30  // sets a constant value for 30 seconds

hmac = SHA1(secret + SHA1(secret + input)) //apply hashing

offset = hmac[len(hmac)-1] & 0x0F //Last nibble

four_bytes = hmac[offset : offset+4] //takes a subset of 4 bytes from 20 bytes

large_integer = INT(four_bytes) //Covert four bytes to integer

small_integer = large_integer % 1,000,00 //gives 6 digit code

enter image description here

Reference: https://garbagecollected.org/2014/09/14/how-google-authenticator-works/

Also check out this github project for GO implementation: https://github.com/robbiev/two-factor-auth/blob/master/main.go

It'll work on a seed based on time so it's similar to the way the RSA key fobs work. i.e. they also don't require any connectivity.

I've just had a look around and this is answered here: https://stackoverflow.com/questions/8340495/how-rsa-tokens-works

HOTP & TOTP algorithms have now become open standards in IT. That means, you can use Google Authenticator not only for Google, but also Facebook, Yahoo, Microsoft websites, or even your own website.

All you have to do is to just use the following code in the server's main base:

import os

import bcrypt
from twilio.rest import TwilioRestClient
from flask.ext.login import LoginManager
from flask import Flask
from flask import request
from flask import redirect
from flask import url_for
from flask import render_template
from flask.ext.login import login_user
from flask.ext.login import logout_user
from flask.ext.login import current_user
from flask.ext.login import login_required
from pymongo import Connection

from konfig import Konfig

app = Flask(__name__)
konf = Konfig()
app.secret_key = konf.secret_key

connection = Connection(konf.mongo_url)

login_manager = LoginManager()
login_manager.setup_app(app)

twilio = TwilioRestClient()

@login_manager.user_loader
def load_user(user_id):
    return User(user_id)

class User:
    def __init__(self, user_id):
        self.id = user_id.lower()
        self.db = connection.tfa.users
        self.account = self.db.find_one({'uid': self.id})

    def create(self):
        self.db.insert({'uid': self.id})
        self.account = self.db.find_one({'uid': self.id})

    def save(self):
        self.db.save(self.account)

    def password_valid(self, pwd):
        pwd_hash = self.account['password_hash']
        return bcrypt.hashpw(pwd, pwd_hash) == pwd_hash

    # The methods below are required by flask-login
    def is_authenticated(self):
        """Always return true - we don't do any account verification"""
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return self.id

@app.route("/", methods=['GET', 'POST'])
def main_page():
    opts = {}
    if request.method == 'GET':
        return render_template('main_page.html', opts=opts)
    user = User(request.form['username'])
    if not user.account or not user.password_valid(request.form['password']):
        opts['invalid_username_or_password'] = True
        return render_template('main_page.html', opts=opts)
    login_user(user)
    return redirect(url_for('user'))

@app.route("/sign-up", methods=['GET', 'POST'])
def sign_up():
    opts = {}
    if request.method == 'GET':
        return render_template('sign_up.html', opts=opts)
    user = User(request.form['username'])
    if user.account:
        opts['username_exists'] = True
        return render_template('sign_up.html', opts=opts)
    if request.form['password1'] != request.form['password2']:
        opts['passwords_do_not_match'] = True
        return render_template('sign_up.html', opts=opts)
    user.create()
    pwd_hash = bcrypt.hashpw(request.form['password1'], bcrypt.gensalt())
    user.account['password_hash'] = pwd_hash
    user.save()
    login_user(user)
    return redirect(url_for('user'))

@app.route("/user")
@login_required
def user():
    opts = {'user': current_user,
            'logged_in': True}
    return render_template('user.html', opts=opts)

@app.route("/logout")
def logout():
    logout_user()
    return redirect(url_for('main_page'))

if __name__ == "__main__":
    # Bind to PORT if defined, otherwise default to 5000.
    port = int(os.environ.get('PORT', 5000))
    if port == 5000:
        app.debug = True
    app.run(host='0.0.0.0', port=port)

Courtesy: Kyle Kelly-Yahner

  • 6
    I think the question was more generic about how the Authenticator works, not how to implement in code. – Andrew Lott Jul 24 '13 at 8:43
  • 6
    Hello Andrew :) With respect to your comment on the relevancy: The generic question has already been answered above by Terry Chia. My comment is for developers. (Because hardly any expert knows that you can use Google Authenticator for third party API/Apps/Web Apps/Website as well) – Gaurav Jha Jul 24 '13 at 17:46
  • 11
    How is the code relevant? I can't see any logic beyond simple single-factor password authentication. – Alan Plum Oct 9 '13 at 21:31
  • agree. there is no logic beyond single factor auth. very misleading – conrad Jul 28 '15 at 16:51

If one straces the sshd daemon, one can see how the server 'knows' about the secret key, as it read the users config file:

#strace -f -v -e open /usr/sbin/sshd -p 2222 -dddd -f /etc/ssh/sshd_config 2>&1 | grep -i goog


> > [pid  2105] open("/home/myuser/.google_authenticator", O_RDONLY) = 4
> > [pid  2105] open("/home/myuser/.google_authenticator~",
> > O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_NOFOLLOW, 0400debug3:
> > mm_sshpam_respond: pam_respond returned 1 [preauth]

The mobile phone already knows it; you scanned it via QR or typed it in.

  • 4
    It was downvoted because it is a terrible answer. It;'s vague at best and doesn't actually answer the OP's question. – catchdave May 24 '17 at 4:41
  • 1
    james - no hate is involved. We encourage good answers to be upvoted and bad ones to be downvoted. It's how the site works. Your post doesn't answer the question. – Rory Alsop Aug 28 at 14:20
  • This is a good example of how to demonstrate that the tokens are not being sent to the server, but doesn't actually explain how it works (Which is what OP asked) – Mr. E Aug 31 at 0:17

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.