refactor; update setup.py

more verbose logging
better error messages
This commit is contained in:
scito 2022-12-24 15:30:17 +01:00
parent 06b8efff62
commit e754befb52
6 changed files with 108 additions and 77 deletions

View File

@ -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.
### 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
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, ...)
sudo apt-get install libzbar0
#### Linux (OpenSUSE)
sudo zyper install libzbar0
sudo zypper install libzbar0
#### Linux (Fedora)
sudo dnf install libzbar0
#### Mac OS X
brew install zbar
## Examples
### Printing otp secrets form text file

View File

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

View File

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

View File

@ -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/
# Secret key: 7KSQL2JTUDIS5EF65KLMRQIIGY
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
@ -136,3 +157,4 @@ Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
Type: totp
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

View File

@ -562,7 +562,7 @@ def test_img_qr_reader_from_stdin_wrong_symbol(capsys, monkeypatch):
# Assert
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.out == ''

View File

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