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 exported QR codes from authentication apps can be read in three ways:
The secrets can be exported to JSON or CSV, or printed as QR codes to console or saved as PNG.
β‘ This project/script was renamed from extract_otp_secret_keys
to extract_otp_secrets
. β‘
chmod +x extract_otp_secrets_X.Y.Z_OS_ARCH
:heavy_check_mark: Everything is just packed in one executable.
:heavy_check_mark: No installation needed, neither Python nor any dependencies have to be installed.
:heavy_check_mark: Easy and convenient
:information_source: There is a delay after starting the executable since the files have internally to be unpacked.
:information_source: If you are a developer, you might prefer to run the Python script directly, see Installation
:warning: Some antivirus tools may show a virus or trojan alert for the executable. This alert is a false positive. This is a known problem for executables generated by PyInstaller. If you have any doubt, please use directly the Python script.
:information_source: The executables are not signed. Thus, the operating system may show a warning about download from unknown source.
Beginning in macOS 10.15, all software built after June 1, 2019, and distributed with Developer ID must be notarized. However, you arenβt required to notarize software that you distribute through the Mac App Store because the App Store submission process already includes equivalent security checks. developer.apple.com
:x: Unfortunately, I cannot provide a signed and notarized installable application for macOS as .dmg or .pkg. Apple is not Open Source friendly and requires a yearly Developer ID subscription. I am not willing to pay USD 99 per year to Apple for this little open source tool.
However, the bare executable can be executed from the command line:
Terminal
applicationcd $HOME/Downloads
xattr -r -d com.apple.quarantine extract_otp_secrets_X.Y.Z_macos_x86_64
chmod +x extract_otp_secrets_X.Y.Z_macos_x86_64
./extract_otp_secrets_X.Y.Z_macos_x86_64
OpenCV: not authorized to capture video (status 0), requesting...
OpenCV: camera failed to properly initialize!
./extract_otp_secrets_X.Y.Z_macos_x86_64
:information_source: Replace X.Y.Z
in above commands with the version number of your downloaded file, e.g. extract_otp_secrets_2.4.0_macos_x86_64
:information_source: If Rosetta2 emulation is installed, these steps work also for M1 and M2 Apple Silicon processors and the program can be executed directly.
Tested with extract_otp_secrets_2.8.1_macos_x86_64 on macOS Sequoia 15.1 beta. Source: #283
extract_otp_secrets
Detected QR codes are surrounded with a frame. The color of the frame indicates the extracting result:
Key commands:
The secrets are printed by default to the console. Set program parameters for other types of output, e.g. --csv exported_secrets.csv
.
extract_otp_secrets example_export.png
otpauth-migration://offline?data=β¦
)extract_otp_secrets example_export.txt
git clone https://github.com/scito/extract_otp_secrets.git
cd extract_otp_secrets
pip install -U -r requirements.txt
python src/extract_otp_secrets.py example_export.txt
In case this script is not starting properly, the debug mode can be activated by adding parameter -d
in the command line.
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.
sudo apt-get install libzbar0
sudo zypper install libzbar0
sudo dnf install zbar
pacman -S zbar
brew install zbar
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. Install vcredist_x64.exe
if using 64-bit Python, vcredist_x86.exe
if using 32-bit Python. For more information see pyzbar
OpenCV requires Visual C++ redistributable 2015. For more information see opencv-python
usage: extract_otp_secrets.py [-h] [--csv FILE] [--keepass FILE] [--json FILE] [--txt FILE] [--urls FILE] [--printqr] [--saveqr DIR] [--camera NUMBER] [--qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}] [-i] [--no-color] [--version] [-d | -v | -q] [infile ...] 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; b) image file containing a QR code or = for stdin for an image containing a QR code options: -h, --help show this help message and exit --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 --txt FILE, -t FILE export txt file or - for stdout --urls FILE, -u FILE export file with list of otpauth urls, or - for stdout --printqr, -p print QR code(s) as text to the terminal --saveqr DIR, -s DIR save QR code(s) as images to directory --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 --version, -V print version and quit -d, --debug enter debug mode, do checks and quit -v, --verbose verbose output -q, --quiet no stdout output, except output set by - examples: python extract_otp_secrets.py python extract_otp_secrets.py example_*.txt 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
python src/extract_otp_secrets.py example_export.txt
python src/extract_otp_secrets.py example_export.png
python src/extract_otp_secrets.py -q --csv extracted_secrets.csv example_export.txt
python src/extract_otp_secrets.py -q --json extracted_secrets.json example_export.txt
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
python src/extract_otp_secrets.py - < example_export.txt
python src/extract_otp_secrets.py = < example_export.png
python src/extract_otp_secrets.py --csv - example_export.txt
python src/extract_otp_secrets.py --csv - example_*.png | tail -n+2
cat example_*.txt | python src/extract_otp_secrets.py --csv - - | tail -n+2
KeePass 2.51 (released in May 2022) and newer support the generation of OTPs (TOTP and HOTP).
KeePass can generate the second factor password (2FA) if the OTP secret is stored in TimeOtp-Secret-Base32
string field for TOTP or HmacOtp-Secret-Base32
string field for HOTP. You view or edit them in entry dialog on the 'Advanced' tab page.
KeePass provides menu commands in the main window for generating one-time passwords ('Copy HMAC-Based OTP', 'Show HMAC-Based OTP', 'Copy Time-Based OTP', 'Show Time-Based OTP'). Furthermore, one-time passwords can be generated during auto-type using the {HMACOTP} and {TIMEOTP} placeholders.
In order to simplify the usage of the second factor password generation in KeePass a specific KeePass CSV export is available with option -keepass
or -k
. This KeePass CSV file can be imported by the "Generic CSV Importer" of KeePass.
If TOTP and HOTP entries have to be exported, then two files with an intermediate suffix .totp or .hotp will be added to the KeePass export filename.
Example:
Import CSV with TOTP entries in KeePass as
Import CSV with HOTP entries in KeePass as
KeePass can be used as a backup for one time passwords (second factor) from the mobile phone.
pip install -U git+https://github.com/scito/extract_otp_secrets
extract_otp_secrets
or run it
python -m extract_otp_secrets
or from a specific tag
pip install -U git+https://github.com/scito/extract_otp_secrets.git@v2.0.0
extract_otp_secrets
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | python -m extract_otp_secrets -
git clone https://github.com/scito/extract_otp_secrets.git
pip install -U -e extract_otp_secrets
extract_otp_secrets extract_otp_secrets/example_export.txt
or run it
python -m extract_otp_secrets extract_otp_secrets/example_export.txt
You can you use Pipenv for running extract_otp_secrets.
pipenv --rm
pipenv install
pipenv shell
python src/extract_otp_secrets.py example_export.txt
You can you use VSCode devcontainer for running extract_otp_secrets.
Requirement: Docker
Alternatively, you can use a python virtual env for the dependencies:
python -m venv venv
. venv/bin/activate
pip install -U -r requirements-dev.txt
pip install -U -r requirements.txt
The requirements*.txt files contain all the dependencies (also the optional ones).
To leave the python virtual env just call deactivate
.
Install devbox, which is a wrapper for nix. Then enter the environment with Python and the packages installed with:
devbox shell
Install devbox, which is a wrapper for nix. Then enter the environment with Python and the packages installed with:
devenv shell
Install Docker.
Prebuilt docker images are available for amd64 and arm64 architectures on Docker Hub and on GitHub Packages.
Extracting from an QR image file:
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets =
Capturing from camera in GUI window (X Window system required on host):
docker run --network none --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
If only text processing is required, there is a small Image based on Alpine Linux:
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.txt | docker run --network none --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest-only-txt -
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 --network none --pull always -i --rm -v "$(pwd)":/files:ro ghcr.io/scito/extract_otp_secrets =
docker run --network none --pull always --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets example_export.png
docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets_only_txt - < example_export.txt
cat example_export.txt | docker run --network none --pull always --rm -i -v "$(pwd)":/files:ro scit0/extract_otp_secrets:latest_only_txt - -c - > example_out.csv
The script is covered by pytests, see extract_otp_secrets_test.py
.
Run tests:
pytest
or
python -m pytest
Your tests can run against an installed version after executing pip install .
Your tests can run against the local copy with an editable install after executing pip install --editable .
If you donβt use an editable install and are relying on the fact that Python by default puts the current directory in sys.path to import your package, you can execute python -m pytest to execute the tests against the local copy directly, without using pip.
https://docs.pytest.org/en/7.1.x/explanation/pythonpath.html#pytest-vs-python-m-pytest
There are some unittests, see extract_otp_secrets_txt_unit_test.py
.
Run tests:
python -m unittest
Note the pytest
s are preferred and complete. For each unittest there is also a test in pytest
.
Setup for running the tests in VSCode.
cd extract_otp_secrets/
pip install -U -e .
python src/extract_otp_secrets.py
pip wheel .
Note: python -m build --wheel
= pip wheel --no-deps .
pip install -U -r requirements.txt
Build and run the app within the container:
docker build . -t extract_otp_secrets --pull -f docker/Dockerfile --build-arg RUN_TESTS=false
Run tests in docker container:
docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets
docker build . -t extract_otp_secrets:only_txt --pull -f docker/Dockerfile_only_txt --build-arg RUN_TESTS=false
Run tests in docker container:
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
pyinstaller -y --add-data $pythonLocation/__yolo_v3_qr_detector/:__yolo_v3_qr_detector/ --onefile src/extract_otp_secrets.py
Output is executable dist/extract_otp_secrets
pyinstaller -y --add-data "%pythonLocation%\__yolo_v3_qr_detector;__yolo_v3_qr_detector" --add-binary "%pythonLocation%\pyzbar\libiconv.dll;pyzbar" --add-binary "%pythonLocation%\pyzbar\libzbar-64.dll;pyzbar" --add-binary "%windir%\system32\msvcr120.dll;pyzbar" --add-binary "%windir%\system32\msvcp120.dll;pyzbar" --add-binary "%windir%\system32\vcamp120.dll;pyzbar" --add-binary "%windir%\system32\vcomp120.dll;pyzbar" --add-binary "%windir%\system32\vccorlib120.dll;pyzbar" --add-binary "%windir%\system32\mfc120.dll;pyzbar" --add-binary "%windir%\system32\mfc120u.dll;pyzbar" --add-binary "%windir%\system32\mfc120chs.dll;pyzbar" --add-binary "%windir%\system32\mfc120cht.dll;pyzbar" --add-binary "%windir%\system32\mfc120deu.dll;pyzbar" --add-binary "%windir%\system32\mfc120enu.dll;pyzbar" --add-binary "%windir%\system32\mfc120esn.dll;pyzbar" --add-binary "%windir%\system32\mfc120fra.dll;pyzbar" --add-binary "%windir%\system32\mfc120ita.dll;pyzbar" --add-binary "%windir%\system32\mfc120jpn.dll;pyzbar" --add-binary "%windir%\system32\mfc120kor.dll;pyzbar" --add-binary "%windir%\system32\mfc120rus.dll;pyzbar" --onefile --version-file build\file_version_info.txt src\extract_otp_secrets.py
Output is dist\extract_otp_secrets.exe
There is a Bash script for a full local build including linting and type checking.
./build.sh
The options of the build script:
Build extract_otp_secrets project
./build.sh [options]
Options:
-i Interactive mode, all steps must be confirmed
-C Ignore version check of protobuf/protoc
-e Build exe
-n Build nuitka exe
-L Do not build local (exes)
-d Build docker
-a Build arm
-X Do not build x86_64
-B Do not build base
-V Do not run pipenv
-g Start extract_otp_secrets.py in GUI mode
-c Clean everything
-r Generate result files
-h, --help Show help and quit
The export QR code of "Google Authenticator" contains the URL otpauth-migration://offline?data=β¦
.
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 --plugin=protoc-gen-mypy=path/to/protoc-gen-mypy --python_out=src/protobuf_generated_python --mypy_out=src/protobuf_generated_python src/google_auth.proto
The generated protobuf Python code was generated by protoc 28.3 (https://github.com/protocolbuffers/protobuf/releases/tag/v28.3).
For Python type hint generation the mypy-protobuf package is used.
If you see an ugly ImportError on Windows you will most likely need the Visual C++ Redistributable Packages for Visual Studio 2013. Install vcredist_x64.exe if using 64-bit Python, vcredist_x86.exe if using 32-bit Python.
This library shared library is required by pyzbar.
Traceback (most recent call last):
File "C:\Users\Admin\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pyzbar\zbar_library.py", line 58, in load
dependencies, libzbar = load_objects(Path(''))
^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Admin\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pyzbar\zbar_library.py", line 50, in load_objects
deps = [
^
File "C:\Users\Admin\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\pyzbar\zbar_library.py", line 51, in <listcomp>
cdll.LoadLibrary(str(directory.joinpath(dep)))
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.496.0_x64__qbz5n2kfra8p0\Lib\ctypes\__init__.py", line 454, in LoadLibrary
return self._dlltype(name)
^^^^^^^^^^^^^^^^^^^
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.496.0_x64__qbz5n2kfra8p0\Lib\ctypes\__init__.py", line 376, in __init__
self._handle = _dlopen(self._name, mode)
^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: Could not find module 'libiconv.dll' (or one of its dependencies). Try using the full path with constructor syntax.