118 lines
3.8 KiB
Python
Executable File
118 lines
3.8 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import zlib
|
|
|
|
from jose import jwk
|
|
from jose.utils import base64url_decode
|
|
from PIL import Image
|
|
from pyzbar.pyzbar import decode
|
|
from rich import print_json
|
|
from rich.console import Console
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('data', help='The path to the QR code image file.')
|
|
parser.add_argument('--raw', help='Decode raw QR code data instead of an image file.', action='store_true')
|
|
parser.add_argument('--validate', help='Validate the QR code signature against a public key. Must be the path to a public key.', nargs='?', default=False)
|
|
|
|
# Print help if no input
|
|
if len(sys.argv) == 1:
|
|
parser.print_help(sys.stderr)
|
|
sys.exit(1)
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
QRImageFile = args.data
|
|
|
|
# Load data
|
|
if (not args.raw):
|
|
if (os.path.exists(QRImageFile)):
|
|
decodedQR = decode(Image.open(QRImageFile))
|
|
# Check that the QR code was decoded
|
|
if (len(decodedQR) == 0):
|
|
print(f'\033[91mError:\033[00m could not read the QR code.')
|
|
sys.exit(1)
|
|
QRData = decodedQR[0].data.decode("utf-8") # Get first element (this library supports multiple QR codes in a file)
|
|
else:
|
|
print(f'\033[91mError:\033[00m could not find QR code file: {QRImageFile}')
|
|
sys.exit(1)
|
|
if (args.raw):
|
|
QRData = QRImageFile
|
|
|
|
# Rebuild JWS token
|
|
QRNumericData = re.sub("[^0-9]", "", QRData) # Only keep numeric values
|
|
QRNumericPairs = re.findall("..", QRNumericData) # Split into groups of 2 numeric characters each of which represent a single JWS char
|
|
JWSToken = ""
|
|
for n in QRNumericPairs:
|
|
JWSToken += chr(int(n) + 45) # Recreate the JWS string
|
|
|
|
# Extract JWS Content.
|
|
try:
|
|
header, payload, signature = JWSToken.rsplit('.')
|
|
except ValueError:
|
|
print(f'\033[91mError:\033[00m only found {len(JWSToken.rsplit("."))} out of 3 parts of the JWS token.')
|
|
|
|
if (not args.raw):
|
|
print('The QR code couldn\'t be decoded.')
|
|
elif (args.raw):
|
|
print('The raw data couldn\'t be decoded. Make sure your provided string starts with \'shc:/\'.')
|
|
sys.exit(1)
|
|
|
|
# Print
|
|
console = Console()
|
|
|
|
if (not args.raw):
|
|
console.rule("[bold bright_red]RAW QR CODE DATA")
|
|
print(QRData + " \n")
|
|
elif (args.raw):
|
|
print('\n') # the raw qr code data can be confusing so lets put a line between that and the command above
|
|
|
|
console.rule("[bold bright_red]JWS TOKEN")
|
|
print(JWSToken + "\n")
|
|
|
|
console.rule("[bold bright_red]JWS HEADER")
|
|
decHeader = base64url_decode(header.encode('utf-8'))
|
|
print_json(decHeader.decode('utf-8'))
|
|
print('\n')
|
|
|
|
console.rule("[bold bright_red]JWS SIGNATURE (base64)")
|
|
print(signature + "\n")
|
|
|
|
console.rule("[bold bright_red]JWS PAYLOAD")
|
|
decPayload = base64url_decode(payload.encode('utf-8'))
|
|
uncompressedPayload = zlib.decompress(decPayload, -15).decode("utf-8")
|
|
print_json(uncompressedPayload)
|
|
|
|
# Verify the JWS token signature
|
|
if (args.validate != False):
|
|
print('\n')
|
|
console.rule("[bold bright_red]Validate")
|
|
if (args.validate == None or len(args.validate) == 0):
|
|
print(f'\033[91mError:\033[00m must provide the path to the public key file (--validate [path to public key]) to validate the QR code signature against.')
|
|
else:
|
|
if (os.path.exists(args.validate)):
|
|
pass
|
|
else:
|
|
print(f'\033[91mError:\033[00m could not find public key file: {args.validate}')
|
|
print('Not implemented.')
|
|
|
|
# hmac_key = {
|
|
# "kid": "### INSERT KID FROM QR PAYLOAD HERE ###",
|
|
# "alg": "ES256",
|
|
# "kty": "EC",
|
|
# "crv": "P-256",
|
|
# "use": "sig",
|
|
# "x": "### INSERT X MATCHING KID HERE ###",
|
|
# "y": "### INSERT Y MATCHING KID HERE ###"
|
|
# }
|
|
# key = jwk.construct(hmac_key)
|
|
#
|
|
# signedMessage, encodedSignature = JWSToken.rsplit('.', 1)
|
|
# decoded_sig = base64url_decode(encodedSignature.encode('utf-8'))
|
|
# key.verify(signedMessage, decoded_sig)
|