diff --git a/README.md b/README.md index 4be001b..87d72d0 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The protobuf package of Google for proto3 is required for running this script. For printing QR codes, the qrcode module is required - pip install qrcode + pip install qrcode[pil] ## Technical background diff --git a/extract_otp_secret_keys.py b/extract_otp_secret_keys.py index 365d31d..52e2881 100644 --- a/extract_otp_secret_keys.py +++ b/extract_otp_secret_keys.py @@ -46,17 +46,22 @@ import base64 import fileinput import sys from urllib.parse import parse_qs, urlencode, urlparse, quote - +from os import path, mkdir +from re import sub, compile as rcompile import generated_python.google_auth_pb2 arg_parser = argparse.ArgumentParser() arg_parser.add_argument('--verbose', '-v', help='verbose output', action='store_true') -arg_parser.add_argument('--qr', '-q', help='print QR codes (otpauth://...)', action='store_true') +arg_parser.add_argument('--saveqr', '-s', help='save QR code(s) as images to the "qr" subfolder', action='store_true') +arg_parser.add_argument('--printqr', '-p', help='print QR code(s) as text to the terminal', action='store_true') 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() verbose = args.verbose -if args.qr: +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 @@ -68,6 +73,13 @@ def convert_secret_from_bytes_to_base32_str(bytes): return str(base64.b32encode(otp.secret), 'utf-8').replace('=', '') +def save_qr(data, name): + qr = QRCode() + qr.add_data(data) + img = qr.make_image(fill_color="black", back_color="white") + if verbose: print("Saving to {}".format(name)) + img.save(name) + def print_qr(data): qr = QRCode() qr.add_data(data) @@ -85,7 +97,9 @@ for line in (line.strip() for line in fileinput.input(args.infile)): if verbose: print(payload) # pylint: disable=no-member + i = 0 for otp in payload.otp_parameters: + i += 1 print('\nName: {}'.format(otp.name)) secret = convert_secret_from_bytes_to_base32_str(otp.secret) print('Secret: {}'.format(secret)) @@ -95,6 +109,14 @@ 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 args.qr: + if saveqr: + if verbose: print(otp_url) + if not(path.exists("qr")): mkdir("qr") + pattern = rcompile('[\W_]+') + file_otp_name = pattern.sub('', otp.name) + file_otp_issuer = pattern.sub('', otp.issuer) + if not(file_otp_issuer): print_qr(otp_url, "qr/{}-{}.png".format(i, file_otp_name)) + 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)