mirror of
https://github.com/scito/extract_otp_secret_keys
synced 2024-11-16 06:15:17 +00:00
refactor; update setup.py
more verbose logging better error messages
This commit is contained in:
parent
06b8efff62
commit
e754befb52
17
README.md
17
README.md
@ -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
|
||||
|
@ -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):
|
||||
|
4
setup.py
4
setup.py
@ -48,7 +48,9 @@ setup(
|
||||
install_requires=[
|
||||
'protobuf',
|
||||
'qrcode',
|
||||
'Pillow'
|
||||
'Pillow',
|
||||
'qreader',
|
||||
'opencv-python'
|
||||
],
|
||||
|
||||
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/
|
||||
# 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
|
||||
|
@ -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 == ''
|
||||
|
@ -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'])
|
||||
|
Loading…
Reference in New Issue
Block a user