]>
git.gir.st - greenpass.git/blob - greenpass.py
3 # Copyright 2021 Tobias Girstmair (https://gir.st). Consider this code GPLv3
6 # pip3 install flynn base45 PyPDF2 pyzbar Pillow
7 # dnf install zbar || apt install libzbar0
18 from pyzbar
import pyzbar
19 from datetime
import datetime
20 from urllib
.request
import urlopen
22 sch
= urlopen('https://raw.githubusercontent.com/ehn-dcc-development/ehn-dcc-schema/release/1.3.0/DCC.combined-schema.json')
26 infile
= glob
.glob("COVID-19-*-*-*.pdf")[0]
27 print(f
"Warning: using file {found}, since not specified\n", file=sys
.stderr
)
29 print(f
"Usage: {sys.argv[0]} COVID-19-*-*-*.pdf", file=sys
.stderr
)
30 print(f
"Usage: {sys.argv[0]} QR_CODE.png", file=sys
.stderr
)
31 print(f
"Usage: {sys.argv[0]} 'HC1:.....'", file=sys
.stderr
)
34 if os
.path
.exists(sys
.argv
[1]):
38 qr_data_zlib_b45
= sys
.argv
[1]
41 if open(infile
, "rb").read(4) == b
"%PDF":
42 # extract QR code from PDF using hard-coded index, size and bit depth.
43 # This will only work with the official Austrian green pass PDFs.
44 pdf
=PyPDF2
.PdfFileReader(open(infile
, "rb"))
45 qr_img
= pdf
.getPage(0)['/Resources']['/XObject']['/Im3']
46 qr_pil
= Image
.frombytes("1", (400,400), qr_img
.getData())
48 qr_pil
= Image
.open(infile
)
50 # decode QR code into raw bytes:
51 qr_data_zlib_b45
= pyzbar
.decode(qr_pil
)[0].data
53 # strip header ('HC1:') and decompress data:
54 qr_data_zlib
= base45
.b45decode(qr_data_zlib_b45
[4:])
56 qr_data
= zlib
.decompress(qr_data_zlib
)
58 # decode cose document:
59 (_
, (headers1
, headers2
, cbor_data
, signature
)) = flynn
.decoder
.loads(qr_data
)
60 # decode cbor-encoded payload:
61 data
= flynn
.decoder
.loads(cbor_data
)
63 date
= lambda ts
: datetime
.utcfromtimestamp(ts
).strftime('%Y-%m-%d %H:%M:%S')
64 print("QR Code Issuer :", data
[1])
65 print("QR Code Expiry :", date(data
[4]))
66 print("QR Code Generated :", date(data
[6]))
68 glb_schema
= json
.load(sch
)
70 def annotate(data
, schema
, level
=0):
71 for key
, value
in data
.items():
72 description
= schema
[key
].get('title') or schema
[key
].get('description') or key
73 description
, _
, _
= description
.partition(' - ')
74 if type(value
) is dict:
75 print(' '*level
, description
)
76 _
, _
, sch_ref
= schema
[key
]['$ref'].rpartition('/')
77 annotate(value
, glb_schema
['$defs'][sch_ref
]['properties'], level
+1)
78 elif type(value
) is list:
79 print(' '*level
, description
)
80 _
, _
, sch_ref
= schema
[key
]['items']['$ref'].rpartition('/')
82 annotate(v
, glb_schema
['$defs'][sch_ref
]['properties'], level
+1)
83 else: # value is scalar
84 print(' '*level
, description
, ':', value
)
86 annotate(data
[-260][1], glb_schema
['properties'])