Skip to content

Instantly share code, notes, and snippets.

@klustic
Created December 22, 2020 06:21
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save klustic/5d6c4f44f28c6fe5168c81849f027413 to your computer and use it in GitHub Desktop.
Save klustic/5d6c4f44f28c6fe5168c81849f027413 to your computer and use it in GitHub Desktop.
Parse seeds out of Google Authenticator migration QR codes
# Scan the Google Authenticator export QR code to get a otpauth-migration:// URI
# Then, provide that URL here and get your precious seeds back.
# The last components on each line of output are the 80-bit TOTP conforming seeds Authenticator uses, e.g.
#
# Output looks like this
# Some Service 1 (Andres) - JBUSYICBNZSHEZLT
# Some Service 2 (Kevin) - JZXXIICTN4QEEYLE
from urllib.parse import unquote
def consume(b: bytes):
l = b[0]
return b[1:l+1], b[l+2:]
uri = 'otpauth-migration://offline?data=CioKCkhpLCBBbmRyZXMSBkFuZHJlcxoOU29tZSBTZXJ2aWNlIDEgASgBMAIKKQoKTm90IFNvIEJhZBIFS2V2aW4aDlNvbWUgU2VydmljZSAyIAEoATACEAE%3D'
data = base64.b64decode(unquote(uri.split('=')[1]))
while len(data) > 3:
data = data[3:] # Nothing to see here
seed, data = consume(data)
account, data = consume(data)
service, data = consume(data)
print('{} ({}) - {}'.format(service.decode(), account.decode(), base64.b32encode(seed).decode()))
data = data[5:] # Nothing to see here
@markizano
Copy link

For anyone else who came across this first like I did, please allow me to point you in this direction:

https://github.com/scito/extract_otp_secrets

Learning a lot about protobuf helped a lot with this.

@klustic
Copy link
Author

klustic commented Mar 26, 2023

This is much cleaner, thanks for the link!

@arzcbnh
Copy link

arzcbnh commented Sep 13, 2023

Thank you, it worked perfectly! I just had to add import base64 after line 8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment