initial commit
authorTobias Girstmair <tobi@isticktoit.net>
Mon, 21 Jun 2021 16:29:36 +0000 (18:29 +0200)
committerTobias Girstmair <tobi@isticktoit.net>
Mon, 21 Jun 2021 17:29:12 +0000 (19:29 +0200)
greenpass.py [new file with mode: 0644]

diff --git a/greenpass.py b/greenpass.py
new file mode 100644 (file)
index 0000000..853a758
--- /dev/null
@@ -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'])
Imprint / Impressum