Skip to content

Instantly share code, notes, and snippets.

@jojonas
Last active September 9, 2015 13:42
Show Gist options
  • Select an option

  • Save jojonas/af8d29be87aa0f95f123 to your computer and use it in GitHub Desktop.

Select an option

Save jojonas/af8d29be87aa0f95f123 to your computer and use it in GitHub Desktop.

Revisions

  1. jojonas revised this gist Sep 9, 2015. 1 changed file with 28 additions and 2 deletions.
    30 changes: 28 additions & 2 deletions one-time-token.py
    Original file line number Diff line number Diff line change
    @@ -43,7 +43,7 @@ def calckey(secret, timestamp):
    # general steps:
    # 1. have root access
    # 2. use adb shell to pull /data/data/com.google.android.apps.authenticator2/databases
    def from_database(dbfile):
    def from_database(dbfile, enable_plot=False):
    import sqlite3

    now = int(time.time())
    @@ -65,10 +65,36 @@ def from_database(dbfile):
    for name, secret in cursor:
    key = calckey(secret, now)
    print("{:>25} {}".format(name, key))
    if enable_plot:
    plot(name, secret)
    print("-"*width)

    # plots the QR-code using the matplotlib library:
    # the QR code contains a URI, but is encoded in the QR-code "text" mode
    # this can currently not be fixed, it works with the Google Authentificator App though
    def plot(name, secret):
    import qrcode
    import matplotlib.pyplot as plt
    qr = qrcode.QRCode(
    version=None,
    error_correction=qrcode.constants.ERROR_CORRECT_L,
    box_size=1,
    border=1,
    )
    url = "otpauth://totp/{:s}?secret={:s}".format(name, secret)
    qr.add_data(url)
    qr.make(fit=True)
    img = qr.make_image()

    plt.clf()
    plt.imshow(img, cmap="Greys", interpolation="nearest")
    plt.axis('off')
    plt.title(name)
    plt.show()

    if __name__=="__main__":
    parser = argparse.ArgumentParser(description="Two-Step-Token generator (RFC 6238)")
    parser.add_argument("-p", "--plot", action="store_true", help="plot the secrets via the qrcode module and Matplotlib (for transfering to a new device)")
    parser.add_argument("database", type=str, help="sqlite database (dumped from Android)")
    args = parser.parse_args()
    from_database(args.database)
    from_database(args.database, args.plot)
  2. jojonas revised this gist Feb 24, 2015. 1 changed file with 18 additions and 19 deletions.
    37 changes: 18 additions & 19 deletions one-time-token.py
    Original file line number Diff line number Diff line change
    @@ -8,51 +8,51 @@
    # pads the string to a multiple of 8 bytes using "="
    def pad_base32(string):
    return string + "=" * (8 - ((len(string)-1) % 8) - 1)

    # calculates the authentification key for the specified secret and timestamp
    # more info: https://en.wikipedia.org/wiki/Google_Authenticator
    def calckey(secret, timestamp):
    # pad secret to multiple of 8 characters
    secret = pad_base32(secret)

    # decode base32 to byte string (ignoring case)
    key = base64.b32decode(secret, casefold=True)
    # interpret Unix timestamp / 30sec as 8 byte unsigned long long

    # interpret Unix timestamp / 30sec as 8 byte unsigned long long
    message = int(timestamp/30).to_bytes(8, byteorder="big", signed=False)

    # combine message (time) and key (secret) into one authentification code using sha1
    hash = hmac.new(key, message, hashlib.sha1).digest()
    code = hmac.new(key, message, hashlib.sha1).digest()

    # extract offset (last 4 bits of HMAC)
    offset = hash[-1] & 0x0f
    offset = code[-1] & 0x0f

    # extract 4 bytes starting at offset, interpret as 4 byte unsigned int
    truncatedHash = int.from_bytes(hash[offset:offset+4], byteorder="big", signed=False)
    truncatedHash = int.from_bytes(code[offset:offset+4], byteorder="big", signed=False)

    # unset the first bit
    truncatedHash &= ~(1 << (8*4-1))
    truncatedHash &= ~(1 << (8*4-1))

    # restrict code to 6 digits and convert to string
    code = truncatedHash % 1000000
    codeStr = "%06d" % code

    return codeStr

    # to pull the database from Android, see https://nucleussystems.com/blog/android-backup-google-authenticator/
    # general steps:
    # general steps:
    # 1. have root access
    # 2. use adb shell to pull /data/data/com.google.android.apps.authenticator2/databases
    def from_database(dbfile):
    import sqlite3

    now = int(time.time())
    remaining = 30 - (now % 30)

    with sqlite3.connect(dbfile) as connection:
    cursor = connection.cursor()
    cursor.execute("SELECT email, secret FROM accounts")

    # output code is a little ugly :/
    width = 50
    print("="*width)
    @@ -66,10 +66,9 @@ def from_database(dbfile):
    key = calckey(secret, now)
    print("{:>25} {}".format(name, key))
    print("-"*width)

    if __name__=="__main__":
    parser = argparse.ArgumentParser(description="Two-Step-Token generator (RFC 6238)")
    parser.add_argument("database", type=str, help="sqlite database (dumped from Android)")
    args = parser.parse_args()
    from_database(args.database)

  3. jojonas renamed this gist Feb 23, 2015. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. jojonas renamed this gist Feb 20, 2015. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. jojonas revised this gist Feb 20, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.py
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@
    def pad_base32(string):
    return string + "=" * (8 - ((len(string)-1) % 8) - 1)

    # calculatess the authentification key for the specified secret and timestamp
    # calculates the authentification key for the specified secret and timestamp
    # more info: https://en.wikipedia.org/wiki/Google_Authenticator
    def calckey(secret, timestamp):
    # pad secret to multiple of 8 characters
  6. jojonas created this gist Feb 20, 2015.
    75 changes: 75 additions & 0 deletions gistfile1.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,75 @@
    import argparse
    import time

    import base64
    import hmac
    import hashlib

    # pads the string to a multiple of 8 bytes using "="
    def pad_base32(string):
    return string + "=" * (8 - ((len(string)-1) % 8) - 1)

    # calculatess the authentification key for the specified secret and timestamp
    # more info: https://en.wikipedia.org/wiki/Google_Authenticator
    def calckey(secret, timestamp):
    # pad secret to multiple of 8 characters
    secret = pad_base32(secret)

    # decode base32 to byte string (ignoring case)
    key = base64.b32decode(secret, casefold=True)

    # interpret Unix timestamp / 30sec as 8 byte unsigned long long
    message = int(timestamp/30).to_bytes(8, byteorder="big", signed=False)

    # combine message (time) and key (secret) into one authentification code using sha1
    hash = hmac.new(key, message, hashlib.sha1).digest()

    # extract offset (last 4 bits of HMAC)
    offset = hash[-1] & 0x0f

    # extract 4 bytes starting at offset, interpret as 4 byte unsigned int
    truncatedHash = int.from_bytes(hash[offset:offset+4], byteorder="big", signed=False)

    # unset the first bit
    truncatedHash &= ~(1 << (8*4-1))

    # restrict code to 6 digits and convert to string
    code = truncatedHash % 1000000
    codeStr = "%06d" % code

    return codeStr

    # to pull the database from Android, see https://nucleussystems.com/blog/android-backup-google-authenticator/
    # general steps:
    # 1. have root access
    # 2. use adb shell to pull /data/data/com.google.android.apps.authenticator2/databases
    def from_database(dbfile):
    import sqlite3

    now = int(time.time())
    remaining = 30 - (now % 30)

    with sqlite3.connect(dbfile) as connection:
    cursor = connection.cursor()
    cursor.execute("SELECT email, secret FROM accounts")

    # output code is a little ugly :/
    width = 50
    print("="*width)
    print("Two-Step-Token generator (RFC 6238)".center(width))
    print("="*width)
    print("Loaded from databases file '{:s}'.".format(dbfile).center(width))
    print("Remaining time: {:d} sec\n".format(remaining).center(width))
    print("{:>25} {}".format("Account", "Key"))
    print("-"*width)
    for name, secret in cursor:
    key = calckey(secret, now)
    print("{:>25} {}".format(name, key))
    print("-"*width)

    if __name__=="__main__":
    parser = argparse.ArgumentParser(description="Two-Step-Token generator (RFC 6238)")
    parser.add_argument("database", type=str, help="sqlite database (dumped from Android)")
    args = parser.parse_args()
    from_database(args.database)