Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miklobit/8351ed4cc7a67ec63635a74618770644 to your computer and use it in GitHub Desktop.
Save miklobit/8351ed4cc7a67ec63635a74618770644 to your computer and use it in GitHub Desktop.
Decode information from European covid19 travel certificates
#######
#
# REQUIRES: pip install cose #!!!!
#
# HOW TO USE:
# 1. Use your favourite QR scanner to scan the code from a certificate,
# you'll get a string that starts with "HC1:"
# 2. Run the script: python3 covid-qr-certificate-decoder.py
# 3. Paste the string when prompted
#
#
# READING (how to interpret the output and test data):
# api spec: https://github.com/eu-digital-green-certificates/dgca-issuance-web/blob/main/src/generated-files/dgc-combined-schema.d.ts
# https://github.com/eu-digital-green-certificates/dgca-issuance-web/blob/main/src/misc/edgcProcessor.tsx
# https://github.com/admin-ch/CovidCertificate-Examples
# https://github.com/eu-digital-green-certificates/dgc-testdata/tree/main/PL
#
#######
from typing import Union
import zlib
from cose.messages import CoseMessage
import cbor2
import pprint
import json
# example code for testing
# QRCODE = 'HC1:6BFC80Z80T9WTWGVLK659A:TG4G$BTZM0X*4FBBE*3FN0KKC+H3WY0/9CWC4*70 6AD97TK0F90KECTHGXJC$+D.JCBECB1A-:8$966469L6OF6VX6Z/ER2DD46JH8946XJCCWENF6OF63W5NW60A6WJCT3E 6AWJC0FDTA6AIA%G7X+AQB9746XG7TS9 967:6DL6WX8BM8CA6DB83R63X6$A7UF6QG8Q46/A8.A8$96V47.JCP9EJY8L/5M/5 96.96WF6%JCXQEIN8G/D6LE ZDQZCAJB0LEE4F0ECKQEPD09WEQDD+Q6TW6FA7C46TPCBEC8ZKW.CHWEBIAG09:S9Q+9DN90S7BIAOT9W.CUEEY3EAECWGDMXG2QDUW5*MEWUMMPCG/D8EDETAG+9*NAWB8 JC6/DYOACEC+EDR/OLEC$PC5$CUZCY$5Y$5JPCT3E5JDKA7Q47%964W5WA6N68$E5FAWIBG9SQCLEH19FZMD8B TL6AW8JJRBHTB91L0GMSNRCBBPL8R958CR2:7T84H7*6C V86W0*8G1MZJVS72L:M5WAB9UB0HF0'
# credits: https://github.com/kirei/python-base45/blob/main/base45/__init__.py
def b45decode(s: Union[bytes, str]) -> bytes:
"""Decode base45-encoded string to bytes"""
BASE45_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
res = []
try:
if isinstance(s, str):
buf = [BASE45_CHARSET.index(c) for c in s]
else:
buf = [BASE45_CHARSET.index(c) for c in s.decode()]
buflen = len(buf)
for i in range(0, buflen, 3):
x = buf[i] + buf[i + 1] * 45
if buflen - i >= 3:
x = buf[i] + buf[i + 1] * 45 + buf[i + 2] * 45 * 45
res.extend(list(divmod(x, 256)))
else:
res.append(x)
return bytes(res)
except (ValueError, IndexError, AttributeError):
raise ValueError("Invalid base45 string")
def main() -> None:
data = input("Paste the decoded QR code string and press Enter:\n")
if data.startswith('HC1:'): data = data[4:]
decoded = b45decode(data)
decompressed = zlib.decompress(decoded);
msg = CoseMessage.decode(decompressed);
phdr, uhdr = msg._hdr_repr()
payload = cbor2.loads(msg.payload)
#payload is the most interesting part, the others are rather boring
print("\n")
print("==PHDR==\n")
pprint.pprint(phdr)
print("==UHDR==\n")
pprint.pprint(uhdr)
#print("==SIGNATURE==\n",msg.signature,"\n")
print("==PAYLOAD==")
print(json.dumps(payload, ensure_ascii=False, indent=4))
print("\n")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment