# Backing up and recovering 2FA tokens from FreeOTP
## Backing up FreeOTP
Using [adb](https://developer.android.com/studio/command-line/adb.html), [create a backup](https://androidquest.wordpress.com/2014/09/18/backup-applications-on-android-phone-with-adb/) of the app using the following command:
```sh
adb backup -f freeotp-backup.ab -apk org.fedorahosted.freeotp
```
[org.fedorahosted.freeotp](https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp) is the app ID for FreeOTP.
This will ask, on the phone, for a password to encrypt the backup. Proceed with a password.
## Manually extracting the backup
The backups are some form of encrypted tar file. [Android Backup Extractor](https://github.com/nelenkov/android-backup-extractor) can decrypt them.
It's available on the AUR as [android-backup-extractor-git](https://aur.archlinux.org/packages/android-backup-extractor-git/).
Use it like so (this command will ask you for the password you just set to decrypt it):
```sh
abe unpack freeotp-backup.ab freeotp-backup.tar
```
Then extract the generated tar file:
```shell
$ tar xvf freeotp-backup.tar
apps/org.fedorahosted.freeotp/_manifest
apps/org.fedorahosted.freeotp/sp/tokens.xml
```
We don't care about the manifest file, so let's look at `apps/org.fedorahosted.freeotp/sp/tokens.xml`.
### Reading tokens.xml
The `tokens.xml` file is the preference file of FreeOTP. Each `...` is a token (except the one with the name `tokenOrder`).
The token is a JSON blob. Let's take a look at an example token (which is no longer valid!):
```xml
```
Let's open a python shell and get the inner text of the XML into a Python 3 shell. We'll need `base64`, `json` and `html` in a moment:
```py
>>> import base64, json, html
>>> s = """{"algo":"SHA1","counter":0,"digits":6,"imageAlt":"content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Ffile%2F3195/ORIGINAL/NONE/741876674","issuerExt":"Discord","issuerInt":"Discord","label":"me@example.org","period":30,"secret":[122,-15,11,51,-100,-109,21,89,-30,-35],"type":"TOTP"}"""
```
We decode all those HTML entities from the XML encoding:
```py
>>> s = html.unescape(s); print(s)
{"algo":"SHA1","counter":0,"digits":6,"imageAlt":"content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Ffile%2F3195/ORIGINAL/NONE/741876674","issuerExt":"Discord","issuerInt":"Discord","label":"me@example.org","period":30,"secret":[122,-15,11,51,-100,-109,21,89,-30,-35],"type":"TOTP"}
```
What we specifically need from this is the secret. It's a signed byte array from Java... Let's grab it:
```py
>>> token = json.loads(s); print(token["secret"])
[122, -15, 11, 51, -100, -109, 21, 89, -30, -35]
```
Now we have to turn this into a Python bytestring. For that, these bytes need to be turned back into unsigned bytes. Let's go:
```py
>>> secret = bytes((x + 256) & 255 for x in token["secret"]); print(secret)
b'z\xf1\x0b3\x9c\x93\x15Y\xe2\xdd'
```
Finally, the TOTP standard uses base32 strings for TOTP secrets, so we'll need to turn those bytes into a base32 string:
```py
>>> code = base64.b32encode(secret); print(code.decode())
PLYQWM44SMKVTYW5
```
There we go. `PLYQWM44SMKVTYW5` is our secret in a format we can manually input into FreeOTP or Keepass.
## In a nutshell
Install abe (choose your preferred aur installer):
yaourt -S android-backup-extractor-git
Get your token:
```
adb backup -f freeotp-backup.ab -apk org.fedorahosted.freeotp
abe unpack freeotp-backup.ab freeotp-backup.tar
tar xvf freeotp-backup.tar
./get-token.py
secret name: token name
token secret base64: PLYQWM44SMKVTYW5
```
There's a verbose flag on python script if you want all the details