Last active
July 10, 2020 20:11
-
-
Save naufraghi/ba8c35bf4fc9b69c2a61 to your computer and use it in GitHub Desktop.
Script to backup Google Authenticator secrets as QR-codes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Copyright 2014 Matteo Bertini [email protected] | |
# Released as Public Domain | |
# | |
# Latest version: | |
# wget http://slug.it/backup-google-authenticator.py | |
# | |
# Requirements | |
# ============ | |
# | |
# A rooted phone and a google authenticator full of secrets, | |
# connected with an USB cable to a computer with: | |
# - python3 | |
# - adb (Android SDK) | |
# - qrencode | |
# - ImageMagick (+ ghostview for the labels if using brew) | |
# | |
# Usage | |
# ===== | |
# | |
# Connect the phone, run, save or print or acquire the codes. | |
import os | |
import sys | |
import shutil | |
import sqlite3 | |
import subprocess | |
import tempfile | |
import urllib.parse | |
import uu | |
import hashlib | |
import time | |
def get_databases(dest): | |
with open(dest, "w+b") as db: | |
cmd = "adb shell \"su -c 'uuencode /data/data/com.google.android.apps.authenticator2/databases/databases -'\"" | |
dump = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) | |
uu.decode(dump.stdout, db) | |
return dest | |
def get_accounts(db): | |
conn = sqlite3.connect(db) | |
cursor = conn.cursor() | |
for email, secret in cursor.execute("SELECT email, secret FROM accounts"): | |
yield (email, secret) | |
def create_qrcode(msg, filename): | |
subprocess.check_call(["qrencode", "-s", "4", "-o", filename, msg]) | |
return filename | |
def set_image_comment(image, comment): | |
subprocess.check_call(["mogrify", "-comment", comment, image]) | |
return image | |
def create_contact_sheet(filename): | |
subprocess.check_call(["montage", "-geometry", "150x150+50+50", "-tile", "1x", "-label", r"%c", "*.png", filename]) | |
return filename | |
def backup_google_authenticator_secrets(dest_dir, dest_filename): | |
orig_dir = os.getcwd() | |
temp_dir = tempfile.mkdtemp() | |
os.chdir(temp_dir) | |
for account, secret in get_accounts(get_databases("databases.sqlite")): | |
account_ = urllib.parse.quote_plus(account) | |
qrsecret = "otpauth://totp/{account_}?secret={secret}".format(**locals()) | |
filename = hashlib.md5(account.encode('utf-8')).hexdigest()+".png" | |
create_qrcode(qrsecret, filename) | |
set_image_comment(filename, account) | |
shutil.copyfile(os.path.join(temp_dir, create_contact_sheet(dest_filename)), | |
os.path.join(dest_dir, dest_filename)) | |
shutil.rmtree(temp_dir) | |
def checked_dir(adir): | |
if os.path.isdir(adir): | |
return adir | |
else: | |
raise argparse.ArgumentTypeError("Invalid dir %r" % adir) | |
if __name__ == "__main__": | |
import argparse | |
parser = argparse.ArgumentParser("Backup Google Authenticator secrets to QR-codes") | |
parser.add_argument("dest", metavar="DIR", nargs="?", type=checked_dir, | |
help="Save the final image 'auth-codes.png' in %(default)s", default='.') | |
args = parser.parse_args() | |
backup_google_authenticator_secrets(args.dest, "auth-codes.png") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment