Extracting Data from the Swiss Vaccination Certificate
Tags: programming
With the Swiss COVID vaccination campaign picking up some steam, I am grateful to have finally received two doses of a vaccine.1 I was mightily impressed when I also received a nice certificate with a QR code that I may now use to transfer information about my vaccination to a smartphone app—the idea being that every vaccinated person can present this as a proof. I tried to scan the QR code with my normal camera app and instead of a link or some other textual information, I just received what looked like gibberish at first glance. My curiosity thus being piqued, I searched for some way to extract more meaningful information from this code and quickly stumbled over Tobias Girstmair’s awesome description of how he decoded the Austrian vaccination certificate. In true ‘duct-tape programmer’ fashion, I was looking for a way to extend Tobias’s great description so that I could extract all this information with command-line tools. This post shows what I came up with.
Ingredients
You need a few utilities for this:
zbar
, for reading the QR code.- A
base45
decoder such aspython-base45
. - A way to extract
zlib
data, such aspigz
or a version of OpenSSL withzlib
support enabled. rq
, for working with CBOR data.- A set of POSIX core tools, namely
cut
andtr
.
If you are on Mac OS X, all of these—except python-base45
—can be
installed using Homebrew.
Recipe
Assuming your QR code is a file called QR.png
, this is how to extract
and nicely visualise its information:
zbarimg --raw QR.png \
| cut -b5- \
| tr -d '\n' \
| base45 --decode \
| pigz -d \
| cut -b20- \
| cut -b1-384 \
| rq -c
Let’s briefly dissect these commands to uncover their meaning:
- We extract the raw QR code data.
- We remove the outermost header (we are not interested in verifying the signature or the authenticity of that certificate; we just want the data).
- We remove the newline (to enable downstream processing).
- We decode the data (it uses
base45
for efficiency purposes). - We uncompress the data (it is compressed using
zlib
). - We remove some more header information (this was based on my educated guess about the length of the header section; you can also just keep the original data and look at it using
xxd
to see that I did not remove anything relevant here). - We remove the digital signature (we could also leave it in, but then the last command will not result in a pretty output; this is the duct-tape version, after all).
- We finally pretty-print everything.
This should result in an output like this:2
{
"1": {
"dob": "1943-02-01",
"nam": {
"fn": "Müller",
"fnt": "MUELLER",
"gn": "Céline",
"gnt": "CELINE"
},
"v": [
{
"ci": "urn:uvci:01:CH:2987CC9617DD5593806D4285",
"co": "CH",
"dn": 2,
"dt": "2021-04-30",
"is": "Bundesamt für Gesundheit (BAG)",
"ma": "ORG-100031184",
"mp": "EU/1/20/1507",
"sd": 2,
"tg": "840539006",
"vp": "1119349007"
}
],
"ver": "1.0.0"
}
}
There you have it—a pretty amazing amount of information contained in a nice QR code. The format appears to follow the Electronic Health Certificate to the letter,3 which is surprising to me because the relationship between the EU and Switzerland is somewhat complicated. I am glad to see that in these times, authorities are rallying under a common banner and creating interoperable standards.
Aftermath
This was fun to fiddle around with—I find command-line data processing pipelines neat because they tend to contain a mixture of tools from different decades. That being said, if you want to parse such certificates properly, you should read the most recent specification. This duct-tape version should not be used in production.
Stay healthy, until next time!
-
Insert obligatory 5G/telepathy joke here. ↩︎
-
This is not my real certificate. Your output might also contain a few additional lines that only specify some information about the issuer. Adjust the first
cut
command to remove them. ↩︎ -
I got my test data from this repository, but the code as shown here also works with my personal certificate. ↩︎