mirror of
https://github.com/scito/extract_otp_secrets.git
synced 2025-12-12 09:49:46 +01:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a77e775948 | ||
|
|
eae01a07d5 | ||
|
|
10fefacd2d | ||
|
|
b562ceb00a | ||
|
|
3e1818619e | ||
|
|
d08195507e | ||
|
|
a95a0d1325 | ||
|
|
302c45be99 | ||
|
|
354a4bdada | ||
|
|
13c4b6c7d4 | ||
|
|
397534d5ef | ||
|
|
52d5c56890 | ||
|
|
8bc7d8f035 | ||
|
|
a60cbbb7bb | ||
|
|
4546655cc5 | ||
|
|
39af5ab077 | ||
|
|
be4d0d37db | ||
|
|
3933e6ed8a |
@@ -6,7 +6,7 @@
|
||||
"context": "..",
|
||||
//Update 'VARIANT' to pick a Python version: 3, 3.10, ...
|
||||
"args": {
|
||||
"VARIANT": "3.10"
|
||||
"VARIANT": "3.11"
|
||||
}
|
||||
},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10", "3.x"]
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.x"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
74
.github/workflows/codeql-analysis.yml
vendored
Normal file
74
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '25 19 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,3 +13,4 @@ venv/
|
||||
!.vscode/settings.json
|
||||
!.devcontainer/
|
||||
!.devcontainer/*.json
|
||||
*.whl
|
||||
|
||||
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM python:3.11-alpine
|
||||
|
||||
WORKDIR /extract
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
WORKDIR /files
|
||||
|
||||
ENTRYPOINT [ "python", "/extract/extract_otp_secret_keys.py" ]
|
||||
5
Pipfile
5
Pipfile
@@ -10,6 +10,9 @@ pillow = "*"
|
||||
|
||||
[dev-packages]
|
||||
pytest = "*"
|
||||
wheel = "*"
|
||||
flake8 = "*"
|
||||
pylint = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.10"
|
||||
python_version = "3.11"
|
||||
|
||||
348
Pipfile.lock
generated
348
Pipfile.lock
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "360af7adfda239c3869ca72b8c07e4d53b66dc3c83c38dc304a175fe408f6737"
|
||||
"sha256": "38a1c4d86c2546c0ac33f77c1a20dc325b50be44b8c4b310050b63033558507d"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.10"
|
||||
"python_version": "3.11"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
@@ -18,87 +18,90 @@
|
||||
"default": {
|
||||
"pillow": {
|
||||
"hashes": [
|
||||
"sha256:0030fdbd926fb85844b8b92e2f9449ba89607231d3dd597a21ae72dc7fe26927",
|
||||
"sha256:030e3460861488e249731c3e7ab59b07c7853838ff3b8e16aac9561bb345da14",
|
||||
"sha256:0ed2c4ef2451de908c90436d6e8092e13a43992f1860275b4d8082667fbb2ffc",
|
||||
"sha256:136659638f61a251e8ed3b331fc6ccd124590eeff539de57c5f80ef3a9594e58",
|
||||
"sha256:13b725463f32df1bfeacbf3dd197fb358ae8ebcd8c5548faa75126ea425ccb60",
|
||||
"sha256:1536ad017a9f789430fb6b8be8bf99d2f214c76502becc196c6f2d9a75b01b76",
|
||||
"sha256:15928f824870535c85dbf949c09d6ae7d3d6ac2d6efec80f3227f73eefba741c",
|
||||
"sha256:17d4cafe22f050b46d983b71c707162d63d796a1235cdf8b9d7a112e97b15bac",
|
||||
"sha256:1802f34298f5ba11d55e5bb09c31997dc0c6aed919658dfdf0198a2fe75d5490",
|
||||
"sha256:1cc1d2451e8a3b4bfdb9caf745b58e6c7a77d2e469159b0d527a4554d73694d1",
|
||||
"sha256:1fd6f5e3c0e4697fa7eb45b6e93996299f3feee73a3175fa451f49a74d092b9f",
|
||||
"sha256:254164c57bab4b459f14c64e93df11eff5ded575192c294a0c49270f22c5d93d",
|
||||
"sha256:2ad0d4df0f5ef2247e27fc790d5c9b5a0af8ade9ba340db4a73bb1a4a3e5fb4f",
|
||||
"sha256:2c58b24e3a63efd22554c676d81b0e57f80e0a7d3a5874a7e14ce90ec40d3069",
|
||||
"sha256:2d33a11f601213dcd5718109c09a52c2a1c893e7461f0be2d6febc2879ec2402",
|
||||
"sha256:336b9036127eab855beec9662ac3ea13a4544a523ae273cbf108b228ecac8437",
|
||||
"sha256:337a74fd2f291c607d220c793a8135273c4c2ab001b03e601c36766005f36885",
|
||||
"sha256:37ff6b522a26d0538b753f0b4e8e164fdada12db6c6f00f62145d732d8a3152e",
|
||||
"sha256:3d1f14f5f691f55e1b47f824ca4fdcb4b19b4323fe43cc7bb105988cad7496be",
|
||||
"sha256:4134d3f1ba5f15027ff5c04296f13328fecd46921424084516bdb1b2548e66ff",
|
||||
"sha256:4ad2f835e0ad81d1689f1b7e3fbac7b01bb8777d5a985c8962bedee0cc6d43da",
|
||||
"sha256:50dff9cc21826d2977ef2d2a205504034e3a4563ca6f5db739b0d1026658e004",
|
||||
"sha256:510cef4a3f401c246cfd8227b300828715dd055463cdca6176c2e4036df8bd4f",
|
||||
"sha256:5aed7dde98403cd91d86a1115c78d8145c83078e864c1de1064f52e6feb61b20",
|
||||
"sha256:69bd1a15d7ba3694631e00df8de65a8cb031911ca11f44929c97fe05eb9b6c1d",
|
||||
"sha256:6bf088c1ce160f50ea40764f825ec9b72ed9da25346216b91361eef8ad1b8f8c",
|
||||
"sha256:6e8c66f70fb539301e064f6478d7453e820d8a2c631da948a23384865cd95544",
|
||||
"sha256:74a04183e6e64930b667d321524e3c5361094bb4af9083db5c301db64cd341f3",
|
||||
"sha256:75e636fd3e0fb872693f23ccb8a5ff2cd578801251f3a4f6854c6a5d437d3c04",
|
||||
"sha256:7761afe0126d046974a01e030ae7529ed0ca6a196de3ec6937c11df0df1bc91c",
|
||||
"sha256:7888310f6214f19ab2b6df90f3f06afa3df7ef7355fc025e78a3044737fab1f5",
|
||||
"sha256:7b0554af24df2bf96618dac71ddada02420f946be943b181108cac55a7a2dcd4",
|
||||
"sha256:7c7b502bc34f6e32ba022b4a209638f9e097d7a9098104ae420eb8186217ebbb",
|
||||
"sha256:808add66ea764ed97d44dda1ac4f2cfec4c1867d9efb16a33d158be79f32b8a4",
|
||||
"sha256:831e648102c82f152e14c1a0938689dbb22480c548c8d4b8b248b3e50967b88c",
|
||||
"sha256:93689632949aff41199090eff5474f3990b6823404e45d66a5d44304e9cdc467",
|
||||
"sha256:96b5e6874431df16aee0c1ba237574cb6dff1dcb173798faa6a9d8b399a05d0e",
|
||||
"sha256:9a54614049a18a2d6fe156e68e188da02a046a4a93cf24f373bffd977e943421",
|
||||
"sha256:a138441e95562b3c078746a22f8fca8ff1c22c014f856278bdbdd89ca36cff1b",
|
||||
"sha256:a647c0d4478b995c5e54615a2e5360ccedd2f85e70ab57fbe817ca613d5e63b8",
|
||||
"sha256:a9c9bc489f8ab30906d7a85afac4b4944a572a7432e00698a7239f44a44e6efb",
|
||||
"sha256:ad2277b185ebce47a63f4dc6302e30f05762b688f8dc3de55dbae4651872cdf3",
|
||||
"sha256:adabc0bce035467fb537ef3e5e74f2847c8af217ee0be0455d4fec8adc0462fc",
|
||||
"sha256:b6d5e92df2b77665e07ddb2e4dbd6d644b78e4c0d2e9272a852627cdba0d75cf",
|
||||
"sha256:bc431b065722a5ad1dfb4df354fb9333b7a582a5ee39a90e6ffff688d72f27a1",
|
||||
"sha256:bdd0de2d64688ecae88dd8935012c4a72681e5df632af903a1dca8c5e7aa871a",
|
||||
"sha256:c79698d4cd9318d9481d89a77e2d3fcaeff5486be641e60a4b49f3d2ecca4e28",
|
||||
"sha256:cb6259196a589123d755380b65127ddc60f4c64b21fc3bb46ce3a6ea663659b0",
|
||||
"sha256:d5b87da55a08acb586bad5c3aa3b86505f559b84f39035b233d5bf844b0834b1",
|
||||
"sha256:dcd7b9c7139dc8258d164b55696ecd16c04607f1cc33ba7af86613881ffe4ac8",
|
||||
"sha256:dfe4c1fedfde4e2fbc009d5ad420647f7730d719786388b7de0999bf32c0d9fd",
|
||||
"sha256:ea98f633d45f7e815db648fd7ff0f19e328302ac36427343e4432c84432e7ff4",
|
||||
"sha256:ec52c351b35ca269cb1f8069d610fc45c5bd38c3e91f9ab4cbbf0aebc136d9c8",
|
||||
"sha256:eef7592281f7c174d3d6cbfbb7ee5984a671fcd77e3fc78e973d492e9bf0eb3f",
|
||||
"sha256:f07f1f00e22b231dd3d9b9208692042e29792d6bd4f6639415d2f23158a80013",
|
||||
"sha256:f3fac744f9b540148fa7715a435d2283b71f68bfb6d4aae24482a890aed18b59",
|
||||
"sha256:fa768eff5f9f958270b081bb33581b4b569faabf8774726b283edb06617101dc",
|
||||
"sha256:fac2d65901fb0fdf20363fbd345c01958a742f2dc62a8dd4495af66e3ff502a4"
|
||||
"sha256:03150abd92771742d4a8cd6f2fa6246d847dcd2e332a18d0c15cc75bf6703040",
|
||||
"sha256:073adb2ae23431d3b9bcbcff3fe698b62ed47211d0716b067385538a1b0f28b8",
|
||||
"sha256:0b07fffc13f474264c336298d1b4ce01d9c5a011415b79d4ee5527bb69ae6f65",
|
||||
"sha256:0b7257127d646ff8676ec8a15520013a698d1fdc48bc2a79ba4e53df792526f2",
|
||||
"sha256:12ce4932caf2ddf3e41d17fc9c02d67126935a44b86df6a206cf0d7161548627",
|
||||
"sha256:15c42fb9dea42465dfd902fb0ecf584b8848ceb28b41ee2b58f866411be33f07",
|
||||
"sha256:18498994b29e1cf86d505edcb7edbe814d133d2232d256db8c7a8ceb34d18cef",
|
||||
"sha256:1c7c8ae3864846fc95f4611c78129301e203aaa2af813b703c55d10cc1628535",
|
||||
"sha256:22b012ea2d065fd163ca096f4e37e47cd8b59cf4b0fd47bfca6abb93df70b34c",
|
||||
"sha256:276a5ca930c913f714e372b2591a22c4bd3b81a418c0f6635ba832daec1cbcfc",
|
||||
"sha256:2e0918e03aa0c72ea56edbb00d4d664294815aa11291a11504a377ea018330d3",
|
||||
"sha256:3033fbe1feb1b59394615a1cafaee85e49d01b51d54de0cbf6aa8e64182518a1",
|
||||
"sha256:3168434d303babf495d4ba58fc22d6604f6e2afb97adc6a423e917dab828939c",
|
||||
"sha256:32a44128c4bdca7f31de5be641187367fe2a450ad83b833ef78910397db491aa",
|
||||
"sha256:3dd6caf940756101205dffc5367babf288a30043d35f80936f9bfb37f8355b32",
|
||||
"sha256:40e1ce476a7804b0fb74bcfa80b0a2206ea6a882938eaba917f7a0f004b42502",
|
||||
"sha256:41e0051336807468be450d52b8edd12ac60bebaa97fe10c8b660f116e50b30e4",
|
||||
"sha256:4390e9ce199fc1951fcfa65795f239a8a4944117b5935a9317fb320e7767b40f",
|
||||
"sha256:502526a2cbfa431d9fc2a079bdd9061a2397b842bb6bc4239bb176da00993812",
|
||||
"sha256:51e0e543a33ed92db9f5ef69a0356e0b1a7a6b6a71b80df99f1d181ae5875636",
|
||||
"sha256:57751894f6618fd4308ed8e0c36c333e2f5469744c34729a27532b3db106ee20",
|
||||
"sha256:5d77adcd56a42d00cc1be30843d3426aa4e660cab4a61021dc84467123f7a00c",
|
||||
"sha256:655a83b0058ba47c7c52e4e2df5ecf484c1b0b0349805896dd350cbc416bdd91",
|
||||
"sha256:68943d632f1f9e3dce98908e873b3a090f6cba1cbb1b892a9e8d97c938871fbe",
|
||||
"sha256:6c738585d7a9961d8c2821a1eb3dcb978d14e238be3d70f0a706f7fa9316946b",
|
||||
"sha256:73bd195e43f3fadecfc50c682f5055ec32ee2c933243cafbfdec69ab1aa87cad",
|
||||
"sha256:772a91fc0e03eaf922c63badeca75e91baa80fe2f5f87bdaed4280662aad25c9",
|
||||
"sha256:77ec3e7be99629898c9a6d24a09de089fa5356ee408cdffffe62d67bb75fdd72",
|
||||
"sha256:7db8b751ad307d7cf238f02101e8e36a128a6cb199326e867d1398067381bff4",
|
||||
"sha256:801ec82e4188e935c7f5e22e006d01611d6b41661bba9fe45b60e7ac1a8f84de",
|
||||
"sha256:82409ffe29d70fd733ff3c1025a602abb3e67405d41b9403b00b01debc4c9a29",
|
||||
"sha256:828989c45c245518065a110434246c44a56a8b2b2f6347d1409c787e6e4651ee",
|
||||
"sha256:829f97c8e258593b9daa80638aee3789b7df9da5cf1336035016d76f03b8860c",
|
||||
"sha256:871b72c3643e516db4ecf20efe735deb27fe30ca17800e661d769faab45a18d7",
|
||||
"sha256:89dca0ce00a2b49024df6325925555d406b14aa3efc2f752dbb5940c52c56b11",
|
||||
"sha256:90fb88843d3902fe7c9586d439d1e8c05258f41da473952aa8b328d8b907498c",
|
||||
"sha256:97aabc5c50312afa5e0a2b07c17d4ac5e865b250986f8afe2b02d772567a380c",
|
||||
"sha256:9aaa107275d8527e9d6e7670b64aabaaa36e5b6bd71a1015ddd21da0d4e06448",
|
||||
"sha256:9f47eabcd2ded7698106b05c2c338672d16a6f2a485e74481f524e2a23c2794b",
|
||||
"sha256:a0a06a052c5f37b4ed81c613a455a81f9a3a69429b4fd7bb913c3fa98abefc20",
|
||||
"sha256:ab388aaa3f6ce52ac1cb8e122c4bd46657c15905904b3120a6248b5b8b0bc228",
|
||||
"sha256:ad58d27a5b0262c0c19b47d54c5802db9b34d38bbf886665b626aff83c74bacd",
|
||||
"sha256:ae5331c23ce118c53b172fa64a4c037eb83c9165aba3a7ba9ddd3ec9fa64a699",
|
||||
"sha256:af0372acb5d3598f36ec0914deed2a63f6bcdb7b606da04dc19a88d31bf0c05b",
|
||||
"sha256:afa4107d1b306cdf8953edde0534562607fe8811b6c4d9a486298ad31de733b2",
|
||||
"sha256:b03ae6f1a1878233ac620c98f3459f79fd77c7e3c2b20d460284e1fb370557d4",
|
||||
"sha256:b0915e734b33a474d76c28e07292f196cdf2a590a0d25bcc06e64e545f2d146c",
|
||||
"sha256:b4012d06c846dc2b80651b120e2cdd787b013deb39c09f407727ba90015c684f",
|
||||
"sha256:b472b5ea442148d1c3e2209f20f1e0bb0eb556538690fa70b5e1f79fa0ba8dc2",
|
||||
"sha256:b59430236b8e58840a0dfb4099a0e8717ffb779c952426a69ae435ca1f57210c",
|
||||
"sha256:b90f7616ea170e92820775ed47e136208e04c967271c9ef615b6fbd08d9af0e3",
|
||||
"sha256:b9a65733d103311331875c1dca05cb4606997fd33d6acfed695b1232ba1df193",
|
||||
"sha256:bac18ab8d2d1e6b4ce25e3424f709aceef668347db8637c2296bcf41acb7cf48",
|
||||
"sha256:bca31dd6014cb8b0b2db1e46081b0ca7d936f856da3b39744aef499db5d84d02",
|
||||
"sha256:be55f8457cd1eac957af0c3f5ece7bc3f033f89b114ef30f710882717670b2a8",
|
||||
"sha256:c7025dce65566eb6e89f56c9509d4f628fddcedb131d9465cacd3d8bac337e7e",
|
||||
"sha256:c935a22a557a560108d780f9a0fc426dd7459940dc54faa49d83249c8d3e760f",
|
||||
"sha256:dbb8e7f2abee51cef77673be97760abff1674ed32847ce04b4af90f610144c7b",
|
||||
"sha256:e6ea6b856a74d560d9326c0f5895ef8050126acfdc7ca08ad703eb0081e82b74",
|
||||
"sha256:ebf2029c1f464c59b8bdbe5143c79fa2045a581ac53679733d3a91d400ff9efb",
|
||||
"sha256:f1ff2ee69f10f13a9596480335f406dd1f70c3650349e2be67ca3139280cade0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==9.2.0"
|
||||
"version": "==9.3.0"
|
||||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:011c0f267e85f5d73750b6c25f0155d5db1e9443cd3590ab669a6221dd8fcdb0",
|
||||
"sha256:3ec6f5b37935406bb9df9b277e79f8ed81d697146e07ef2ba8a5a272fb24b2c9",
|
||||
"sha256:5310cbe761e87f0c1decce019d23f2101521d4dfff46034f8a12a53546036ec7",
|
||||
"sha256:5e0b272217aad8971763960238c1a1e6a65d50ef7824e23300da97569a251c55",
|
||||
"sha256:5e0ce02418ef03d7657a420ae8fd6fec4995ac713a3cb09164e95f694dbcf085",
|
||||
"sha256:5eb0724615e90075f1d763983e708e1cef08e66b1891d8b8b6c33bc3b2f1a02b",
|
||||
"sha256:7b6f22463e2d1053d03058b7b4ceca6e4ed4c14f8c286c32824df751137bf8e7",
|
||||
"sha256:a7faa62b183d6a928e3daffd06af843b4287d16ef6e40f331575ecd236a7974d",
|
||||
"sha256:b04484d6f42f48c57dd2737a72692f4c6987529cdd148fb5b8e5f616862a2e37",
|
||||
"sha256:b52e7a522911a40445a5f588bd5b5e584291bfc5545e09b7060685e4b2ff814f",
|
||||
"sha256:bf711b451212dc5b0fa45ae7dada07d8e71a4b0ff0bc8e4783ee145f47ac4f82",
|
||||
"sha256:e5c5a2886ae48d22a9d32fbb9b6636a089af3cd26b706750258ce1ca96cc0116",
|
||||
"sha256:eb1106e87e095628e96884a877a51cdb90087106ee693925ec0a300468a9be3a",
|
||||
"sha256:ee04f5823ed98bb9a8c3b1dc503c49515e0172650875c3f76e225b223793a1f2"
|
||||
"sha256:0413addc126c40a5440ee59be098de1007183d68e9f5f20ed5fbc44848f417ca",
|
||||
"sha256:05cbcb9a25cd781fd949f93f6f98a911883868c0360c6d2264fc99a903c8f0d7",
|
||||
"sha256:0c968753028cb14b1d24cc839723f7e9505b305fc588a37a9e0f7d270cb59d89",
|
||||
"sha256:2a172741b5b041a896b621cef4277077afd571e0d3a6e524e7171f1c70e33200",
|
||||
"sha256:3f08f04b4f101dd469efbcc1731fbf48068eccd8a42f4e2ea530aa012a5f56f8",
|
||||
"sha256:4d97c16c0d11155b3714a29245461f0eb60cace294455077f3a3b8a629afa383",
|
||||
"sha256:5096b3922b45e4b7a04d3d3cb855d13bb5ccd4d5e44b129e706232ebf0ffb870",
|
||||
"sha256:5efa8a8162ada7e10847140308fbf84fdc5b89dc21655d12ec04aed87284fe07",
|
||||
"sha256:6b809f20923b6ef49dc1755cb50bdb21be179b4a3c7ffcab1fe5d3f139b58a51",
|
||||
"sha256:81b233a06c62387ea5c9be2cd9aedd2ba09940e91da53b920e9ff5bd98e48e7f",
|
||||
"sha256:a5e89eabaa0ca72ce1b7c8104a740d44cdb67942cbbed00c69a4c0541de17107",
|
||||
"sha256:b78d7c2c36b51c0041b9bf000be4adb09f4112bfc40bc7a9d48ac0b0dfad139e",
|
||||
"sha256:e53165dd14d19abc7f50733f365de431e51d1d262db40c0ee22e271a074fac59",
|
||||
"sha256:e92768d17473657c87e98b79a4c7724b0ddfa23211b05ce137bfdc55e734e36f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.21.5"
|
||||
"version": "==4.21.10"
|
||||
},
|
||||
"qrcode": {
|
||||
"hashes": [
|
||||
@@ -109,6 +112,14 @@
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"astroid": {
|
||||
"hashes": [
|
||||
"sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907",
|
||||
"sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7"
|
||||
],
|
||||
"markers": "python_full_version >= '3.7.2'",
|
||||
"version": "==2.12.13"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6",
|
||||
@@ -117,6 +128,22 @@
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==22.1.0"
|
||||
},
|
||||
"dill": {
|
||||
"hashes": [
|
||||
"sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0",
|
||||
"sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.3.6"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7",
|
||||
"sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.0.0"
|
||||
},
|
||||
"iniconfig": {
|
||||
"hashes": [
|
||||
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
|
||||
@@ -124,6 +151,47 @@
|
||||
],
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7",
|
||||
"sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"
|
||||
],
|
||||
"markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
|
||||
"version": "==5.10.1"
|
||||
},
|
||||
"lazy-object-proxy": {
|
||||
"hashes": [
|
||||
"sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada",
|
||||
"sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d",
|
||||
"sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7",
|
||||
"sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe",
|
||||
"sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd",
|
||||
"sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c",
|
||||
"sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858",
|
||||
"sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288",
|
||||
"sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec",
|
||||
"sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f",
|
||||
"sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891",
|
||||
"sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c",
|
||||
"sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25",
|
||||
"sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156",
|
||||
"sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8",
|
||||
"sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f",
|
||||
"sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e",
|
||||
"sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0",
|
||||
"sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.8.0"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
|
||||
"sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.7.0"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
|
||||
@@ -132,6 +200,14 @@
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==21.3"
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7",
|
||||
"sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.5.4"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
||||
@@ -140,13 +216,29 @@
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"py": {
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719",
|
||||
"sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"
|
||||
"sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053",
|
||||
"sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==1.11.0"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.10.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf",
|
||||
"sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"pylint": {
|
||||
"hashes": [
|
||||
"sha256:1d561d1d3e8be9dd880edc685162fbdaa0409c88b9b7400873c0cf345602e326",
|
||||
"sha256:91e4776dbcb4b4d921a3e4b6fec669551107ba11f29d9199154a01622e460a57"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.15.7"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
@@ -158,19 +250,97 @@
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7",
|
||||
"sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"
|
||||
"sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71",
|
||||
"sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==7.1.3"
|
||||
"version": "==7.2.0"
|
||||
},
|
||||
"tomli": {
|
||||
"tomlkit": {
|
||||
"hashes": [
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
"sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b",
|
||||
"sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.0.1"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.11.6"
|
||||
},
|
||||
"wheel": {
|
||||
"hashes": [
|
||||
"sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac",
|
||||
"sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.38.4"
|
||||
},
|
||||
"wrapt": {
|
||||
"hashes": [
|
||||
"sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3",
|
||||
"sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b",
|
||||
"sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4",
|
||||
"sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2",
|
||||
"sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656",
|
||||
"sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3",
|
||||
"sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff",
|
||||
"sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310",
|
||||
"sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a",
|
||||
"sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57",
|
||||
"sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069",
|
||||
"sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383",
|
||||
"sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe",
|
||||
"sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87",
|
||||
"sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d",
|
||||
"sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b",
|
||||
"sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907",
|
||||
"sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f",
|
||||
"sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0",
|
||||
"sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28",
|
||||
"sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1",
|
||||
"sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853",
|
||||
"sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc",
|
||||
"sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3",
|
||||
"sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3",
|
||||
"sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164",
|
||||
"sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1",
|
||||
"sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c",
|
||||
"sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1",
|
||||
"sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7",
|
||||
"sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1",
|
||||
"sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320",
|
||||
"sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed",
|
||||
"sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1",
|
||||
"sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248",
|
||||
"sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c",
|
||||
"sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456",
|
||||
"sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77",
|
||||
"sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef",
|
||||
"sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1",
|
||||
"sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7",
|
||||
"sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86",
|
||||
"sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4",
|
||||
"sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d",
|
||||
"sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d",
|
||||
"sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8",
|
||||
"sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5",
|
||||
"sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471",
|
||||
"sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00",
|
||||
"sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68",
|
||||
"sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3",
|
||||
"sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d",
|
||||
"sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735",
|
||||
"sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d",
|
||||
"sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569",
|
||||
"sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7",
|
||||
"sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59",
|
||||
"sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5",
|
||||
"sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb",
|
||||
"sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b",
|
||||
"sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f",
|
||||
"sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462",
|
||||
"sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015",
|
||||
"sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"
|
||||
],
|
||||
"markers": "python_version >= '3.11'",
|
||||
"version": "==1.14.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
105
README.md
105
README.md
@@ -3,39 +3,43 @@
|
||||
[](https://github.com/scito/extract_otp_secret_keys/actions/workflows/ci.yml)
|
||||

|
||||
[](https://github.com/scito/extract_otp_secret_keys/blob/master/Pipfile.lock)
|
||||

|
||||

|
||||
[](https://github.com/scito/extract_otp_secret_keys/blob/master/LICENSE)
|
||||
[](https://github.com/scito/extract_otp_secret_keys/tags)
|
||||
[](https://stand-with-ukraine.pp.ua)
|
||||
|
||||
---
|
||||
|
||||
Extract two-factor authentication (2FA, TFA) secret keys from export QR codes of "Google Authenticator" app.
|
||||
Extract two-factor authentication (2FA, TFA, one time passwords, otp) secret keys from export QR codes of "Google Authenticator" app.
|
||||
The secret and otp values can be printed and exported to json or csv. The QR codes can be printed or saved as PNG images.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Export the QR codes from "Google Authenticator" app
|
||||
2. Read QR codes with QR code reader
|
||||
3. Save the captured QR codes in a text file. Save each QR code on a new line. (The captured QR codes look like `otpauth-migration://offline?data=...`)
|
||||
4. Call this script with the file as input:
|
||||
1. Open "Google Authenticator" app on the mobile phone
|
||||
2. Export the QR codes from "Google Authenticator" app
|
||||
3. Read QR codes with a QR code reader (e.g. from another phone)
|
||||
4. Save the captured QR codes in the QR code reader to a text file, e.g. example_export.txt. Save each QR code on a new line. (The captured QR codes look like `otpauth-migration://offline?data=...`)
|
||||
5. Transfer the file to the computer where his script is installed.
|
||||
6. Call this script with the file as input:
|
||||
|
||||
python extract_otp_secret_keys.py example_export.txt
|
||||
|
||||
## Program help: arguments and options
|
||||
|
||||
<pre>usage: extract_otp_secret_keys.py [-h] [--json FILE] [--csv FILE] [--printqr] [--saveqr DIR] [--verbose] [--quiet] infile
|
||||
<pre>usage: extract_otp_secret_keys.py [-h] [--json FILE] [--csv FILE] [--keepass FILE] [--printqr] [--saveqr DIR] [--verbose] [--quiet] infile
|
||||
|
||||
positional arguments:
|
||||
infile file or - for stdin (default: -) with "otpauth-migration://..." URLs separated by newlines, lines starting with # are ignored
|
||||
infile file or - for stdin (default: -) with "otpauth-migration://..." URLs separated by newlines, lines starting with # are ignored
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--json FILE, -j FILE export to json file
|
||||
--csv FILE, -c FILE export to csv file
|
||||
--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)
|
||||
--verbose, -v verbose output
|
||||
--quiet, -q no stdout output</pre>
|
||||
-h, --help show this help message and exit
|
||||
--json FILE, -j FILE export json file
|
||||
--csv FILE, -c FILE export csv file
|
||||
--keepass FILE, -k FILE export totp/hotp csv file(s) for KeePass
|
||||
--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)
|
||||
--verbose, -v verbose output
|
||||
--quiet, -q no stdout output</pre>
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -43,7 +47,8 @@ options:
|
||||
|
||||
Known to work with
|
||||
|
||||
* Python 3.10.6, protobuf 4.21.5, qrcode 7.3.1, and pillow 9.2
|
||||
* Python 3.10.8, protobuf 4.21.9, qrcode 7.3.1, and pillow 9.2
|
||||
* Python 3.11.0, protobuf 4.21.10, qrcode 7.3.1, and pillow 9.2
|
||||
|
||||
For protobuf versions 3.14.0 or similar or Python 3.6, use the extract_otp_secret_keys version 1.4.0.
|
||||
|
||||
@@ -53,6 +58,44 @@ For printing QR codes, the qrcode module is required, otherwise it can be omitte
|
||||
|
||||
pip install qrcode[pil]
|
||||
|
||||
## KeePass
|
||||
|
||||
[KeePass 2.51](https://keepass.info/news/n220506_2.51.html) (released in May 2022) and newer [support the generation of OTPs (TOTP and HOTP)](https://keepass.info/help/base/placeholders.html#otp).
|
||||
|
||||
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](https://keepass.info/help/kb/imp_csv.html).
|
||||
|
||||
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:
|
||||
- Only TOTP entries to export and parameter --keepass example_keepass_output.csv<br>
|
||||
→ example_keepass_output.csv with TOTP entries will be exported
|
||||
- Only HOTP entries to export and parameter --keepass example_keepass_output.csv<br>
|
||||
→ example_keepass_output.csv with HOTP entries will be exported
|
||||
- If both TOTP and HOTP entries to export and parameter --keepass example_keepass_output.csv<br>
|
||||
→ example_keepass_output.totp.csv with TOTP entries will be exported<br>
|
||||
→ example_keepass_output.hotp.csv with HOTP entries will be exported
|
||||
|
||||
Import CSV with TOTP entries in KeePass as
|
||||
|
||||
- Title
|
||||
- User Name
|
||||
- String (TimeOtp-Secret-Base32)
|
||||
- Group (/)
|
||||
|
||||
Import CSV with HOTP entries in KeePass as
|
||||
|
||||
- Title
|
||||
- User Name
|
||||
- String (HmacOtp-Secret-Base32)
|
||||
- String (HmacOtp-Counter)
|
||||
- Group (/)
|
||||
|
||||
KeePass can be used as a backup for one time passwords (second factor) from the mobile phone.
|
||||
|
||||
## Technical background
|
||||
|
||||
The export QR code of "Google Authenticator" contains the URL `otpauth-migration://offline?data=...`.
|
||||
@@ -62,7 +105,7 @@ Command for regeneration of Python code from proto3 message definition file (onl
|
||||
|
||||
protoc --python_out=protobuf_generated_python google_auth.proto
|
||||
|
||||
The generated protobuf Python code was generated by protoc 21.5 (https://github.com/protocolbuffers/protobuf/releases/tag/v21.5).
|
||||
The generated protobuf Python code was generated by protoc 21.10 (https://github.com/protocolbuffers/protobuf/releases/tag/v21.10).
|
||||
|
||||
## References
|
||||
|
||||
@@ -76,6 +119,7 @@ The generated protobuf Python code was generated by protoc 21.5 (https://github.
|
||||
You can you use [Pipenv](https://github.com/pypa/pipenv) for running extract_otp_secret_keys.
|
||||
|
||||
```
|
||||
pipenv --rm
|
||||
pipenv install
|
||||
pipenv shell
|
||||
python extract_otp_secret_keys.py example_export.txt
|
||||
@@ -114,6 +158,17 @@ Install [devbox](https://github.com/jetpack-io/devbox), which is a wrapper for n
|
||||
devbox shell
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
Install [Docker](https://docs.docker.com/get-docker/).
|
||||
|
||||
Build and run the app within the container:
|
||||
|
||||
```bash
|
||||
docker build . -t extract_otp
|
||||
docker run --rm -v "$(pwd)":/files:ro extract_otp -p example_export.txt
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
### PyTest
|
||||
@@ -148,3 +203,19 @@ Setup for running the tests in VSCode.
|
||||
2. Type command "Python: Configure Tests"
|
||||
3. Choose unittest or pytest. (pytest is recommended, both are supported)
|
||||
4. Set ". Root" directory
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Upgrade pip Packages
|
||||
|
||||
```
|
||||
pip install -U -r requirements.txt
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
# #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.
|
||||
|
||||
@@ -9,3 +9,6 @@ otpauth-migration://offline?data=CigKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXB
|
||||
# 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
|
||||
|
||||
2
example_keepass_output.hotp.csv
Normal file
2
example_keepass_output.hotp.csv
Normal file
@@ -0,0 +1,2 @@
|
||||
Title,User Name,HmacOtp-Secret-Base32,HmacOtp-Counter,Group
|
||||
,hotp demo,7KSQL2JTUDIS5EF65KLMRQIIGY,4,OTP/HOTP
|
||||
|
5
example_keepass_output.totp.csv
Normal file
5
example_keepass_output.totp.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
Title,User Name,TimeOtp-Secret-Base32,Group
|
||||
raspberrypi,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
raspberrypi,pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,OTP/TOTP
|
||||
|
@@ -1,5 +1,6 @@
|
||||
name,secret,issuer,type,url
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,raspberrypi,OTP_TOTP,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,,OTP_TOTP,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,,OTP_TOTP,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,raspberrypi,OTP_TOTP,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
name,secret,issuer,type,counter,url
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,raspberrypi,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
pi@raspberrypi,7KSQL2JTUDIS5EF65KLMRQIIGY,raspberrypi,totp,,otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
hotp demo,7KSQL2JTUDIS5EF65KLMRQIIGY,,hotp,4,otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
|
||||
|
@@ -3,28 +3,40 @@
|
||||
"name": "pi@raspberrypi",
|
||||
"secret": "7KSQL2JTUDIS5EF65KLMRQIIGY",
|
||||
"issuer": "raspberrypi",
|
||||
"type": "OTP_TOTP",
|
||||
"type": "totp",
|
||||
"counter": null,
|
||||
"url": "otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi"
|
||||
},
|
||||
{
|
||||
"name": "pi@raspberrypi",
|
||||
"secret": "7KSQL2JTUDIS5EF65KLMRQIIGY",
|
||||
"issuer": "",
|
||||
"type": "OTP_TOTP",
|
||||
"type": "totp",
|
||||
"counter": null,
|
||||
"url": "otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY"
|
||||
},
|
||||
{
|
||||
"name": "pi@raspberrypi",
|
||||
"secret": "7KSQL2JTUDIS5EF65KLMRQIIGY",
|
||||
"issuer": "",
|
||||
"type": "OTP_TOTP",
|
||||
"type": "totp",
|
||||
"counter": null,
|
||||
"url": "otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY"
|
||||
},
|
||||
{
|
||||
"name": "pi@raspberrypi",
|
||||
"secret": "7KSQL2JTUDIS5EF65KLMRQIIGY",
|
||||
"issuer": "raspberrypi",
|
||||
"type": "OTP_TOTP",
|
||||
"type": "totp",
|
||||
"counter": null,
|
||||
"url": "otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi"
|
||||
},
|
||||
{
|
||||
"name": "hotp demo",
|
||||
"secret": "7KSQL2JTUDIS5EF65KLMRQIIGY",
|
||||
"issuer": "",
|
||||
"type": "hotp",
|
||||
"counter": 4,
|
||||
"url": "otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4"
|
||||
}
|
||||
]
|
||||
@@ -65,21 +65,24 @@ def main(sys_args):
|
||||
|
||||
otps = extract_otps(args)
|
||||
write_csv(args, otps)
|
||||
write_keepass_csv(args, otps)
|
||||
write_json(args, otps)
|
||||
|
||||
|
||||
def parse_args(sys_args):
|
||||
arg_parser = argparse.ArgumentParser()
|
||||
formatter = lambda prog: argparse.HelpFormatter(prog,max_help_position=52)
|
||||
arg_parser = argparse.ArgumentParser(formatter_class=formatter)
|
||||
arg_parser.add_argument('infile', help='file or - for stdin (default: -) with "otpauth-migration://..." URLs separated by newlines, lines starting with # are ignored')
|
||||
arg_parser.add_argument('--json', '-j', help='export to json file', metavar=('FILE'))
|
||||
arg_parser.add_argument('--csv', '-c', help='export to csv file', metavar=('FILE'))
|
||||
arg_parser.add_argument('--json', '-j', help='export json file', metavar=('FILE'))
|
||||
arg_parser.add_argument('--csv', '-c', help='export csv file', metavar=('FILE'))
|
||||
arg_parser.add_argument('--keepass', '-k', help='export totp/hotp csv file(s) for KeePass', metavar=('FILE'))
|
||||
arg_parser.add_argument('--printqr', '-p', help='print QR code(s) as text to the terminal (requires qrcode module)', action='store_true')
|
||||
arg_parser.add_argument('--saveqr', '-s', help='save QR code(s) as images to the given folder (requires qrcode module)', metavar=('DIR'))
|
||||
arg_parser.add_argument('--verbose', '-v', help='verbose output', action='count')
|
||||
arg_parser.add_argument('--quiet', '-q', help='no stdout output', action='store_true')
|
||||
args = arg_parser.parse_args(sys_args)
|
||||
if args.verbose and args.quiet:
|
||||
print("The arguments --verbose and --quite are mutual exclusive.")
|
||||
print("The arguments --verbose and --quiet are mutually exclusive.")
|
||||
sys.exit(1)
|
||||
return args
|
||||
|
||||
@@ -102,13 +105,15 @@ def extract_otps(args):
|
||||
j += 1
|
||||
if verbose: print('\n{}. Secret Key'.format(j))
|
||||
secret = convert_secret_from_bytes_to_base32_str(raw_otp.secret)
|
||||
otp_type = get_enum_name_by_number(raw_otp, 'type')
|
||||
otp_type_enum = get_enum_name_by_number(raw_otp, 'type')
|
||||
otp_type = get_otp_type_str_from_code(raw_otp.type)
|
||||
otp_url = build_otp_url(secret, raw_otp)
|
||||
otp = {
|
||||
"name": raw_otp.name,
|
||||
"secret": secret,
|
||||
"issuer": raw_otp.issuer,
|
||||
"type": otp_type,
|
||||
"counter": raw_otp.counter if raw_otp.type == 1 else None,
|
||||
"url": otp_url
|
||||
}
|
||||
if not quiet:
|
||||
@@ -154,6 +159,10 @@ def get_enum_name_by_number(parent, field_name):
|
||||
return parent.DESCRIPTOR.fields_by_name[field_name].enum_type.values_by_number.get(field_value).name
|
||||
|
||||
|
||||
def get_otp_type_str_from_code(otp_type):
|
||||
return 'totp' if otp_type == 2 else 'hotp'
|
||||
|
||||
|
||||
def convert_secret_from_bytes_to_base32_str(bytes):
|
||||
return str(base64.b32encode(bytes), 'utf-8').replace('=', '')
|
||||
|
||||
@@ -162,15 +171,17 @@ def build_otp_url(secret, raw_otp):
|
||||
url_params = {'secret': secret}
|
||||
if raw_otp.type == 1: url_params['counter'] = raw_otp.counter
|
||||
if raw_otp.issuer: url_params['issuer'] = raw_otp.issuer
|
||||
otp_url = 'otpauth://{}/{}?'.format('totp' if raw_otp.type == 2 else 'hotp', quote(raw_otp.name)) + urlencode(url_params)
|
||||
otp_url = 'otpauth://{}/{}?'.format(get_otp_type_str_from_code(raw_otp.type), quote(raw_otp.name)) + urlencode(url_params)
|
||||
return otp_url
|
||||
|
||||
|
||||
def print_otp(otp):
|
||||
print('Name: {}'.format(otp['name']))
|
||||
print('Secret: {}'.format(otp['secret']))
|
||||
if otp['issuer']: print('Issuer: {}'.format(otp['issuer']))
|
||||
print('Type: {}'.format(otp['type']))
|
||||
print('Name: {}'.format(otp['name']))
|
||||
print('Secret: {}'.format(otp['secret']))
|
||||
if otp['issuer']: print('Issuer: {}'.format(otp['issuer']))
|
||||
print('Type: {}'.format(otp['type']))
|
||||
if otp['type'] == 'hotp':
|
||||
print('Counter: {}'.format(otp['counter']))
|
||||
if verbose:
|
||||
print(otp['url'])
|
||||
|
||||
@@ -209,7 +220,48 @@ def write_csv(args, otps):
|
||||
writer = csv.DictWriter(outfile, otps[0].keys())
|
||||
writer.writeheader()
|
||||
writer.writerows(otps)
|
||||
if not quiet: print("Exported {} otps to csv".format(len(otps)))
|
||||
if not quiet: print("Exported {} otps to csv {}".format(len(otps), args.csv))
|
||||
|
||||
|
||||
def write_keepass_csv(args, otps):
|
||||
global verbose, quiet
|
||||
if args.keepass and len(otps) > 0:
|
||||
has_totp = has_otp_type(otps, 'totp')
|
||||
has_hotp = has_otp_type(otps, 'hotp')
|
||||
otp_filename_totp = args.keepass if has_totp != has_hotp else add_pre_suffix(args.keepass, "totp")
|
||||
otp_filename_hotp = args.keepass if has_totp != has_hotp else add_pre_suffix(args.keepass, "hotp")
|
||||
count_totp_entries = 0
|
||||
count_hotp_entries = 0
|
||||
if has_totp:
|
||||
with open(otp_filename_totp, "w") as outfile:
|
||||
writer = csv.DictWriter(outfile, ["Title", "User Name", "TimeOtp-Secret-Base32", "Group"])
|
||||
writer.writeheader()
|
||||
for otp in otps:
|
||||
if otp['type'] == 'totp':
|
||||
writer.writerow({
|
||||
'Title': otp['issuer'],
|
||||
'User Name': otp['name'],
|
||||
'TimeOtp-Secret-Base32': otp['secret'] if otp['type'] == 'totp' else None,
|
||||
'Group': "OTP/{}".format(otp['type'].upper())
|
||||
})
|
||||
count_totp_entries += 1
|
||||
if has_hotp:
|
||||
with open(otp_filename_hotp, "w") as outfile:
|
||||
writer = csv.DictWriter(outfile, ["Title", "User Name", "HmacOtp-Secret-Base32", "HmacOtp-Counter", "Group"])
|
||||
writer.writeheader()
|
||||
for otp in otps:
|
||||
if otp['type'] == 'hotp':
|
||||
writer.writerow({
|
||||
'Title': otp['issuer'],
|
||||
'User Name': otp['name'],
|
||||
'HmacOtp-Secret-Base32': otp['secret'] if otp['type'] == 'hotp' else None,
|
||||
'HmacOtp-Counter': otp['counter'] if otp['type'] == 'hotp' else None,
|
||||
'Group': "OTP/{}".format(otp['type'].upper())
|
||||
})
|
||||
count_hotp_entries += 1
|
||||
if not quiet:
|
||||
if count_totp_entries > 0: print("Exported {} totp entries to keepass csv file {}".format(count_totp_entries, otp_filename_totp))
|
||||
if count_hotp_entries > 0: print("Exported {} hotp entries to keepass csv file {}".format(count_hotp_entries, otp_filename_hotp))
|
||||
|
||||
|
||||
def write_json(args, otps):
|
||||
@@ -217,7 +269,20 @@ def write_json(args, otps):
|
||||
if args.json:
|
||||
with open(args.json, "w") as outfile:
|
||||
json.dump(otps, outfile, indent=4)
|
||||
if not quiet: print("Exported {} otp entries to json".format(len(otps)))
|
||||
if not quiet: print("Exported {} otp entries to json {}".format(len(otps), args.json))
|
||||
|
||||
|
||||
def has_otp_type(otps, otp_type):
|
||||
for otp in otps:
|
||||
if otp['type'] == otp_type:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def add_pre_suffix(file, pre_suffix):
|
||||
'''filename.ext, pre -> filename.pre.ext'''
|
||||
name, ext = path.splitext(file)
|
||||
return name + "." + pre_suffix + (ext if ext else "")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
11
test/example_export_only_totp.txt
Normal file
11
test/example_export_only_totp.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
# 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
|
||||
@@ -18,10 +18,10 @@ batch_id: -1320898453
|
||||
|
||||
|
||||
1. Secret Key
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: totp
|
||||
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
|
||||
|
||||
@@ -42,9 +42,9 @@ batch_id: -2094403140
|
||||
|
||||
|
||||
2. Secret Key
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
|
||||
|
||||
@@ -74,16 +74,41 @@ batch_id: -1822886384
|
||||
|
||||
|
||||
3. Secret Key
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
|
||||
|
||||
4. Secret Key
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: totp
|
||||
otpauth://totp/pi%40raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
|
||||
|
||||
|
||||
# otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
otpauth-migration://offline?data=CiUKEPqlBekzoNEukL7qlsjBCDYSCWhvdHAgZGVtbyABKAEwATgEEAEYASAAKNuv15j6%2F%2F%2F%2F%2FwE%3D
|
||||
|
||||
4. Payload Line
|
||||
otp_parameters {
|
||||
secret: "\372\245\005\3513\240\321.\220\276\352\226\310\301\0106"
|
||||
name: "hotp demo"
|
||||
algorithm: ALGO_SHA1
|
||||
digits: 1
|
||||
type: OTP_HOTP
|
||||
counter: 4
|
||||
}
|
||||
version: 1
|
||||
batch_size: 1
|
||||
batch_id: -1558849573
|
||||
|
||||
|
||||
5. Secret Key
|
||||
Name: hotp demo
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: hotp
|
||||
Counter: 4
|
||||
otpauth://hotp/hotp%20demo?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&counter=4
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: totp
|
||||
|
||||
|
||||
█▀▀▀▀▀█ ▄▀▄▄█ █▀ ▀▀▀▀▀█ ▄▄ █▀▀▀▀▀█
|
||||
@@ -26,9 +26,9 @@ Type: OTP_TOTP
|
||||
|
||||
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
|
||||
|
||||
█▀▀▀▀▀█ ▀▀██ █▄▀█ ▀▄▄▀█▀▄ █▀▀▀▀▀█
|
||||
@@ -51,9 +51,9 @@ Type: OTP_TOTP
|
||||
|
||||
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
|
||||
|
||||
█▀▀▀▀▀█ ▀▀██ █▄▀█ ▀▄▄▀█▀▄ █▀▀▀▀▀█
|
||||
@@ -76,10 +76,10 @@ Type: OTP_TOTP
|
||||
|
||||
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: totp
|
||||
|
||||
|
||||
█▀▀▀▀▀█ ▄▀▄▄█ █▀ ▀▀▀▀▀█ ▄▄ █▀▀▀▀▀█
|
||||
@@ -104,3 +104,31 @@ Type: OTP_TOTP
|
||||
|
||||
|
||||
|
||||
Name: hotp demo
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: hotp
|
||||
Counter: 4
|
||||
|
||||
|
||||
█▀▀▀▀▀█ ▄█▀▄▄ ▄▄▄██ █▄▄██ █▄ █▀▀▀▀▀█
|
||||
█ ███ █ ▀▄ ▄▄▄ ▀▄ ▄▄▀ █▀ █ ███ █
|
||||
█ ▀▀▀ █ ▀█ ▄▄█▄ ▄▀█▀▀██▄▄██▄ █ ▀▀▀ █
|
||||
▀▀▀▀▀▀▀ ▀▄▀ █▄▀▄█▄█▄█ ▀▄█▄█ █ ▀▀▀▀▀▀▀
|
||||
█▄█ █ ▀▄▄ ▀ ▄▄███▄█▄ ▄█▀ ▀█ ▄█▄▄▀▄
|
||||
▄██▄▀▄▀██▄▀▄▀ ▀▀█ ▀▄ █▄▀██▄ ▀▄▀▀▀▄█▀
|
||||
▄ ▄█▄▀▀▀▀█▄▄▄▀▄█▄ ▄ ▄██▀█▀▄ ▀▄█▄ █▀▀█
|
||||
▄▀▄▀██▀█▀ ██▀▄ ▀▀ ▄▄▄█▄██ ▄▀█▄▄▄ ▀▄▀
|
||||
█▄▀▀▀█▀█▄ ▄ ▀ ▀█ ▄ ▄█ █▄▀█▄ █▄█ ▀▄
|
||||
▀▀██▄█▀ ▄█▄▀▀█▄ ▄█▀██▄▄█▄ █▀▄█ ▀▀▀█
|
||||
█████▄▀▀█▀▀█▀▀▄ ▄ ▀█▀▄ ██▄ ▄███ ▄▀█
|
||||
▄▄█▀▀▀█▀█▄█ ▄█▄▄█ ▀▀ ▄▀▄ ▄█▀▄▄█▀▀▄▄
|
||||
██▄ █▀▄▀▀ █ ▀██ █▄ ▄ █ ▀▄█▀▄█▄██
|
||||
▀▄▀ █ ▀▄▀▄██▄█ ▀█▀▄▄ ██▄▄▄▀ ▀▄ ▄█ ███
|
||||
▀ ▀▀ ▀▀▄ ▄▄▄█▄██▀▀ ▄█ ▀ █▀▀█▀▀▀█▀ █
|
||||
█▀▀▀▀▀█ █▄█▀█▀▀█▀ ▄█ ▀▄▄▀█ ▀ █▀▀ ▀
|
||||
█ ███ █ ▀█▀▀ ▀ █ ▄ ▄█▄█ █▄ █▀▀██▀ ██
|
||||
█ ▀▀▀ █ ▀▄▀▄█▀▀▄ ▀▀█▄▄ ▀▄▄█ █▀▀▀▄▀
|
||||
▀▀▀▀▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀ ▀▀ ▀ ▀▀▀▀ ▀▀
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from utils import read_csv, read_json, remove_file, remove_dir_with_files, read_file_to_str
|
||||
from utils import read_csv, read_json, remove_files, remove_dir_with_files, read_file_to_str, file_exits
|
||||
from os import path
|
||||
from pytest import raises
|
||||
|
||||
@@ -47,6 +47,58 @@ def test_extract_csv(capsys):
|
||||
cleanup()
|
||||
|
||||
|
||||
def test_keepass_csv(capsys):
|
||||
'''Two csv files .totp and .htop are generated.'''
|
||||
# Arrange
|
||||
cleanup()
|
||||
|
||||
# Act
|
||||
extract_otp_secret_keys.main(['-q', '-k', 'test_example_keepass_output.csv', 'example_export.txt'])
|
||||
|
||||
# Assert
|
||||
expected_totp_csv = read_csv('example_keepass_output.totp.csv')
|
||||
expected_hotp_csv = read_csv('example_keepass_output.hotp.csv')
|
||||
actual_totp_csv = read_csv('test_example_keepass_output.totp.csv')
|
||||
actual_hotp_csv = read_csv('test_example_keepass_output.hotp.csv')
|
||||
|
||||
assert actual_totp_csv == expected_totp_csv
|
||||
assert actual_hotp_csv == expected_hotp_csv
|
||||
assert not file_exits('test_example_keepass_output.csv')
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == ''
|
||||
assert captured.err == ''
|
||||
|
||||
# Clean up
|
||||
cleanup()
|
||||
|
||||
|
||||
def test_single_keepass_csv(capsys):
|
||||
'''Does not add .totp or .hotp pre-suffix'''
|
||||
# Arrange
|
||||
cleanup()
|
||||
|
||||
# Act
|
||||
extract_otp_secret_keys.main(['-q', '-k', 'test_example_keepass_output.csv', 'test/example_export_only_totp.txt'])
|
||||
|
||||
# Assert
|
||||
expected_totp_csv = read_csv('example_keepass_output.totp.csv')
|
||||
actual_totp_csv = read_csv('test_example_keepass_output.csv')
|
||||
|
||||
assert actual_totp_csv == expected_totp_csv
|
||||
assert not file_exits('test_example_keepass_output.totp.csv')
|
||||
assert not file_exits('test_example_keepass_output.hotp.csv')
|
||||
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert captured.out == ''
|
||||
assert captured.err == ''
|
||||
|
||||
# Clean up
|
||||
cleanup()
|
||||
|
||||
|
||||
def test_extract_json(capsys):
|
||||
# Arrange
|
||||
cleanup()
|
||||
@@ -76,23 +128,28 @@ def test_extract_stdout(capsys):
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
expected_stdout = '''Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: OTP_TOTP
|
||||
expected_stdout = '''Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: totp
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: totp
|
||||
|
||||
Name: hotp demo
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: hotp
|
||||
Counter: 4
|
||||
|
||||
'''
|
||||
|
||||
@@ -107,25 +164,25 @@ def test_extract_not_encoded_plus(capsys):
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
expected_stdout = '''Name: SerenityLabs:test1@serenitylabs.co.uk
|
||||
Secret: A4RFDYMF4GSLUIBQV4ZP67OJEZ2XUQVM
|
||||
Issuer: SerenityLabs
|
||||
Type: OTP_TOTP
|
||||
expected_stdout = '''Name: SerenityLabs:test1@serenitylabs.co.uk
|
||||
Secret: A4RFDYMF4GSLUIBQV4ZP67OJEZ2XUQVM
|
||||
Issuer: SerenityLabs
|
||||
Type: totp
|
||||
|
||||
Name: SerenityLabs:test2@serenitylabs.co.uk
|
||||
Secret: SCDDZ7PW5MOZLE3PQCAZM7L4S35K3UDX
|
||||
Issuer: SerenityLabs
|
||||
Type: OTP_TOTP
|
||||
Name: SerenityLabs:test2@serenitylabs.co.uk
|
||||
Secret: SCDDZ7PW5MOZLE3PQCAZM7L4S35K3UDX
|
||||
Issuer: SerenityLabs
|
||||
Type: totp
|
||||
|
||||
Name: SerenityLabs:test3@serenitylabs.co.uk
|
||||
Secret: TR76272RVYO6EAEY2FX7W7R7KUDEGPJ4
|
||||
Issuer: SerenityLabs
|
||||
Type: OTP_TOTP
|
||||
Name: SerenityLabs:test3@serenitylabs.co.uk
|
||||
Secret: TR76272RVYO6EAEY2FX7W7R7KUDEGPJ4
|
||||
Issuer: SerenityLabs
|
||||
Type: totp
|
||||
|
||||
Name: SerenityLabs:test4@serenitylabs.co.uk
|
||||
Secret: N2ILWSXSJUQUB7S6NONPJSC62NPG7EXN
|
||||
Issuer: SerenityLabs
|
||||
Type: OTP_TOTP
|
||||
Name: SerenityLabs:test4@serenitylabs.co.uk
|
||||
Secret: N2ILWSXSJUQUB7S6NONPJSC62NPG7EXN
|
||||
Issuer: SerenityLabs
|
||||
Type: totp
|
||||
|
||||
'''
|
||||
|
||||
@@ -210,7 +267,25 @@ def test_extract_help(capsys):
|
||||
assert pytest_wrapped_e.value.code == 0
|
||||
|
||||
|
||||
def test_verbose_and_quiet(capsys):
|
||||
with raises(SystemExit) as pytest_wrapped_e:
|
||||
# Act
|
||||
extract_otp_secret_keys.main(['-v', '-q', 'example_export.txt'])
|
||||
|
||||
# Assert
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert len(captured.out) > 0
|
||||
assert 'The arguments --verbose and --quiet are mutually exclusive.' in captured.out
|
||||
|
||||
|
||||
def test_add_pre_suffix(capsys):
|
||||
assert extract_otp_secret_keys.add_pre_suffix("name.csv", "totp") == "name.totp.csv"
|
||||
assert extract_otp_secret_keys.add_pre_suffix("name.csv", "") == "name..csv"
|
||||
assert extract_otp_secret_keys.add_pre_suffix("name", "totp") == "name.totp"
|
||||
|
||||
|
||||
def cleanup():
|
||||
remove_file('test_example_output.csv')
|
||||
remove_file('test_example_output.json')
|
||||
remove_files('test_example_*.csv')
|
||||
remove_files('test_example_*.json')
|
||||
remove_dir_with_files('testout/')
|
||||
|
||||
@@ -50,23 +50,28 @@ class TestExtract(unittest.TestCase):
|
||||
extract_otp_secret_keys.main(['example_export.txt'])
|
||||
|
||||
expected_output = [
|
||||
'Name: pi@raspberrypi',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Issuer: raspberrypi',
|
||||
'Type: OTP_TOTP',
|
||||
'Name: pi@raspberrypi',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Issuer: raspberrypi',
|
||||
'Type: totp',
|
||||
'',
|
||||
'Name: pi@raspberrypi',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Type: OTP_TOTP',
|
||||
'Name: pi@raspberrypi',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Type: totp',
|
||||
'',
|
||||
'Name: pi@raspberrypi',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Type: OTP_TOTP',
|
||||
'Name: pi@raspberrypi',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Type: totp',
|
||||
'',
|
||||
'Name: pi@raspberrypi',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Issuer: raspberrypi',
|
||||
'Type: OTP_TOTP',
|
||||
'Name: pi@raspberrypi',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Issuer: raspberrypi',
|
||||
'Type: totp',
|
||||
'',
|
||||
'Name: hotp demo',
|
||||
'Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY',
|
||||
'Type: hotp',
|
||||
'Counter: 4',
|
||||
''
|
||||
]
|
||||
self.assertEqual(output, expected_output)
|
||||
@@ -78,23 +83,28 @@ class TestExtract(unittest.TestCase):
|
||||
extract_otp_secret_keys.main(['example_export.txt'])
|
||||
actual_output = out.getvalue()
|
||||
|
||||
expected_output = '''Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: OTP_TOTP
|
||||
expected_output = '''Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: totp
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: totp
|
||||
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: OTP_TOTP
|
||||
Name: pi@raspberrypi
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Issuer: raspberrypi
|
||||
Type: totp
|
||||
|
||||
Name: hotp demo
|
||||
Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY
|
||||
Type: hotp
|
||||
Counter: 4
|
||||
|
||||
'''
|
||||
self.assertEqual(actual_output, expected_output)
|
||||
@@ -105,25 +115,25 @@ Type: OTP_TOTP
|
||||
extract_otp_secret_keys.main(['test/test_plus_problem_export.txt'])
|
||||
actual_output = out.getvalue()
|
||||
|
||||
expected_output = '''Name: SerenityLabs:test1@serenitylabs.co.uk
|
||||
Secret: A4RFDYMF4GSLUIBQV4ZP67OJEZ2XUQVM
|
||||
Issuer: SerenityLabs
|
||||
Type: OTP_TOTP
|
||||
expected_output = '''Name: SerenityLabs:test1@serenitylabs.co.uk
|
||||
Secret: A4RFDYMF4GSLUIBQV4ZP67OJEZ2XUQVM
|
||||
Issuer: SerenityLabs
|
||||
Type: totp
|
||||
|
||||
Name: SerenityLabs:test2@serenitylabs.co.uk
|
||||
Secret: SCDDZ7PW5MOZLE3PQCAZM7L4S35K3UDX
|
||||
Issuer: SerenityLabs
|
||||
Type: OTP_TOTP
|
||||
Name: SerenityLabs:test2@serenitylabs.co.uk
|
||||
Secret: SCDDZ7PW5MOZLE3PQCAZM7L4S35K3UDX
|
||||
Issuer: SerenityLabs
|
||||
Type: totp
|
||||
|
||||
Name: SerenityLabs:test3@serenitylabs.co.uk
|
||||
Secret: TR76272RVYO6EAEY2FX7W7R7KUDEGPJ4
|
||||
Issuer: SerenityLabs
|
||||
Type: OTP_TOTP
|
||||
Name: SerenityLabs:test3@serenitylabs.co.uk
|
||||
Secret: TR76272RVYO6EAEY2FX7W7R7KUDEGPJ4
|
||||
Issuer: SerenityLabs
|
||||
Type: totp
|
||||
|
||||
Name: SerenityLabs:test4@serenitylabs.co.uk
|
||||
Secret: N2ILWSXSJUQUB7S6NONPJSC62NPG7EXN
|
||||
Issuer: SerenityLabs
|
||||
Type: OTP_TOTP
|
||||
Name: SerenityLabs:test4@serenitylabs.co.uk
|
||||
Secret: N2ILWSXSJUQUB7S6NONPJSC62NPG7EXN
|
||||
Issuer: SerenityLabs
|
||||
Type: totp
|
||||
|
||||
'''
|
||||
self.assertEqual(actual_output, expected_output)
|
||||
|
||||
214
upgrade_deps.sh
Executable file
214
upgrade_deps.sh
Executable file
@@ -0,0 +1,214 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Upgrades Protoc from https://github.com/protocolbuffers/protobuf/releases
|
||||
|
||||
black='\e[0;30m'
|
||||
blackBold='\e[1;30m'
|
||||
blackBackground='\e[1;40m'
|
||||
red='\e[0;31m'
|
||||
redBold='\e[1;31m'
|
||||
redBackground='\e[0;41m'
|
||||
green='\e[0;32m'
|
||||
greenBold='\e[1;32m'
|
||||
greenBackground='\e[0;42m'
|
||||
yellow='\e[0;33m'
|
||||
yellowBold='\e[1;33m'
|
||||
yellowBackground='\e[0;43m'
|
||||
blue='\e[0;34m'
|
||||
blueBold='\e[1;34m'
|
||||
blueBackground='\e[0;44m'
|
||||
magenta='\e[0;35m'
|
||||
magentaBold='\e[1;35m'
|
||||
magentaBackground='\e[0;45m'
|
||||
cyan='\e[0;36m'
|
||||
cyanBold='\e[1;36m'
|
||||
cyanBackground='\e[0;46m'
|
||||
white='\e[0;37m'
|
||||
whiteBold='\e[1;37m'
|
||||
whiteBackground='\e[0;47m'
|
||||
reset='\e[0m'
|
||||
|
||||
abort() {
|
||||
echo '
|
||||
***************
|
||||
*** ABORTED ***
|
||||
***************
|
||||
' >&2
|
||||
echo "An error occurred on line $1. Exiting..." >&2
|
||||
date -Iseconds >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap 'abort $LINENO' ERR
|
||||
set -e -o pipefail
|
||||
|
||||
quit() {
|
||||
trap : 0
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Asks if [Yn] if script shoud continue, otherwise exit 1
|
||||
# $1: msg or nothing
|
||||
# Example call 1: askContinueYn
|
||||
# Example call 1: askContinueYn "Backup DB?"
|
||||
askContinueYn() {
|
||||
if [[ $1 ]]; then
|
||||
msg="$1 "
|
||||
else
|
||||
msg=""
|
||||
fi
|
||||
|
||||
# http://stackoverflow.com/questions/3231804/in-bash-how-to-add-are-you-sure-y-n-to-any-command-or-alias
|
||||
read -e -p "${msg}Continue? [Y/n] " response
|
||||
response=${response,,} # tolower
|
||||
if [[ $response =~ ^(yes|y|)$ ]] ; then
|
||||
# echo ""
|
||||
# OK
|
||||
:
|
||||
else
|
||||
echo "Aborted"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Reference: https://gist.github.com/steinwaywhw/a4cd19cda655b8249d908261a62687f8
|
||||
|
||||
echo "Checking Protoc version..."
|
||||
VERSION=$(curl -sL https://github.com/protocolbuffers/protobuf/releases/latest | grep -E "<title>" | perl -pe's%.*Protocol Buffers v(\d+\.\d+(\.\d+)?).*%\1%')
|
||||
BASEVERSION=4
|
||||
echo
|
||||
|
||||
interactive=true
|
||||
check_version=true
|
||||
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
echo "Upgrade Protoc"
|
||||
echo
|
||||
echo "$0 [options]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo "-a Automatic mode"
|
||||
echo "-C Ignore version check"
|
||||
echo "-h, --help Help"
|
||||
quit
|
||||
;;
|
||||
-a)
|
||||
interactive=false
|
||||
shift
|
||||
;;
|
||||
-C)
|
||||
check_version=false
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
BIN="$HOME/bin"
|
||||
DOWNLOADS="$HOME/downloads"
|
||||
|
||||
PIP="pip3.11"
|
||||
PIPENV="python3.11 -m pipenv"
|
||||
|
||||
# Upgrade protoc
|
||||
|
||||
DEST="protoc"
|
||||
|
||||
OLDVERSION=$(cat $BIN/$DEST/.VERSION.txt || echo "")
|
||||
echo -e "\nProtoc remote version $VERSION\n"
|
||||
echo -e "Protoc local version: $OLDVERSION\n"
|
||||
|
||||
if [ "$OLDVERSION" != "$VERSION" ]; then
|
||||
echo "Upgrade protoc from $OLDVERSION to $VERSION"
|
||||
|
||||
NAME="protoc-$VERSION"
|
||||
ARCHIVE="$NAME.zip"
|
||||
|
||||
mkdir -p $DOWNLOADS
|
||||
# https://github.com/protocolbuffers/protobuf/releases/download/v21.6/protoc-21.6-linux-x86_64.zip
|
||||
cmd="wget --trust-server-names https://github.com/protocolbuffers/protobuf/releases/download/v$VERSION/protoc-$VERSION-linux-x86_64.zip -O $DOWNLOADS/$ARCHIVE"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="echo -e '\nSize [Byte]'; stat --printf='%s\n' $DOWNLOADS/$ARCHIVE; echo -e '\nMD5'; md5sum $DOWNLOADS/$ARCHIVE; echo -e '\nSHA256'; sha256sum $DOWNLOADS/$ARCHIVE;"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="mkdir -p $BIN/$NAME; unzip $DOWNLOADS/$ARCHIVE -d $BIN/$NAME"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="echo $VERSION > $BIN/$NAME/.VERSION.txt; echo $VERSION > $BIN/$NAME/.VERSION_$VERSION.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="[ -d $BIN/$DEST.old ] && rm -rf $BIN/$DEST.old || echo 'No old dir to delete'"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="[ -d $BIN/$DEST ] && mv -iT $BIN/$DEST $BIN/$DEST.old || echo 'No previous dir to keep'"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="mv -iT $BIN/$NAME $BIN/$DEST"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="rm $DOWNLOADS/$ARCHIVE"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$BIN/$DEST/bin/protoc --python_out=protobuf_generated_python google_auth.proto"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
# Update README.md
|
||||
|
||||
cmd="perl -i -pe 's%proto(buf|c)([- ])(\d\.)?$OLDVERSION%proto\$1\$2\${3}$VERSION%g' README.md && perl -i -pe 's%(protobuf/releases/tag/v)$OLDVERSION%\${1}$VERSION%g' README.md"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
else
|
||||
echo -e "\nVersion has not changed. Quit"
|
||||
fi
|
||||
|
||||
|
||||
# Upgrade pip requirements
|
||||
|
||||
cmd="sudo pip install --upgrade pip"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIP --version
|
||||
|
||||
cmd="$PIP install -U -r requirements.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIP install -U -r requirements-dev.txt"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIP install -U pipenv"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIPENV --version
|
||||
|
||||
cmd="$PIPENV update && $PIPENV --rm && $PIPENV install"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
$PIPENV run python --version
|
||||
|
||||
# Test
|
||||
|
||||
cmd="pytest"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
cmd="$PIPENV run pytest"
|
||||
if $interactive ; then askContinueYn "$cmd"; fi
|
||||
eval "$cmd"
|
||||
|
||||
quit
|
||||
12
utils.py
12
utils.py
@@ -19,6 +19,7 @@ import os
|
||||
import shutil
|
||||
from io import StringIO
|
||||
import sys
|
||||
import glob
|
||||
|
||||
|
||||
# Ref. https://stackoverflow.com/a/16571630
|
||||
@@ -39,8 +40,17 @@ with Capturing() as output:
|
||||
sys.stdout = self._stdout
|
||||
|
||||
|
||||
def file_exits(file):
|
||||
return os.path.isfile(file)
|
||||
|
||||
|
||||
def remove_file(file):
|
||||
if os.path.isfile(file): os.remove(file)
|
||||
if file_exits(file): os.remove(file)
|
||||
|
||||
|
||||
def remove_files(glob_pattern):
|
||||
for f in glob.glob(glob_pattern):
|
||||
os.remove(f)
|
||||
|
||||
|
||||
def remove_dir_with_files(dir):
|
||||
|
||||
Reference in New Issue
Block a user