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) # calculatess 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 hash = hmac.new(key, message, hashlib.sha1).digest() # extract offset (last 4 bits of HMAC) offset = hash[-1] & 0x0f # extract 4 bytes starting at offset, interpret as 4 byte unsigned int truncatedHash = int.from_bytes(hash[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): 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)) print("-"*width) if __name__=="__main__": parser = argparse.ArgumentParser(description="Two-Step-Token generator (RFC 6238)") parser.add_argument("database", type=str, help="sqlite database (dumped from Android)") args = parser.parse_args() from_database(args.database)