Skip to content

Instantly share code, notes, and snippets.

@janbraiins
Created November 6, 2017 13:59
Show Gist options
  • Save janbraiins/4ba1d7116791aca1d9e4962113d7ecae to your computer and use it in GitHub Desktop.
Save janbraiins/4ba1d7116791aca1d9e4962113d7ecae to your computer and use it in GitHub Desktop.
Conversion tool for google authenticator to FreeOTP
#!/usr/bin/python
#
# Why:
#
# Currently, there is no easy way how to transfer all secrets from a google authenticator to a new device if:
#
# - the new device is not rooted (no way to push/access the sqlite DB of google authenticator)
# - you are not willing to enter every secret manually (assuming you have the sqlite DB from the old device)
#
# However, if you are willing to replace google authenticator for
# FreeOTP, you can populate the DB quite efficiently as long as you
# are able to get the Sqlite database of the Google Auth from the
# original device.
#
# Steps:
# - Fetch the google authenticator database from the old device (requires root)
# adb pull /data/data/com.google.android.apps.authenticator2/databases/databases
#
# - Create a backup of the freeotp to get the empty .xml:
# adb backup -noapk org.fedorahosted.freeotp
#
# - Unpack the backup via android: backup https://sourceforge.net/projects/adbextractor/files/latest/download
# java -jar abe.jar unpack backup.ab freeotp.tar
# tar xvf freeotp.tar
#
# - Run the script with the google auth database that has been retrieved from an old device:
# ./gauth2freeotp.py ./databases
#
# - Edit/Append the result into apps/org.fedorahosted.freeotp/sp/tokens.xml
#
# - Pack the result into the .tar
# find apps/ -type f | pax -wd > freeotpnew.tar
#
# - Create a valid Android backup from the tar
# java -jar abe.jar pack freeotpnew.tar otp.ab
#
# - Finally, restore the OTP database on a new device
# adb restore otp.ab
import base64
import array
import sqlite3
import sys
db = sqlite3.connect(sys.argv[1])
cursor = db.cursor()
secret_tmpl = '<string name="{0}">{{&quot;algo&quot;:&quot;SHA1&quot;,&quot;counter&quot;:0,&quot;digits&quot;:6,&quot;issuerExt&quot;:&quot;{0}&quot;,&quot;label&quot;:&quot;vvv&quot;,&quot;period&quot;:30,&quot;secret&quot;:[{1}],&quot;type&quot;:&quot;TOTP&quot;}}</string>'
order_tmpl = '<string name="tokenOrder">[{0}]</string>'
cursor.execute('''SELECT email, secret, provider, issuer, original_name FROM accounts''')
ordered_names = []
for (email, secret, provider, issuer, original_name) in cursor:
# base32 decoder requires the encoded string to be padded with '='
# to multiple of 8 bytes
padding = ((len(secret) + 7) & 0xffffff8) - len(secret)
secret = secret + '=' * padding
# print('Padding = %s secret= %s' % (padding, secret))
try:
secret_str = ','.join([str(x) for x in array.array('B', base64.b32decode(secret))])
except Exception as e:
sys.stderr.write('Incorrect secret: %s, error: %s\n' % (secret, e))
else:
print(' %s' % secret_tmpl.format(email, secret_str))
ordered_names.append('&quot;%s&quot;' % email)
print(' %s' % order_tmpl.format(','.join(ordered_names)))
db.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment