NOTE:
It has come to my attention that the deployment of SmartHealth Cards has gained momentum globally. This analysis also covers shc:/ format cards issued in Canada by Yukon, Alberta, Saskatchewan, Northwest Territories, Ontario and Quebec. The following US States have also made these QR codes available: Arizona, California, Hawaii, Louisiana, Maryland (some counties), Mississippi, New York, North Dakota, Virginia, Washington, Washington D.C. and West Virginia. Other jurisdictions include Cayman Islands, Singapore and the Sydney Local Health District in Australia.
Introduction
Thank you to everyone who provided feedback and interacted on social media regarding this topic. While it is clear we all have different ideas about security, privacy and vaccine mandates, there is one thing most of us have in common - we want to know how it works and if it is safe.
My initial post was designed to be informative for regular folks who want to know more about the vaccine card/passport. I personally do not consider this to be a vaccine passport for many reasons. One of these reasons is that, where passports create records of where you have been and who you have interacted with, the BC Vaccine Card thankfully does not; it merely attests to your status in a verifiable way.
Many of you peppered me with questions about implementation in the last few days, so while it means repeating the work of many other great security researchers, I will provide all of the technical details as to how the BC Vaccine Card works and some insight as to why some decisions may have been made. If you are one of the developers of the system, I would love to chat and determine whether these assumptions are correct and provide you an opportunity to correct any erroneous assumptions I may have made.
The first feedback I received from quite a few people was: “This QR code isn’t scannable, what the heck?!”
Analysis
Most QR codes you run across are designed to encode a small set of well-known formats. Most common is URLs, but other formats that may be recognized by common QR code readers are bookmarks, email, phone numbers, Wi-Fi SSIDs/passwords, geolocations and Bitcoin wallets. In the case of the BC Vaccine Card, it is using a framework called SMART Health Card, which is based on a W3C defined open standard.
If you scan the QR code in a reader that can read the raw text encoded in the image, you will see a long serialized URI starting with shc:/.
shc:/5676290952432060346029243740446031222959532654603460292540772804336028702864716745222809286133314564376531415906402203064504590856435503414245413640370636654171372412363803043756220467374075323239254334433260573601104131295371707424350541442769413771737666643354207740302124437175605830270559685331345838282662593160215069042305402439281164677574600962297508110845082134625425272303736545256469665237562868537303377257312737272153725936575625406955226341330558592069065336112726360853357072256663312129276224696627582457096637606350503230505935670335543958360433710470686652315712743107040071042244416745702171702859324038714555546945695553753600403366576753042709742934205232623875615959557353376754303607042545756669543306395911712355292423746059255226546433322256382232593303691270286630657368316120685623681120577632080421111108214256364358632556272145696409427607056809123156611028256540745473086370416673737034052806031038556650547500580526763875057436457476093165684123277257773530637411087706105956535461534411606508402928614333391258612263252857567110720823754263504259442808686259591038283475684533633071662720592112297476763036384204040434647469313734681238450674777568640045376312240365312171237100036068225072124131055225242743545033677628613726351067052340426353651136324245093444422438586131126968590571545826317745200564220869712771616806212907696174756644086743380407602656560909255241680333063461051021633771503274772858215321123007352137744473327061617456580958081245355906506374776610106850017342244165087767383036242168032429333974047672443050743137085757306457257255071203440906107658106441625660242965117109675540762945107028702572031264072822095200747706694220
That long string of digits is a a serialized JWS (JSON Web Signature) token. A JWS token is really just a JWT (JSON Web Token) that has been digitally signed. A JWS is made up of a header, the actual content and a signature to assure the content hasn’t been modified and is authentic. Additonally, the payload component is minified and Gzip’d to reduce the size of the data so it fits within a QR code.
If we un-Gzip and prettify the payload we end up with something like this (My BC Vaccine Card with some details altered to protect my privacy):
{
"iss": "https://smarthealthcard.phsa.ca/v1/issuer",
"nbf": 1631590344.175,
"vc": {
"type": [
"https://smarthealth.cards#health-card",
"https://smarthealth.cards#immunization",
"https://smarthealth.cards#covid19"
],
"credentialSubject": {
"fhirVersion": "4.0.1",
"fhirBundle": {
"resourceType": "Bundle",
"type": "collection",
"entry": [
{
"fullUrl": "resource:0",
"resource": {
"resourceType": "Patient",
"name": [
{
"family": "WISNIEWSKI",
"given": [
"CHESTER"
]
}
],
"birthDate": "1975-12-01"
}
},
{
"fullUrl": "resource:1",
"resource": {
"resourceType": "Immunization",
"status": "completed",
"vaccineCode": {
"coding": [
{
"system": "http://hl7.org/fhir/sid/cvx",
"code": "208"
},
{
"system": "http://snomed.info/sct",
"code": "28581000087106"
}
]
},
"patient": {
"reference": "resource:0"
},
"occurrenceDateTime": "2021-04-08",
"lotNumber": "EW3344",
"performer": [
{
"actor": {
"display": "Vancouver Convention Centre"
}
}
]
}
},
{
"fullUrl": "resource:2",
"resource": {
"resourceType": "Immunization",
"status": "completed",
"vaccineCode": {
"coding": [
{
"system": "http://hl7.org/fhir/sid/cvx",
"code": "208"
},
{
"system": "http://snomed.info/sct",
"code": "28581000087106"
}
]
},
"patient": {
"reference": "resource:0"
},
"occurrenceDateTime": "2021-05-24",
"lotNumber": "EY0586",
"performer": [
{
"actor": {
"display": "Vancouver Convention Centre"
}
}
]
}
}
]
}
}
}
}
Let’s start to break this down. First field is iss, which is a HTTPS URL that must have CORS enabled and is postpended with /.well-known/jwks.json. For BC Vaccine Cards this value results in https://smarthealthcard.phsa.ca/v1/issuer/.well-known/jwks.json which contains the public key to verify the JWS token has been signed by the issuer.
keys
0
kty "EC"
kid "XCqxdhhS7SWlPqihaUXovM_FjU65WeoBFGc_ppent0Q"
use "sig"
alg "ES256"
crv "P-256"
x "xscSbZemoTx1qFzFo-j9VSnvAXdv9K-3DchzJvNnwrY"
y "jA5uS5bz8R2nxf_TU-0ZmXq6CKWZhAG1Y4icAx8a9CA"
Next we have nbf, which is shorthand for not before. It is a simply a decimal string which is specified in epoch to denote the date and time from which the vaccine card is valid. Then we have vc, which is the verifiable credential, and the type, which in this case links to URIs describing that this QR code is for COVID-19 vaccination status.
The next section is credentialSubject, which is the information that allows you to verify the identity of the person supplying the credential, in this case the vaccine card. The important fields are “resourceType” - which should always be “patient”, name - both family and given, and birthDate. In the example provided the credentialSubject is resource:0.
The first vaccination is represented as resource:1. It includes a type “Immunization”, status “completed”, vaccineCode which denotes which vaccine you received and the occurranceDateTime, lotNumber and performer (location). If you have received a second vaccine it will be the same, but likely encoded as resource:2.
Thoughts/Opinions
There are no simple answers when it comes to decisions around vaccination proofs and preservation of privacy. Like anything these things require a balance and the choices made will leave many people unhappy with the compromises that were chosen. Personally I think the province of British Columbia has chosen a reasonable balance, but certainly not perfect by anyone’s personal views.
Some have questioned the inclusion of date of birth and details of vaccine types and times. While I have not had the opportunity to consult with the province on how decisions were made there are some logical conclusions as to why these choices might have been made.
Many people, especially male residents, may have the same names as their fathers, mothers or forebearers. The date of birth allows for more detailed confirmation if deemed necessary in the future. Similarly the type of vaccine and the dates may also be important if this identification is used for international travel or other means in the future.
The privacy guidance from the OIPC requires businesses not to decode the QR with anything other than the provincial verifier app and states that it cannot be stored. This of course cannot be enforced, but does come with potential penalties for violations.
In essence, the data contained int the QR code is minimally intrusive, but provides just enough for its current intended use and enough to allow for future use without disclosing anything particularly sensitive. The alternative would require an online lookup of information which introduces a plethora of privacy issues that far outweigh the risk and impact of the information currently encoded in the QR code.
The abiility to confirm status offline, combined with just enough information to expedite travel while remaining offline, which prevents tracking by the government, seems to be the ideal balance of privacy, security and integrity.
None of these technical measures prevent abuse and tracking/correlation by the organizations we entrust with scanning our codes, but as always there must be some balance and trust involved. The risks are very low, the potential penalties to violators could be significant and overall this appears to be a well thought out solution.
In the end, all of the alternatives are worse. So, let’s go with the thing that sucks the least.