After finding out the vendor lock in of this tool, I wanted to get rid of it to move my TOTP generation to Bitwarden. But for that I would either need to setup TOTP again for 20 separate services, or somehow extract the seed from the current generator.
The backup from this tool is a .encrypt file, which encrypts with the password
totpauthenticator by default, You can set it under Settings->Encryption Key
without knowing the previous password, if you’d like to change it.
Then it’s off to decrypting it! I won’t explain the .apk decompilation and
Java scouting done to find this method, but I will give you the tools to decrypt
it yourself.
The process is quite simple, and explained in the code snippet below. To run it,
you need to pip install pycryptodome.
import hashlib
import base64
import json
from getpass import getpass
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
# Place the file as `backup.encrypt` next to this script.
# It's a base64 encoded binary.
with open("backup.encrypt", "rb") as f:
base64_binary = f.read()
binary = base64.b64decode(base64_binary)
password = getpass()
# We need a SHA256 hash of the password.
password_hash = hashlib.sha256(password.encode('utf-8')).digest()
# The IV is set in an imported library, which didn't bother with it.
iv = b'\x00'*16
# Now we can decrypt the AES/CBC/PKCS7 encrypted binary.
def decrypt_with_AES(cipher_text, secret_key):
cipher = AES.new(password_hash, AES.MODE_CBC, iv)
plain_bytes = unpad(cipher.decrypt(cipher_text), AES.block_size)
return plain_bytes.decode()
decrypted = decrypt_with_AES(binary, password_hash)
# The content is a key value pair, of which the key contains a
# list of key-value blocks with our data.
entries = json.loads(list(json.loads(decrypted))[0])
# Finally, write the resulting json to a file.
with open("backup.json", "w") as f:
json.dump(entries, f)
# We find out the seeds have been encoded, so we will decode
# these as well. Quite simple luckily: hex encoded byte arrays from
# which we retrieve the base32 seed.
readable = ""
for entry in entries:
encoded = entry["key"]
decoded = bytes.fromhex(encoded)
recoded = base64.b32encode(decoded).decode()
readable += f"{entry['issuer']} / {entry['name']}: {recoded}\n"
# Don't use any padding characters (=) when setting the seed
with open("backup.txt", "w") as f:
f.write(readable)
Using it
For Windows users. If you’re a Linux user, you don’t need this walkthrough 😚
- Download and install Python. You can get it from the Microsoft Store
- In TOTP Authenticator:
- Set a (simple) password as decribed above, and remember it
- Create a backup file
- Transfer this file to your computer into some folder
- Ensure it is named
backup.encrypt
- Create a new Text Document in that same folder, and copy the code above into it
- Rename that file to
decrypt.py, making sure you also replace the.txtextension - Open a terminal in this folder. Several ways to do it, but a simple one is:
- When in the Windows File Explorer with your new file visible, press F4 to
select the address bar, type
cmd, and press Enter
- When in the Windows File Explorer with your new file visible, press F4 to
select the address bar, type
- In that terminal, type and press Enter for each of these steps:
pip install pycryptodomepython3 decrypt.py- Enter the password you set before
- That should have a
backup.txtappear with your TOTP seed values
With many thanks to my good friend Eddy.