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.txt
extension - 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 pycryptodome
python3 decrypt.py
- Enter the password you set before
- That should have a
backup.txt
appear with your TOTP seed values
With many thanks to my good friend Eddy
.