@ -2,10 +2,10 @@
#
# Usage:
# 1. Export the QR codes from "Google Authenticator" app
# 2. Read QR codes with QR code reader
# 2. Read QR codes with QR code reader (e.g. with a second device)
# 3. Save the captured QR codes in a text file. Save each QR code on a new line. (The captured QR codes look like "otpauth-migration://offline?data=...")
# 4. Call this script with the file as input:
# python extract_otp_secret_keys.py - q example_export.txt
# python extract_otp_secret_keys.py - p example_export.txt
#
# Requirement:
# The protobuf package of Google for proto3 is required for running this script.
@ -57,12 +57,8 @@ arg_parser.add_argument('--printqr', '-p', help='print QR code(s) as text to the
arg_parser . add_argument ( ' infile ' , help = ' file or - for stdin (default: -) with " otpauth-migration://... " URLs separated by newlines, lines starting with # are ignored ' )
args = arg_parser . parse_args ( )
if args . saveqr or args . printqr : from qrcode import QRCode
verbose = args . verbose
saveqr = args . saveqr
printqr = args . printqr
if saveqr or printqr :
from qrcode import QRCode
# https://stackoverflow.com/questions/40226049/find-enums-listed-in-python-descriptor-for-protobuf
def get_enum_name_by_number ( parent , field_name ) :
@ -84,6 +80,7 @@ def print_qr(data):
qr . add_data ( data )
qr . print_tty ( )
i = j = 0
for line in ( line . strip ( ) for line in fileinput . input ( args . infile ) ) :
if verbose : print ( line )
if line . startswith ( ' # ' ) or line == ' ' : continue
@ -97,13 +94,15 @@ for line in (line.strip() for line in fileinput.input(args.infile)):
data = base64 . b64decode ( data_encoded )
payload = generated_python . google_auth_pb2 . MigrationPayload ( )
payload . ParseFromString ( data )
if verbose : print ( payload )
i + = 1
if verbose : print ( ' \n {} . Payload Line ' . format ( i ) , payload , sep = ' \n ' )
# pylint: disable=no-member
i = 0
for otp in payload . otp_parameters :
i + = 1
print ( ' \n Name: {} ' . format ( otp . name ) )
j + = 1
if verbose : print ( ' \n {} . Secret Key ' . format ( j ) )
else : print ( )
print ( ' Name: {} ' . format ( otp . name ) )
secret = convert_secret_from_bytes_to_base32_str ( otp . secret )
print ( ' Secret: {} ' . format ( secret ) )
if otp . issuer : print ( ' Issuer: {} ' . format ( otp . issuer ) )
@ -112,14 +111,12 @@ for line in (line.strip() for line in fileinput.input(args.infile)):
if otp . type == 1 : url_params [ ' counter ' ] = otp . counter
if otp . issuer : url_params [ ' issuer ' ] = otp . issuer
otp_url = ' otpauth:// {} / {} ? ' . format ( ' totp ' if otp . type == 2 else ' hotp ' , quote ( otp . name ) ) + urlencode ( url_params )
if saveqr :
if verbose : print ( otp_url )
if verbose : print ( otp_url )
if args . printqr :
print_qr ( otp_url )
if args . saveqr :
if not ( path . exists ( ' qr ' ) ) : mkdir ( ' qr ' )
pattern = rcompile ( r ' [ \ W_]+ ' )
file_otp_name = pattern . sub ( ' ' , otp . name )
file_otp_issuer = pattern . sub ( ' ' , otp . issuer )
if not ( file_otp_issuer ) : print_qr ( otp_url )
if file_otp_issuer : save_qr ( otp_url , ' qr/ {} - {} - {} .png ' . format ( i , file_otp_name , file_otp_issuer ) )
if printqr :
if verbose : print ( otp_url )
print_qr ( otp_url )
save_qr ( otp_url , ' qr/ {} - {} {} .png ' . format ( j , file_otp_name , ' - ' + file_otp_issuer if file_otp_issuer else ' ' ) )