diff --git a/README.md b/README.md index a4a1c5c..22e1f1b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Extract TOTP/HOTP two-factor authentication secrets +# Extract secrets from QR codes exported by two-factor authentication apps [![CI tests](https://github.com/scito/extract_otp_secrets/actions/workflows/ci.yml/badge.svg)](https://github.com/scito/extract_otp_secrets/actions/workflows/ci.yml) ![coverage](https://img.shields.io/badge/coverage-95%25-brightgreen) @@ -12,40 +12,74 @@ --- -TODO add src/ -TODO mention renaming extract_otp_secrets +The Python script `extract_otp_secrets.py` extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps such as "Google Authenticator". +The export QR codes from authentication apps can be provided in three ways to this script: -Extract one time passwords (OTP) / two-factor authentication (2FA, TFA) secrets from export QR codes from 2FA authentication apps such as "Google Authenticator". -The QR codes can be captured from the system camera, imported from images or from text files containing the QR code data. -The secret and otp values can be printed and exported to json or csv, as well as printed or saved to PNG images. +1. Capture from the system camera in a GUI, +2. Image files containing the QR codes, and +3. Text files containing the QR code data generated by QR readers. + +The secret and otp values can be exported to json or csv files, as well as printed or saved to PNG images. + +This script/project was renamed from extract_otp_secret_keys to extract_otp_secrets in version 2.0.0. ## Installation +``` git clone https://github.com/scito/extract_otp_secrets.git cd extract_otp_secrets +pip install -U -r requirements.txt +``` + +### Installation of shared libraries for ZBAR QR reader + +For reading QR codes with `ZBAR` QR reader, the zbar library must be installed. +If you do not use the `ZBAR` QR reader, you do not need to install the zbar shared library. Note: The `ZBAR` QR reader is the showed for me the best results and is thus default QR Reader. + +For a detailed installation documentation of [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar#installation). + +#### Linux (Debian, Ubuntu, ...) + + sudo apt-get install libzbar0 + +#### Linux (OpenSUSE) + + sudo zypper install libzbar0 + +#### Linux (Fedora) + + sudo dnf install libzbar0 + +#### Mac OS X + + brew install zbar + +#### Windows + +The zbar DLLs are included with the Windows Python wheels. However, you might need additionally to install [Visual C++ Redistributable Packages for Visual Studio 2013](https://www.microsoft.com/en-US/download/details.aspx?id=40784). Install `vcredist_x64.exe` if using 64-bit Python, `vcredist_x86.exe` if using 32-bit Python. ## Usage -### Capture QR codes from camera (since v2.0.0) +### Capture QR codes from camera (since version 2.0.0) 1. Open "Google Authenticator" app on the mobile phone 2. Export the QR codes from "Google Authenticator" app 3. Point the QR codes to the camera of your computer -4. Call this script with the file as input: +4. Call this script without infile parameters: - python extract_otp_secrets.py + python src/extract_otp_secrets.py ![CV2 Capture from camera screenshot](cv2_capture_screenshot.png) -### With builtin QR decoder from image files (since v2.0.0) +### With builtin QR decoder from image files (since version 2.0.0) 1. Open "Google Authenticator" app on the mobile phone 2. Export the QR codes from "Google Authenticator" app -4. Save the captured QR codes as image files, e.g. example_export.png +4. Save the QR code as image file, e.g. example_export.png 5. Transfer the images files to the computer where his script is installed. 6. Call this script with the file as input: - python extract_otp_secrets.py example_export.png + python src/extract_otp_secrets.py example_export.png ### With external QR decoder app from text files @@ -56,14 +90,12 @@ cd extract_otp_secrets 5. Transfer the file to the computer where his script is installed. 6. Call this script with the file as input: - python extract_otp_secrets.py example_export.txt + python src/extract_otp_secrets.py example_export.txt ## Program help: arguments and options -
usage: extract_otp_secrets.py [-h] [--camera NUMBER] [--qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}] [--json FILE] [--csv FILE] [--keepass FILE] [--printqr] [--saveqr DIR] [--no-color] [--verbose | --quiet] [infile ...]
-
-Extracts one time password (OTP) / two-factor authentication (2FA) secrets from export QR codes, e.g. from Google Authenticator app.
-If no infiles are provided, the QR codes a GUI window starts and QR codes can interactively be captured from the system camera.
+
Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps
+If no infiles are provided, a GUI window starts and QR codes are captured from the camera.
 
 positional arguments:
   infile                        a) file or - for stdin with 'otpauth-migration://...' URLs separated by newlines, lines starting with # are ignored;
@@ -71,17 +103,18 @@ positional arguments:
 
 options:
   -h, --help                    show this help message and exit
-  --camera NUMBER, -C NUMBER    camera number of system (default camera: 0)
-  --qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}, -Q {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}
-                                QR reader (default: ZBAR)
-  --json FILE, -j FILE          export json file or - for stdout
   --csv FILE, -c FILE           export csv file or - for stdout
   --keepass FILE, -k FILE       export totp/hotp csv file(s) for KeePass, - for stdout
+  --json FILE, -j FILE          export json file or - for stdout
   --printqr, -p                 print QR code(s) as text to the terminal (requires qrcode module)
   --saveqr DIR, -s DIR          save QR code(s) as images to the given folder (requires qrcode module)
+  --camera NUMBER, -C NUMBER    camera number of system (default camera: 0)
+  --qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}, -Q {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}
+                                QR reader (default: ZBAR)
+  -i, --ignore                  ignore duplicate otps
   --no-color, -n                do not use ANSI colors in console output
-  --verbose, -v                 verbose output
-  --quiet, -q                   no stdout output, except output set by -
+  -v, --verbose                 verbose output
+  -q, --quiet                   no stdout output, except output set by -
 
 examples:
 python extract_otp_secrets.py
@@ -90,92 +123,54 @@ python extract_otp_secrets.py - < example_export.txt
 python extract_otp_secrets.py --csv - example_*.png | tail -n+2
 python extract_otp_secrets.py = < example_export.png
-## Dependencies - - pip install -r requirements.txt - -Known to work with - -* Python 3.10.8, protobuf 4.21.9, qrcode 7.3.1, and pillow 9.2 -* Python 3.11.1, protobuf 4.21.12, qrcode 7.3.1, and pillow 9.2 - -For protobuf versions 3.14.0 or similar or Python 3.6, use the extract_otp_secrets version 1.4.0. - -### Shared libs installation for reading QR code images - -For reading QR code images the zbar library must be installed for `ZBAR` QR reader. -If you do not extract directly from images or using catpuring from camera, you do not need to install the zbar shared library. - -For a detailed installation documentation of [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar#installation). - -#### Linux (Debian, Ubuntu, ...) - - sudo apt-get install libzbar0 - -#### Linux (OpenSUSE) - - sudo zypper install libzbar0 - -#### Linux (Fedora) - - sudo dnf install libzbar0 - -#### Mac OS X - - brew install zbar - -#### Windows - -The zbar DLLs are included with the Windows Python wheels. However, you might need to install [Visual C++ Redistributable Packages for Visual Studio 2013](https://www.microsoft.com/en-US/download/details.aspx?id=40784). Install vcredist_x64.exe if using 64-bit Python, vcredist_x86.exe if using 32-bit Python. - ## Examples ### Printing otp secrets form text file - python extract_otp_secrets.py example_export.txt + python src/extract_otp_secrets.py example_export.txt ### Printing otp secrets from image file - python extract_otp_secrets.py example_export.png + python src/extract_otp_secrets.py example_export.png ### Printing otp secrets multiple files - python extract_otp_secrets.py example_*.txt - python extract_otp_secrets.py example_*.png - python extract_otp_secrets.py example_export.* - python extract_otp_secrets.py example_*.txt example_*.png + python src/extract_otp_secrets.py example_*.txt + python src/extract_otp_secrets.py example_*.png + python src/extract_otp_secrets.py example_export.* + python src/extract_otp_secrets.py example_*.txt example_*.png ### Printing otp secrets from stdin (text) - python extract_otp_secrets.py - < example_export.txt + python src/extract_otp_secrets.py - < example_export.txt ### Printing otp secrets from stdin (image) - python extract_otp_secrets.py = < example_export.png + python src/extract_otp_secrets.py = < example_export.png ### Printing otp secrets csv to stdout - python extract_otp_secrets.py --csv - example_export.txt + python src/extract_otp_secrets.py --csv - example_export.txt ### Printing otp secrets csv to stdout without header line - python extract_otp_secrets.py --csv - example_*.png | tail -n+2 + python src/extract_otp_secrets.py --csv - example_*.png | tail -n+2 ### Reading from stdin and printing to stdout - cat example_*.txt | python extract_otp_secrets.py --csv - - | tail -n+2 + cat example_*.txt | python src/extract_otp_secrets.py --csv - - | tail -n+2 ## Features * Free and open source * Supports Google Authenticator exports (and compatible apps like Aegis Authenticator) -* Captures the the QR codes directly from the camera using different QR code readers (based on OpenCV) - * ZBAR: fast and reliable, good for images and video capture (default/recommended) [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar) - * QREADER: fast [QReader](https://github.com/Eric-Canas/QReader) - * QREADER_DEEP: slow, not good for camera [QReader](https://github.com/Eric-Canas/QReader) - * CV2: fast [QRCodeDetector](https://docs.opencv.org/4.x/de/dc3/classcv_1_1QRCodeDetector.html) - * CV2_WECHAT: fast [WeChatQRCode](https://docs.opencv.org/4.x/dd/d63/group__wechat__qrcode.html) -* Supports TOTP and HOTP +* Captures the the QR codes directly from the camera using different QR code libraries (based on OpenCV) + * ZBAR: [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar) - fast and reliable, good for images and video capture (default and recommended) + * QREADER: [QReader](https://github.com/Eric-Canas/QReader) + * QREADER_DEEP: [QReader](https://github.com/Eric-Canas/QReader) - very slow in GUI + * CV2: [QRCodeDetector](https://docs.opencv.org/4.x/de/dc3/classcv_1_1QRCodeDetector.html) + * CV2_WECHAT: [WeChatQRCode](https://docs.opencv.org/4.x/dd/d63/group__wechat__qrcode.html) +* Supports TOTP and HOTP standards * Generates QR codes * Exports to various formats: * CSV @@ -191,7 +186,8 @@ The zbar DLLs are included with the Windows Python wheels. However, you might ne * Windows bitmaps - *.bmp, *.dib * JPEG 2000 files - *.jp2 * Portable image format - *.pbm, *.pgm, *.ppm *.pxm, *.pnm -* Errors and warnings are written to stderr and these are printed in red color on all platforms +* Prints errors and warnings to stderr +* Prints colored output * Many ways to run the script: * Native Python * pipenv @@ -200,6 +196,7 @@ The zbar DLLs are included with the Windows Python wheels. However, you might ne * Docker * VSCode devcontainer * devbox +* Prebuilt Docker images provided for amd64 and arm64 * Compatible with major platforms: * Linux * macOS @@ -208,7 +205,7 @@ The zbar DLLs are included with the Windows Python wheels. However, you might ne * Supports Python >= 3.7 * Written in modern Python using type hints and following best practices * All these features are backed by tests ran nightly -* All functionality in one Python script: extract_otp_secrets.py (except protobuf generated code in protobuf_generated_python) +* All functionality in one Python script: src/extract_otp_secrets.py (except protobuf generated code in protobuf_generated_python) ## KeePass @@ -255,11 +252,11 @@ The data parameter is a base64 encoded proto3 message (Google Protocol Buffers). Command for regeneration of Python code from proto3 message definition file (only necessary in case of changes of the proto3 message definition or new protobuf versions): - protoc --python_out=protobuf_generated_python google_auth.proto --mypy_out=protobuf_generated_python + protoc -plugin=protoc-gen-mypy=path/to/protoc-gen-mypy --python_out=protobuf_generated_python --mypy_out=protobuf_generated_python google_auth.proto The generated protobuf Python code was generated by protoc 21.12 (https://github.com/protocolbuffers/protobuf/releases/tag/v21.12). -https://github.com/nipunn1313/mypy-protobuf +For Python type hint generation the [mypy-protobuf](https://github.com/nipunn1313/mypy-protobuf) package is used. ## References @@ -284,35 +281,20 @@ pip install -U git+https://github.com/scito/extract_otp_secrets python -m extract_otp_secrets ``` -#### For development +or from a specific tag ``` -pip install -U git+https://github.com/scito/extract_otp_secrets.git@support_img_read -python -m extract_otp_secrets -``` - -``` - -https://stackoverflow.com/questions/13685920/install-specific-git-commit-with-pip --e for egg -pip install -U -e git+ - -# pip install -U -e git+https://github.com/scito/extract_otp_secrets@$(git ls-remote git@github.com:scito/extract_otp_secrets@support_img_read.git | head -1 | awk '{print $1;}')#egg=extract_otp_secrets -pip3.11 install -e git+https://github.com/scito/extract_otp_secrets.git@$(git ls-remote git@github.com:scito/extract_otp_secrets.git | grep support_img_read | head -1 | awk '{print $1;}')#egg=extract_otp_secrets +pip install -U git+https://github.com/scito/extract_otp_secrets.git@v2.0.0 python -m extract_otp_secrets -``` - -#### Example - -``` -wget https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt -python -m extract_otp_secrets example_export.txt +curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | python -m extract_otp_secrets - ``` ### local pip ``` -pip install -U -e . +git clone https://github.com/scito/extract_otp_secrets.git +pip install -U -e extract_otp_secrets +python -m extract_otp_secrets example_export.txt ``` ### pipenv @@ -323,7 +305,7 @@ You can you use [Pipenv](https://github.com/pypa/pipenv) for running extract_otp pipenv --rm pipenv install pipenv shell -python extract_otp_secrets.py example_export.txt +python src/extract_otp_secrets.py example_export.txt ``` ### Visual Studio Code Remote - Containers / VSCode devcontainer @@ -337,7 +319,7 @@ Requirement: Docker 3. Open VSCode command palette (Ctrl-Shift-P) 4. Type command "Remote-Containers: Reopen in Container" 5. Open integrated bash terminal in VSCode -6. Execute: python extract_otp_secrets.py example_export.txt +6. Execute: python src/extract_otp_secrets.py example_export.txt ### venv @@ -359,56 +341,51 @@ Install [devbox](https://github.com/jetpack-io/devbox), which is a wrapper for n devbox shell ``` -### Docker +### docker Install [Docker](https://docs.docker.com/get-docker/). -Build and run the app within the container: +Prebuilt docker images are available for amd64 and arm64 architectures on [Docker Hub](https://hub.docker.com/repository/docker/scit0/extract_otp_secrets) and on [GitHub Packages](https://github.com/users/scito/packages/container/package/extract_otp_secrets). -```bash -docker build . -t extract_otp_secrets --pull -docker run --rm -v "$(pwd)":/files:ro extract_otp_secrets example_export.txt -docker run --rm -v "$(pwd)":/files:ro extract_otp_secrets example_export.png +Extracting from an QR image file: + +``` +docker login -u USERNAME +curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets = ``` -TODO link to docker/github repos +Capturing from camera in GUI (X Window system required on host): -docker run --rm -v "$(pwd)":/files:ro -i extract_otp_secrets = < example_export.png -docker run --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secrets +``` docker run --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro scit0/extract_otp_secrets -docker run --entrypoint /bin/bash -it --rm -v "$(pwd)":/files:ro extract_otp_secrets -docker run --pull always --rm -v "$(pwd)":/files:ro -i scit0/extract_otp_secrets - -docker login -uscit0 -docker build . -t extract_otp_secrets_only_txt -f Dockerfile_only_txt --pull -docker build . -t extract_otp_secrets_only_txt -f Dockerfile_only_txt --pull --build-arg RUN_TESTS=false -docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets_only_txt extract_otp_secrets_test.py -k "not qreader" --relaxed -docker run --rm -v "$(pwd)":/files:ro extract_otp_secrets_only_txt example_export.txt -docker run --rm -v "$(pwd)":/files:ro -i extract_otp_secrets_only_txt - < example_export.txt -docker build . -t extract_otp_secrets_only_txt -f Dockerfile_only_txt --pull && docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets_only_txt extract_otp_secrets_test.py -k "not qreader" -vvv --relaxed -s +``` -docker pull scit0/extract_otp_secrets -docker pull scit0/extract_otp_secrets_only_txt +If only text processing is required, there is a small Image based on Alpine Linux: -docker pull ghcr.io/scito/extract_otp_secrets -docker pull ghcr.io/scito/extract_otp_secrets_only_txt +``` +curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | docker run --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest-only-txt - +``` -### Docker examples +Docker image from GitHub: +``` +docker login ghcr.io -u USERNAME +curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --pull always -i --rm -v "$(pwd)":/files:ro ghcr.io/scito/extract_otp_secrets = +``` -docker run --pull always --rm -v \"$(pwd)\":/files:ro scit0/extract_otp_secrets example_export.png +### More docker examples -docker run --pull always --rm -i -v \"$(pwd)\":/files:ro scit0/extract_otp_secrets_only_txt - < example_export.txt + docker run --pull always --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets example_export.png -cat example_export.txt | docker run --pull always --rm -i -v \"$(pwd)\":/files:ro scit0/extract_otp_secrets_only_txt - -c - > example_out.csv + docker run --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets_only_txt - < example_export.txt -docker run --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro scit0/extract_otp_secrets + cat example_export.txt | docker run --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest_only_txt - -c - > example_out.csv ## Tests ### PyTest -There are basic [pytest](https://pytest.org)s, see `extract_otp_secrets_test.py`. +The script is covered by [pytest](https://pytest.org)s, see `extract_otp_secrets_test.py`. Run tests: @@ -432,7 +409,7 @@ https://docs.pytest.org/en/7.1.x/explanation/pythonpath.html#pytest-vs-python-m- ### unittest -There are basic [unittest](https://docs.python.org/3.10/library/unittest.html)s, see `extract_otp_secrets_txt_unit_test.py`. +There are some [unittest](https://docs.python.org/3.10/library/unittest.html)s, see `extract_otp_secrets_txt_unit_test.py`. Run tests: @@ -440,7 +417,7 @@ Run tests: python -m unittest ``` -Note the `pytest`s are preferred and more complete. For each unittest there is also a test in `pytest`. +Note the `pytest`s are preferred and complete. For each unittest there is also a test in `pytest`. ### VSCode Setup @@ -460,23 +437,40 @@ pip install -U -e . python src/extract_otp_secrets.py pip wheel . -# --isolated -# --prefer-binary - -python3.11 -m build --wheel -# = -pip wheel --no-deps . ``` +Note: `python -m build --wheel` = `pip wheel --no-deps .` + ### Upgrade pip Packages ``` pip install -U -r requirements.txt ``` +### Build docker images + +Build and run the app within the container: + +```bash +docker build . -t extract_otp_secrets --pull --build-arg RUN_TESTS=false +``` + +```bash +docker build . -t extract_otp_secrets_only_txt --pull -f Dockerfile_only_txt --build-arg RUN_TESTS=false +``` + +Run tests in docker container: +```bash +docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets +``` + +```bash +docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets_only_txt extract_otp_secrets_test.py -k "not qreader" --relaxed +``` + ## Issues -https://github.com/opencv/opencv/issues/23072 +* Known issue for macOS: https://github.com/opencv/opencv/issues/23072 ## Problems and Troubleshooting @@ -505,8 +499,6 @@ Traceback (most recent call last): FileNotFoundError: Could not find module 'libiconv.dll' (or one of its dependencies). Try using the full path with constructor syntax. ``` - - ## Related projects * [ZBar](https://github.com/mchehab/zbar) is an open source software suite for reading bar codes from various sources, including webcams. @@ -520,6 +512,4 @@ FileNotFoundError: Could not find module 'libiconv.dll' (or one of its dependenc # #StandWithUkraine πŸ‡ΊπŸ‡¦ -I have Ukrainian relatives and friends. - #RussiaInvadedUkraine on 24 of February 2022, at 05:00 the armed forces of the Russian Federation attacked Ukraine. Please, stand with Ukraine, stay tuned for updates on Ukraine's official sources and channels in English and support Ukraine in its fight for freedom and democracy in Europe. diff --git a/cv2_capture_screenshot.png b/cv2_capture_screenshot.png index b44a6c9..b469f4f 100644 Binary files a/cv2_capture_screenshot.png and b/cv2_capture_screenshot.png differ diff --git a/pyproject.toml b/pyproject.toml index 993f7bd..cb86bbe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ dependencies = [ "typing_extensions; python_version<='3.7'", "colorama>=0.4.6", ] -description = "Extracts one time password (OTP) / two-factor authentication (2FA) secrets from export QR codes, e.g. from Google Authenticator app." +description = "Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps such as 'Google Authenticator'" dynamic = ["version"] keywords = ["python", "security", "json", "otp", "csv", "protobuf", "qrcode", "two-factor", "totp", "google-authenticator", "recovery", "proto3", "mfa", "two-factor-authentication", "tfa", "qr-codes", "otpauth", "2fa", "security-tools", "cv2"] license = {text = "GNU General Public License v3 (GPLv3)"} @@ -48,7 +48,7 @@ authors = [{name = "scito", email = "info@scito.ch"}] maintainers = [{name = "scito", email = "info@scito.ch"}] requires-python = ">=3.7, <4" scripts = {extract_otp_secrets = "extract_otp_secrets:sys_main"} -urls = {Project-URL = "https://github.com/scito/extract_otp_secrets", Bug-Reports = "https://github.com/scito/extract_otp_secrets/issues", Source = "https://github.com/scito/extract_otp_secrets" } +urls = {Project-URL = "https://github.com/scito/extract_otp_secrets", Bug-Reports = "https://github.com/scito/extract_otp_secrets/issues", Source = "https://github.com/scito/extract_otp_secrets"} # [tool.setuptools] # Still in beta, once it is stable move config from setup.cfg to pyproject.toml