Skip to content

Instantly share code, notes, and snippets.

@jojonas
Last active September 9, 2015 13:42
Show Gist options
  • Save jojonas/af8d29be87aa0f95f123 to your computer and use it in GitHub Desktop.
Save jojonas/af8d29be87aa0f95f123 to your computer and use it in GitHub Desktop.
One-Time-Token generator (RFC 6238)
import argparse
import time
import base64
import hmac
import hashlib
# pads the string to a multiple of 8 bytes using "="
def pad_base32(string):
return string + "=" * (8 - ((len(string)-1) % 8) - 1)
# calculates the authentification key for the specified secret and timestamp
# more info: https://en.wikipedia.org/wiki/Google_Authenticator
def calckey(secret, timestamp):
# pad secret to multiple of 8 characters
secret = pad_base32(secret)
# decode base32 to byte string (ignoring case)
key = base64.b32decode(secret, casefold=True)
# interpret Unix timestamp / 30sec as 8 byte unsigned long long
message = int(timestamp/30).to_bytes(8, byteorder="big", signed=False)
# combine message (time) and key (secret) into one authentification code using sha1
code = hmac.new(key, message, hashlib.sha1).digest()
# extract offset (last 4 bits of HMAC)
offset = code[-1] & 0x0f
# extract 4 bytes starting at offset, interpret as 4 byte unsigned int
truncatedHash = int.from_bytes(code[offset:offset+4], byteorder="big", signed=False)
# unset the first bit
truncatedHash &= ~(1 << (8*4-1))
# restrict code to 6 digits and convert to string
code = truncatedHash % 1000000
codeStr = "%06d" % code
return codeStr
# to pull the database from Android, see https://nucleussystems.com/blog/android-backup-google-authenticator/
# general steps:
# 1. have root access
# 2. use adb shell to pull /data/data/com.google.android.apps.authenticator2/databases
def from_database(dbfile, enable_plot=False):
import sqlite3
now = int(time.time())
remaining = 30 - (now % 30)
with sqlite3.connect(dbfile) as connection:
cursor = connection.cursor()
cursor.execute("SELECT email, secret FROM accounts")
# output code is a little ugly :/
width = 50
print("="*width)
print("Two-Step-Token generator (RFC 6238)".center(width))
print("="*width)
print("Loaded from databases file '{:s}'.".format(dbfile).center(width))
print("Remaining time: {:d} sec\n".format(remaining).center(width))
print("{:>25} {}".format("Account", "Key"))
print("-"*width)
for name, secret in cursor:
key = calckey(secret, now)
print("{:>25} {}".format(name, key))
if enable_plot:
plot(name, secret)
print("-"*width)
# plots the QR-code using the matplotlib library:
# the QR code contains a URI, but is encoded in the QR-code "text" mode
# this can currently not be fixed, it works with the Google Authentificator App though
def plot(name, secret):
import qrcode
import matplotlib.pyplot as plt
qr = qrcode.QRCode(
version=None,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=1,
border=1,
)
url = "otpauth://totp/{:s}?secret={:s}".format(name, secret)
qr.add_data(url)
qr.make(fit=True)
img = qr.make_image()
plt.clf()
plt.imshow(img, cmap="Greys", interpolation="nearest")
plt.axis('off')
plt.title(name)
plt.show()
if __name__=="__main__":
parser = argparse.ArgumentParser(description="Two-Step-Token generator (RFC 6238)")
parser.add_argument("-p", "--plot", action="store_true", help="plot the secrets via the qrcode module and Matplotlib (for transfering to a new device)")
parser.add_argument("database", type=str, help="sqlite database (dumped from Android)")
args = parser.parse_args()
from_database(args.database, args.plot)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment