-
Notifications
You must be signed in to change notification settings - Fork 0
/
dgc_reader.py
executable file
·89 lines (69 loc) · 2.67 KB
/
dgc_reader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/env python3
# -*- coding: utf8 -*-
"""
A simple (not practical) example of processing of data read from QR code of
Digital Green Certificate (aka EU Digital COVID Certificate). I tried that on
data generated by https://ocko.uzis.cz/ only.
Primary references:
- https://ec.europa.eu/info/live-work-travel-eu/coronavirus-response/safe-covid-19-vaccines-europeans/eu-digital-covid-certificate_en
- https://ec.europa.eu/health/sites/default/files/ehealth/docs/digital-green-certificates_v3_en.pdf
- https://cbor2.readthedocs.io/en/latest/index.html
- https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
Secondary references:
- https://twitter.com/zajdee/status/1399436026398101508
"""
from datetime import datetime
import argparse
import json
import sys
import zlib
from base45 import b45decode
import cbor2
def main():
ap = argparse.ArgumentParser(
description="Process QR code data from EU Covid19 DGC")
ap.add_argument(
"file",
nargs='?',
type=argparse.FileType('r'),
default=sys.stdin,
help="file with plaintext data decoded from the QR code")
args = ap.parse_args()
qr_text = args.file.read().rstrip()
# get rid of headers which are not part of the base45 encoded data
if qr_text.startswith("QR-Code:"):
qr_text = qr_text[8:]
if qr_text.startswith("HC1:"):
qr_text = qr_text[4:]
# The QR code contains a cbor tag,
# see https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
cbortag = cbor2.loads(zlib.decompress(b45decode(qr_text)))
# We expect COSE Single Signer Data Object (signed cbor tag).
assert cbortag.tag == 18
assert len(cbortag.value) == 4
# See also 2.6.1 COSE Structure from digital-green-certificates_v3_en.pdf
alg = cbor2.loads(cbortag.value[0])[1]
# Expecting ES256 Signing Algorithm
assert alg in (-7, -37)
# Key identifier (First 8 bytes of the hash value)
kid = cbortag.value[1][4].hex()
# Payboad (with DGC data)
payload = cbor2.loads(cbortag.value[2])
# Signature of the payload
signature = cbortag.value[3].hex()
# TODO: verify signed payload
# See 2.6.3 Common Payload Values from digital-green-certificates_v3_en.pdf
assert len(payload) == 4
# Payload of the DGC (Cac, Tst, Rec)
hcert = payload[-260]
# Issuer of the DGC
iss = payload[1]
# Issuing date of the DGC
iat = datetime.fromtimestamp(payload[6])
# Expiring date of the DGC
exp = datetime.fromtimestamp(payload[4])
# Just print some data
print(f"issued by {iss} on {iat}, expiring on {exp}", file=sys.stderr)
print(json.dumps(hcert, indent=4))
if __name__ == '__main__':
sys.exit(main())