From: Tobias Girstmair Date: Mon, 21 Jun 2021 16:29:36 +0000 (+0200) Subject: initial commit X-Git-Url: https://git.gir.st/greenpass.git/commitdiff_plain/eee81d79d748d50765e6fd461499c297f165e67e?ds=inline initial commit --- eee81d79d748d50765e6fd461499c297f165e67e diff --git a/greenpass.py b/greenpass.py new file mode 100644 index 0000000..853a758 --- /dev/null +++ b/greenpass.py @@ -0,0 +1,79 @@ +#!/usr/bin/python3 + +# Copyright 2021 Tobias Girstmair (https://gir.st). Consider this code GPLv3 +# licensed. + +# pip3 install flynn base45 PyPDF2 pyzbar +# dnf install zbar || apt install libzbar0 + +import sys +import glob +import json +import zlib +import flynn +import base45 +import PyPDF2 +from PIL import Image +from pyzbar import pyzbar +from datetime import datetime +from urllib.request import urlopen + +sch = urlopen('https://raw.githubusercontent.com/ehn-dcc-development/ehn-dcc-schema/release/1.3.0/DCC.combined-schema.json') + +if len(sys.argv) < 2: + try: + infile = glob.glob("COVID-19-*-*-*.pdf")[0] + print(f"Warning: using file {found}, since not specified\n", file=sys.stderr) + except: + print(f"Usage: {sys.argv[0]} COVID-19-*-*-*.pdf", file=sys.stderr) + print(f"Usage: {sys.argv[0]} QR_CODE.png", file=sys.stderr) + sys.exit(1) +else: + infile = sys.argv[1] + +if open(infile, "rb").read(4) == b"%PDF": + # extract QR code from PDF using hard-coded index, size and bit depth. + # This will only work with the official Austrian green pass PDFs. + pdf=PyPDF2.PdfFileReader(open(infile, "rb")) + qr_img = pdf.getPage(0)['/Resources']['/XObject']['/Im3'] + qr_pil = Image.frombytes("1", (400,400), qr_img.getData()) +else: # assume image + qr_pil = Image.open(infile) + +# decode QR code into raw bytes: +qr_data_zlib_b45 = pyzbar.decode(qr_pil)[0].data + +# strip header ('HC1:') and decompress data: +qr_data_zlib = base45.b45decode(qr_data_zlib_b45[4:]) +# decompress: +qr_data = zlib.decompress(qr_data_zlib) + +# decode cose document: +(_, (headers1, headers2, cbor_data, signature)) = flynn.decoder.loads(qr_data) +# decode cbor-encoded payload: +data = flynn.decoder.loads(cbor_data) + +date = lambda ts: datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') +print("QR Code Issuer :", data[1]) +print("QR Code Expiry :", date(data[4])) +print("QR Code Generated :", date(data[6])) + +glb_schema = json.load(sch) + +def annotate(data, schema, level=0): + for key, value in data.items(): + description = schema[key].get('title') or schema[key].get('description') or key + description, _, _ = description.partition(' - ') + if type(value) is dict: + print(' '*level, description) + _, _, sch_ref = schema[key]['$ref'].rpartition('/') + annotate(value, glb_schema['$defs'][sch_ref]['properties'], level+1) + elif type(value) is list: + print(' '*level, description) + _, _, sch_ref = schema[key]['items']['$ref'].rpartition('/') + for v in value: + annotate(v, glb_schema['$defs'][sch_ref]['properties'], level+1) + else: # value is scalar + print(' '*level, description, ':', value) + +annotate(data[-260][1], glb_schema['properties'])