initial commit
This commit is contained in:
commit
6cdbc385ad
4 changed files with 168 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
in/
|
||||||
|
out/
|
||||||
|
json/
|
||||||
|
env/
|
20
README.md
Normal file
20
README.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# TOTP Generator
|
||||||
|
|
||||||
|
A simple python script used to decode and encode TOTP QR-Codes.
|
||||||
|
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
You need [zbar][zbar] as a QR-code reader library.
|
||||||
|
This can usually be installed via your distribution.
|
||||||
|
|
||||||
|
On Arch Linux, install the `zbar` package.
|
||||||
|
On Ubuntu, install `zbar-tools`.
|
||||||
|
|
||||||
|
Additionally, you need `python3` and the `pip` packages listed in `requirements.txt`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
|
||||||
|
[zbar]: https://github.com/mchehab/zbar
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
opencv-python
|
||||||
|
pyzbar
|
||||||
|
qrcode
|
||||||
|
pillow
|
140
totp.py
Executable file
140
totp.py
Executable file
|
@ -0,0 +1,140 @@
|
||||||
|
#! /usr/bin/python3
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import pyzbar.pyzbar as pyzbar
|
||||||
|
import re
|
||||||
|
import qrcode
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
from typing import Union, Dict
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
ROOT_DIR = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
QRCODE_INPUT_DIR = ROOT_DIR / 'in'
|
||||||
|
JSON_DIR = ROOT_DIR / 'json'
|
||||||
|
QRCODE_OUTPUT_DIR = ROOT_DIR / 'out'
|
||||||
|
|
||||||
|
for d in [QRCODE_INPUT_DIR, JSON_DIR, QRCODE_OUTPUT_DIR]:
|
||||||
|
d.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def normalize(string: str):
|
||||||
|
return string.strip().lower().replace(' ', '_')
|
||||||
|
|
||||||
|
|
||||||
|
def make_qr_code(data):
|
||||||
|
qr = qrcode.QRCode(
|
||||||
|
error_correction=qrcode.constants.ERROR_CORRECT_H,
|
||||||
|
version=None,
|
||||||
|
box_size=12,
|
||||||
|
border=5,
|
||||||
|
)
|
||||||
|
qr.add_data(str(data))
|
||||||
|
qr.make(fit=True)
|
||||||
|
|
||||||
|
img = qr.make_image()
|
||||||
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
def make_totp_url(secret, issuer='XXX', username='xxx', **kwargs):
|
||||||
|
return "otpauth://totp/{issuer}:{username}?secret={secret}&issuer={issuer}".format(
|
||||||
|
issuer=issuer, secret=secret, username=username
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def read_qr_code(path):
|
||||||
|
img = cv2.imread(str(path))
|
||||||
|
dec = pyzbar.decode(img, symbols=[pyzbar.ZBarSymbol.QRCODE])
|
||||||
|
for obj in dec:
|
||||||
|
return obj.data.decode('ascii')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_totp_link(string) -> Union[Dict, None]:
|
||||||
|
match = re.search(
|
||||||
|
r'otpauth://totp/'
|
||||||
|
r'(?:(?P<issuer>.*):)?(?P<username>.*?)?'
|
||||||
|
r'\?secret=(?P<secret>.*?)'
|
||||||
|
r'(?:'
|
||||||
|
r'(&digits=(?P<digits>\d+?))|'
|
||||||
|
r'(&algorithm=(?P<algorithm>.+?))|'
|
||||||
|
r'(&issuer=(?P<issuer2>.+?))|'
|
||||||
|
r'(&period=(?P<period>\d+?))|'
|
||||||
|
r')*$',
|
||||||
|
string
|
||||||
|
)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
d = match.groupdict()
|
||||||
|
try:
|
||||||
|
if d['issuer'] and d['issuer2'] and d['issuer'] != d['issuer2']:
|
||||||
|
print('different issuers: confused')
|
||||||
|
exit(1)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if d['issuer2']:
|
||||||
|
d['issuer'] = d['issuer2']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
d.pop('issuer2')
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
d['url'] = string
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def parse_qr_codes():
|
||||||
|
all = []
|
||||||
|
for qr in QRCODE_INPUT_DIR.rglob('*'):
|
||||||
|
print(f'reading in: {qr.name}' )
|
||||||
|
link = urllib.parse.unquote(read_qr_code(qr))
|
||||||
|
if not link:
|
||||||
|
continue
|
||||||
|
totp = parse_totp_link(link)
|
||||||
|
try:
|
||||||
|
if not totp['issuer'] == totp['issuer2']:
|
||||||
|
print('different issuers found. confused')
|
||||||
|
else:
|
||||||
|
totp.pop('issuer2')
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
if totp is not None:
|
||||||
|
all.append(totp)
|
||||||
|
with open(JSON_DIR / qr.with_suffix('.json').name, 'w') as f:
|
||||||
|
json.dump(totp, f, indent=4, sort_keys=True)
|
||||||
|
with open(JSON_DIR / 'all.json', 'w') as f:
|
||||||
|
json.dump(all, f, indent=4, sort_keys=True)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_qr_code(totp):
|
||||||
|
img = make_qr_code(make_totp_url(**totp))
|
||||||
|
filename = '{issuer}:{username}.png'.format(
|
||||||
|
issuer=totp['issuer'] if 'issuer' in totp.keys() else 'XXX',
|
||||||
|
username=totp['username'] if 'username' in totp.keys() else 'xxx'
|
||||||
|
)
|
||||||
|
img.save(QRCODE_OUTPUT_DIR / normalize(filename))
|
||||||
|
|
||||||
|
|
||||||
|
def generate_qr_codes():
|
||||||
|
for js in JSON_DIR.rglob('*.json'):
|
||||||
|
if js.name == 'all.json':
|
||||||
|
continue
|
||||||
|
with open(js, 'r') as f:
|
||||||
|
totp = json.load(f)
|
||||||
|
generate_qr_code(totp)
|
||||||
|
|
||||||
|
|
||||||
|
def produce_from_all():
|
||||||
|
with open(JSON_DIR / 'all.json') as f:
|
||||||
|
l = json.load(f)
|
||||||
|
l = list(l)
|
||||||
|
for d in l:
|
||||||
|
generate_qr_code(d)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_qr_codes()
|
||||||
|
parse_qr_codes()
|
Loading…
Reference in a new issue