refactor; update setup.py

more verbose logging
better error messages
cv2_1
scito 2 years ago
parent 06b8efff62
commit e754befb52

@ -69,32 +69,33 @@ Known to work with
For protobuf versions 3.14.0 or similar or Python 3.6, use the extract_otp_secret_keys version 1.4.0. For protobuf versions 3.14.0 or similar or Python 3.6, use the extract_otp_secret_keys version 1.4.0.
### Linux and macOS ### Shared libs installation for reading QR code images
For reading QR code images the zbar lib must be installed. For reading QR code images the zbar library must be installed.
If you do not extract directly from images, you do not need to install the zbar shared library.
Detailed [installation documentation for pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar#installation). For a detailed installation documentation of [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar#installation).
#### Windows #### Windows
The zbar DLLs are included with the Windows Python wheels. On other operating systems, you will need to install the zbar shared library. The zbar DLLs are included with the Windows Python wheels. On other operating systems, you will need to install the zbar shared library.
#### Mac OS X
brew install zbar
#### Linux (Debian, Ubuntu, ...) #### Linux (Debian, Ubuntu, ...)
sudo apt-get install libzbar0 sudo apt-get install libzbar0
#### Linux (OpenSUSE) #### Linux (OpenSUSE)
sudo zyper install libzbar0 sudo zypper install libzbar0
#### Linux (Fedora) #### Linux (Fedora)
sudo dnf install libzbar0 sudo dnf install libzbar0
#### Mac OS X
brew install zbar
## Examples ## Examples
### Printing otp secrets form text file ### Printing otp secrets form text file

@ -100,8 +100,11 @@ def extract_otps(args):
otps = [] otps = []
i = j = 0 i = j = k = 0
if verbose: print('Input files: {}'.format(args.infile))
for infile in args.infile: for infile in args.infile:
if verbose: print('Processing infile {}'.format(infile))
k += 1
for line in get_lines_from_file(infile): for line in get_lines_from_file(infile):
if verbose: print(line) if verbose: print(line)
if line.startswith('#') or line == '': continue if line.startswith('#') or line == '': continue
@ -134,79 +137,83 @@ def extract_otps(args):
print() print()
otps.append(otp) otps.append(otp)
if verbose: print('{} infile(s) processed'.format(k))
return otps return otps
def get_lines_from_file(filename): def get_lines_from_file(filename):
lines = read_lines_from_text_file(filename) # stdin stream cannot be rewinded, thus distinguish, use - for utf-8 stdin and = for binary image stdin
if are_bytes(lines): if filename != '=':
abort('\nBinary input was given in stdin, please use = instead of -.') check_file_exists(filename)
elif lines: lines = read_lines_from_text_file(filename)
return lines if lines:
return lines
# could not process text file, try reading as image # could not process text file, try reading as image
return convert_img_to_line(filename) if filename != '-':
return convert_img_to_line(filename)
def read_lines_from_text_file(filename): def read_lines_from_text_file(filename):
if filename != '=': if verbose: print('Reading lines of {}'.format(filename))
check_file_exists(filename) finput = fileinput.input(filename)
finput = fileinput.input(filename) try:
try: lines = []
lines = [] for line in (line.strip() for line in finput):
for line in (line.strip() for line in finput): if verbose: print(line)
# TODO improve if is_binary(line):
# if verbose: print(line) abort('\nBinary input was given in stdin, please use = instead of - as infile argument for images.')
# if line.startswith('#') or line == '': # unfortunately yield line leads to random test fails
# continue lines.append(line)
# unfortunately yield line leads to random test fails return lines
lines.append(line) except UnicodeDecodeError:
return lines if filename == '-':
except UnicodeDecodeError: abort('\nERROR: Unable to open text file form stdin. '
if filename == '-': 'In case you want read an image file from stdin, you must use "=" instead of "-".')
abort('\nERROR: Unable to open text file form stdin. ' else: # The file is probably an image, process below
'In case you want read an image file from stdin, you must use "=" instead of "-".') return None
else: # The file is probably an image, process below finally:
return None finput.close()
finally:
finput.close()
def convert_img_to_line(filename): def convert_img_to_line(filename):
if filename != '-': if verbose: print('Reading image {}'.format(filename))
try: try:
if filename != '=': if filename != '=':
image = imread(filename) image = imread(filename)
else: else:
try: try:
stdin = sys.stdin.buffer.read() stdin = sys.stdin.buffer.read()
except AttributeError: except AttributeError:
# Workaround for pytest, since pytest cannot monkeypatch sys.stdin.buffer # Workaround for pytest, since pytest cannot monkeypatch sys.stdin.buffer
stdin = sys.stdin.read() stdin = sys.stdin.read()
try:
array = frombuffer(stdin, dtype='uint8')
except TypeError as e:
abort('\nERROR: Cannot read binary stdin buffer. Exception: {}'.format(str(e)))
image = imdecode(array, IMREAD_UNCHANGED)
if image is None:
abort('\nERROR: Unable to open file for reading.\ninput file: {}'.format(filename))
# dynamic import of QReader since this module has a dependency to zbar lib
try: try:
from qreader import QReader array = frombuffer(stdin, dtype='uint8')
except ImportError as e: except TypeError as e:
abort('\nERROR: Cannot import QReader module probably due to missing zbar shared library. Exception:\n{}'.format(str(e))) abort('\nERROR: Cannot read binary stdin buffer. Exception: {}'.format(str(e)))
image = imdecode(array, IMREAD_UNCHANGED)
decoder = QReader() if image is None:
decoded_text = decoder.detect_and_decode(image=image) abort('\nERROR: Unable to open file for reading.\ninput file: {}'.format(filename))
if decoded_text is None:
abort('\nERROR: Unable to read QR Code from file.\ninput file: {}'.format(filename))
return [decoded_text] # dynamic import of QReader since this module has a dependency to zbar lib and import it only when necessary
except Exception as e: try:
abort('\nERROR: Encountered exception "{}".\ninput file: {}'.format(str(e), filename)) from qreader import QReader
except ImportError as e:
abort('''
ERROR: Cannot import QReader module. This problem is probably due to the missing zbar shared library.
On Linux and macOS libzbar0 must be installed.
See in README.md for the installation of the libzbar0.
Exception: {}'''.format(str(e)))
decoder = QReader()
decoded_text = decoder.detect_and_decode(image=image)
if decoded_text is None:
abort('\nERROR: Unable to read QR Code from file.\ninput file: {}'.format(filename))
return [decoded_text]
except Exception as e:
abort('\nERROR: Encountered exception "{}".\ninput file: {}'.format(str(e), filename))
def get_payload_from_line(line, i, infile): def get_payload_from_line(line, i, infile):
@ -392,13 +399,12 @@ def check_file_exists(filename):
'\ninput file: {}'.format(filename)) '\ninput file: {}'.format(filename))
def are_bytes(lines): def is_binary(line):
if lines and len(lines) > 0: try:
try: line.startswith('#')
lines[0].startswith('#') return False
return False except (UnicodeDecodeError, AttributeError, TypeError):
except (UnicodeDecodeError, AttributeError, TypeError): return True
return True
def eprint(*args, **kwargs): def eprint(*args, **kwargs):

@ -48,7 +48,9 @@ setup(
install_requires=[ install_requires=[
'protobuf', 'protobuf',
'qrcode', 'qrcode',
'Pillow' 'Pillow',
'qreader',
'opencv-python'
], ],
project_urls={ project_urls={

@ -1,3 +1,24 @@
Input files: ['example_export.txt']
Processing infile example_export.txt
Reading lines of example_export.txt
# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/
# Secret key: 7KSQL2JTUDIS5EF65KLMRQIIGY
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
otpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACEAEYASAAKLzjp5n4%2F%2F%2F%2F%2FwE%3D
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpIAEoATACCjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACiQ7OOa%2Bf%2F%2F%2F%2F8B
# otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
otpauth-migration://offline?data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D
# otpauth://totp/encoding%3A%20%C2%BF%C3%A4%C3%84%C3%A9%C3%89%3F%20%28demo%29?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
# Name: "encoding: ¿äÄéÉ? (demo)"
otpauth-migration://offline?data=CjYKEPqlBekzoNEukL7qlsjBCDYSHGVuY29kaW5nOiDCv8Okw4TDqcOJPyAoZGVtbykgASgBMAIQARgBIAAorfCurv%2F%2F%2F%2F%2F%2FAQ%3D%3D
# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/ # 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/
# Secret key: 7KSQL2JTUDIS5EF65KLMRQIIGY # Secret key: 7KSQL2JTUDIS5EF65KLMRQIIGY
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi # otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
@ -136,3 +157,4 @@ Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
Type: totp Type: totp
otpauth://totp/encoding%3A%20%C2%BF%C3%A4%C3%84%C3%A9%C3%89%3F%20%28demo%29?secret=7KSQL2JTUDIS5EF65KLMRQIIGY otpauth://totp/encoding%3A%20%C2%BF%C3%A4%C3%84%C3%A9%C3%89%3F%20%28demo%29?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
1 infile(s) processed

@ -562,7 +562,7 @@ def test_img_qr_reader_from_stdin_wrong_symbol(capsys, monkeypatch):
# Assert # Assert
captured = capsys.readouterr() captured = capsys.readouterr()
expected_stderr = '\nBinary input was given in stdin, please use = instead of -.\n' expected_stderr = '\nBinary input was given in stdin, please use = instead of - as infile argument for images.\n'
assert captured.err == expected_stderr assert captured.err == expected_stderr
assert captured.out == '' assert captured.out == ''

@ -23,7 +23,7 @@ from utils import Capturing
import extract_otp_secret_keys import extract_otp_secret_keys
class TestExtract(unittest.TestCase): class TestQRImageExtract(unittest.TestCase):
def test_img_qr_reader_happy_path(self): def test_img_qr_reader_happy_path(self):
with Capturing() as actual_output: with Capturing() as actual_output:
extract_otp_secret_keys.main(['test/test_googleauth_export.png']) extract_otp_secret_keys.main(['test/test_googleauth_export.png'])

Loading…
Cancel
Save