Skip to content

Instantly share code, notes, and snippets.

@aerosol
Last active November 14, 2017 07:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aerosol/039c5a22c7ce2027d3b2d8b02f943ac0 to your computer and use it in GitHub Desktop.
Save aerosol/039c5a22c7ce2027d3b2d8b02f943ac0 to your computer and use it in GitHub Desktop.

Source: andOTP/andOTP#66

Hello, congratulations on such a nice OTP app. It has everything I want in an app, so I migrated my codes from FreeOTP to andOTP. I wrote a short script to do this.

To run the script, get the tokens.xml file from the FreeOTP directory on the phone, run the script with the filename as a parameter (./freeotp_migrate.py tokens.xml), and the script will output an andOTP-compatible plaintext JSON file. Just restore that and you're done.

Here's the script, released under the MIT license:

#!/usr/bin/env python3

import base64
import json
import sys
import xml.etree.ElementTree


def read_tokens(filename):
    e = xml.etree.ElementTree.parse(filename).getroot()
    for item in e.findall("string"):
        if item.get("name") == "tokenOrder":
            continue
        token = json.loads(item.text)
        token["secret"] = base64.b32encode(
            bytes(x & 0xff for x in token["secret"])
        ).decode("utf8")

        issuer = token.get("issuerAlt") or \
            token.get("issuerExt") or \
            token.get("issuerInt")
        label = token.get("label") or token.get("labelAlt")

        if label and issuer:
            token_label = "%s - %s" % (issuer, label)
        else:
            token_label = label or issuer

        yield {
            "algorithm": token["algo"],
            "secret": token["secret"],
            "digits": token["digits"],
            "type": token["type"],
            "period": token["period"],
            "label": token_label,
        }


def main():
    if len(sys.argv) != 2:
        print("Usage: ./freeotp_migrate.py <filename>")
        sys.exit(1)

    # Dump the tokens.
    output = json.dumps(
        list(read_tokens(sys.argv[1])),
        sort_keys=True,
        indent=2,
        separators=(',', ': ')
    )

    print(output)


if __name__ == "__main__":
    main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment