Skip to navigation Skip to main content Skip to footer

Exploring the Security & Privacy of Canada’s Digital Proof of Vaccination Programs

31 December 2021

By Drew Wade

by Drew Wade, Emily Liu, and Siddarth Adukia

TL; DR

We studied a range of Canadian provinces’ proof-of-vaccination apps to analyze their associated security and privacy properties. In particular, building on prior work in which some of us created an assessment framework for evaluating the security privacy of vaccine passports, in this post, we discuss our exploration of the Verify Ontario app in depth to understand its’ ability to correctly reject forged and malformed vaccine credentials, to avoid collecting unnecessary private data, and to manage data using security best-practices.

Canada’s provincial and territorial Proofs of Vaccination are:

  • based in a sound standard that minimizes the inclusion of personal information
  • collected from online sources that securely verify the requestor’s identity
  • protected against tampering by strong cryptographic cyphers

The Verify Ontario application appears to:

  • reliably reject forged and malformed Proofs of Vaccination
  • manage data securely
  • avoid collecting unnecessary private data
  • only temporarily store and transmit non-private analytics data to Google

Introduction

With the requirements for Canadian businesses to verify COVID-19 vaccination status in many settings, and with use of the provincial and territorial proofs of vaccination for international travel, we are left with the question of how well our privacy and proof authenticity is being protected as we show proof of vaccination to businesses and governments at home and around the world. These concerns are most poignant now that Ontario is requiring QR codes to be the means of verifying vaccination status in the face of rising Omicron numbers.

Our goal in this blog post is to provide an overview of the digital proof of vaccination efforts across Canada and to explore in greater technical detail Ontario’s digital proof of vaccination and its proof verification application. We examined the processes for collecting proofs of vaccination, available samples of the proofs for several provinces and territories, and the iOS and Android mobile applications for verifying proofs in Ontario. The Ontario verifier app was reviewed for security and privacy concerns in its handling of network traffic and locally stored data, and for its response to forged and malformed QR codes.

Individual provinces and territories provide greater or lesser protections in claiming a proof of vaccination, largely relying on provincial or territorial health card numbers. Given the major healthcare data breaches in recent years, both Ontario and Newfoundland and Labrador make the notable requirement for a security code or expiry date from a valid health card. These government sites do not appear to be particularly vulnerable to brute-force requests for proofs of vaccination or to manipulation of the government e-health information systems behind them, though our analysis mainly focused on non-server components. No highly-specific or personally-identifying information was found incorporated into pre-populated portions of the site used to verify identity. 

The now pan-Canadian use of the SMART Health Card standard maintains privacy while providing a reliable means of verifying vaccination status. Because the data is digitally signed, a Verifier App only needs to check the signature to confirm that the encoded data is valid and does not need to make connections out to healthcare databases. This limits the opportunities for data exposure and compromise of the provincial or territorial healthcare information systems. Quebec could stand to do better with its information exposure; the inclusion of sex/gender information and the name of the vaccination clinic are not necessary for proof of vaccination.

Most provinces and territories have produced their own verifier applications, able to verify the signed data’s integrity, to interpret a vaccination status from that data, and to display basic identification information to be confirmed against government-issued photo identification. Credentials that only display a vaccination status and name offer a lower degree of assurance that the vaccine credential and government-issued ID belong to the same individual. The Alberta and Ontario verifier apps additionally display the date of birth, decreasing the likelihood of identity spoofing. 

Looking specifically at Ontario, the Proof collection process makes use of multiple stages of identification and validation to securely provide Proofs for those with valid claim to them. The verifier app does not need to be connected to the Internet to work, and only requires Internet access for: (1) updates to Ontario’s vaccination rules and (2) to public keys for other provinces and territories. The app does periodically send analytics data to Google Analytics, but only appears to send basic anonymized device information and non-sensitive QR scan data (e.g. number of positive scans). Importantly, the app correctly failed to validate unsigned, malformed, and forged QR codes, verifying the QR codes based on secure digital signature by the Ontario Ministry of Health. While it is trivial to create your own QR code, it would be near impossible to sign the data in a way that will trick the verifier application without access to the private keys used by the government healthcare bodies issuing valid proofs of vaccination.

Disclaimer

No testing was performed against provincial and territorial vaccination and e-health websites, and only passive observations on the public portion of these sites were made for interactions to receive a proof of vaccination. 

Terminology

Proof (AKA “vaccine passport”, “vaccine certificate”)

a means of verifiably and reliably demonstrating that someone has received one or more COVID-19 vaccinations. This may refer to a variety of cards, documents, websites, and print or digital codes, but in the Canadian context has predominantly come to mean the vaccination receipts and QR codes issued by provincial and territorial health authorities.

Credential App 

a dedicated (typically mobile) application for storing and presenting a Proof for verification at venues required to confirm vaccination status prior to admittance (e.g., gyms, bars, offices, concert halls, etc.). This can be construed to include Apple’s Wallet application, which now stores vaccination Proof QR codes, but some provinces have their own Credential App.

Verifier App

a dedicated (typically mobile) application for reading and validating the authenticity of a Proof. In the Canadian context this is a QR code reader that can verify the integrity of the data presented to confirm its source and authenticity, as well as assess the encoded vaccination types and dates for conformance to provincial regulations.

Evolution of Vaccination Proofs in Canada

In large part due to the provincial and territorial responsibility for healthcare, Canada has taken a province-specific approach to developing vaccine Proofs, Credential Apps, and Verifier Apps. With the mass inoculation of Canadians in the spring of 2021, there became a steadily growing need for a means to demonstrate vaccination status to employers in essential services and other venues hoping to reopen their businesses safely. Beginning in May 2021, Quebec began providing a QR code as an electronic Proof for its citizens based on the SMART Health Card standard. Manitoba followed suit in August, releasing a QR code of their own that linked to the provincial e-health website rather than encoding vaccination data directly to the QR code. British Columbia released a QR code, also using the SMART standard in September, and the remaining provinces and territories have released a variety of SMART-based QR codes, along with Credential and Verifier Apps, to be used in their reopening strategies. At the time of writing, Canada has adopted the SMART standard across the country, with Manitoba issuing a new SMART-based Pan-Canadian Vaccine Credential (PVC) version, to facilitate their use as Canadian proofs of vaccination for international travel. 

SMART Health Card (SHC) Standard

The SMART Health Card standard is now in use across Canada as the basis for demonstrating proof of vaccination in Canadian businesses and for international travel. The SMART standard was designed with privacy in mind, and the standard is intended only to encode:

  •         Your legal name and date of birth
  •         Your clinical information
  •         Tests: date, manufacturer, and result
  •         Vaccinations: type, date, and location

and aligning with the principles of data minimization, should not contain other personal information such as:

  •         Your phone number
  •         Your address
  •         Your government-issued identifier 
  •         Any other health information

A SMART Health Card encodes this information in several ways:

  •         the data is recorded in a structured format (JSON)
  •         the data is digitally signed to demonstrate it has not been tampered with
  •         the signed data is converted to a string of numbers (e.g. shc://235961…884378)
  •         the SHC data is converted to a QR code that can be read by any QR code reader

Because the data is digitally signed, a Verifier App only needs to check the signature to confirm that the encoded data is valid, and does not need to make connections out to healthcare databases. This limits the opportunities for data exposure and compromise of the provincial or territorial healthcare information systems. The SMART Health Card QR code, however, does not store any other individuating information. In order to verify that the data presented in the QR code belongs to the person presenting it, it is still necessary to show additional photo identification, such as a driver’s license or passport, for comparison to the basic name and birthdate information in the QR code data.

Verifier Applications

In order to access the data encoded in a SMART QR code, most people will use a verifier application, which takes the SHC string of numbers and reproduces the vaccination data and digital signature. At this point, most applications will check the structured vaccination data and, depending on the standards of vaccination required (e.g., 2+ doses of Pfizer or Moderna, or 1 AstraZeneca and 1+ Pfizer or Moderna, or 3+ other COVID-19 vaccines, etc.), return a pass/fail indication for those standards. Most provinces and territories have produced their own verifier applications, able to verify the signed data’s integrity, to interpret a vaccination status from that data, and to display basic identification information to be confirmed against government-issued photo identification. 

Not all provinces and territories have their own Verifier Apps and, at various stages of release of QR codes, third-party applications have been recommended with mixed results. The Northwest Territories, for example, has no Verifier App of its own and is recommending The Commons Project’s SMART Health Card Verifier App. The Commons Project is a Rockefeller Foundation-supported non-profit public trust that “builds and operates digital platforms and services for the common good“. While no major issues have arisen from its use, the same cannot be said for other third-party applications like Portpass, recommended by the Calgary Sports and Entertainment Corporation for vaccination status verification at sports arenas in Calgary, which suffered from poor validation in storing and verifying vaccination data.

Canada’s Proofs of Vaccination

For each of the provincial and territorial proofs, we examined each step from the publicly-accessible portions of the process of collecting a Proof through to verification of the Proof, as best we were able to without access to valid Proofs for every province and territory. We begin by providing a review of this information, as it is the most readily understandable to a wide non-technical audience and not limited to a single geographic region of the country. Because one or more of the authors are residents of Ontario and have valid claims to Proofs for Ontario, we were also able to scrutinise the Ontario Proof collection, presentation, and verification processes in greater detail. A more technical and detailed exploration of that process follows the coast-to-coast-to-coast overview of vaccination Proofs in Canada.

Our analysis is loosely modelled on the earlier threat model on vaccine credentials we published on the NCC Group Research blog.

Collecting a Proof

Privacy concerns exist immediately with the process for collecting a Proof; the benefit of proof of vaccination would surely be outweighed by an insecure collection process that exposed sensitive personal and healthcare data. To gauge the security of the issuing site, we accessed the public portions of the provincial and territorial proof of vaccination websites to answer the following questions:

  • How is the information request formatted (free-form text/multiple-choice/calendar/other)?
  • What information is required to verify identity?
  • How is the information transmitted between the browser/application and healthcare record system?
  • Is it likely that an attack process (e.g., brute-force, open-source intelligence) could collect a Proof for someone else?

For the most part, the information is provided via free-form text boxes and calendar pop-ups or drop-down menus. No highly-specific or personally-identifying information is incorporated into pre-populated drop-down menus to verify identity. In a handful of cases, information other than dates is provided using multiple select buttons and drop-down menus. These are used for sex/gender questions and for the commercial names of vaccines. Several sites also used checkboxes to opt out of items that were not applicable (see Quebec info), to confirm completeness and validity of provided information, and for reCAPTCHA confirmations.

For Manitoba, New Brunswick, and Saskatchewan, the collection process is completed within the provincial e-health website, which could not be accessed or tested within the scope of this research. In all three cases, an email and password are required to access the site and the security of the Proofs contained within hinge on the basic security protections applied to the login page. The Yukon Territory process was also incompletely reviewed, as personally-identifying information is provided in stages, with validation of one set required before more is requested and provided; we will look only at the publicly-accessible request here.

The most common pieces of information requested were provincial or territorial health card numbers, names, and dates of birth (see Table 1 below – Identity verification info). While one’s health card number is likely kept private, names and dates of birth are often very easily accessed through OSINT (Open-Source INTelligence – e.g. social media, personal websites, etc). Other requested information that is relatively easy to collect through OSINT includes dates of vaccination (Month/Year for AB, NT; Day/Month/Year for BC, QC), email, postal codes, sex or gender, and the vaccine brand received. The sex and gender questions, too, are very limited (Male/Female “gender” for QC; Male/Female/Other “sex as written on your health card” for YT). The commercial vaccine name options varied widely, from only three options for the Northwest Territories to twenty-three options for Quebec. Several provinces required additional information from the user’s health card, including the security code number, expiry, and version code. Quebec also requests parental names (or confirmation that their identity is unknown), provides an option for confirmation of no insurance (health card) number, and has an option for confirmation of exemption from vaccination. Only four provinces used reCAPTCHAs, but these were limited to an “I am not a robot” checkbox.

Table 1: Identity Verification Information by Province

Testing the protections on the HTTPS-based web forms and the back-end APIs that service the sites was outside the scope of this research, so we will focus on the nature of the information provided to assess the likelihood of an attacker gaining access to Proofs for which they have no valid claim. As mentioned earlier, a great deal of information requested for claiming a Proof is relatively easy to access through OSINT methods. People regularly share their email addresses, their ages and birthdays, their home addresses or neighbourhoods, their sex or gender, and their parents’ names, on social media, job site profiles, and other websites that display this data publicly or have leaked private data at one time or another. In the excitement of finally receiving a vaccination, and from the desire to inspire others to get theirs, many people posted to social media both the date and brand of vaccination they received. 

As a result of all of this publicly-accessible information being used for identity verification in the Proof collection process, most of the efforts at verifying the identity of the individual requesting a Proof rely on knowledge of a provincial or territorial health card number. While Canadians tend to be fairly guarded about that piece of information in their day-to-day lives, it is shared with a variety of healthcare providers. Between the major healthcare data breach at LifeLabs in 2019 and the ongoing targeting of hospitals in ransomware attacks, it is not unlikely that health card numbers and their associated names have been available to attackers seeking to mine more personal information, to profit from schemes to circumvent public health measures, and to disrupt vaccination and proof of vaccination efforts. As a result, the addition of health card expiry and security code features for identity verification become very important. This is particularly relevant for Ontario and British Columbia, which were most heavily affected by the LifeLabs data breach, but at time of writing, only Ontario has implemented these additional identifiers in their Proof collection process.

Other notable exceptions in the Proof request process were Nova Scotia, which required only the email provided at time of vaccination and the health card number, but which sends the Proof to the registered email address; Prince Edward Island, which has an automated phone option that will send a 6-digit verification code to proceed by text or automated call to the phone number on file; and Nunavut, for which Proofs can only be obtained in person at local health centres, Iqaluit Public Health, or Qikiqtani General Hospital.

The Proof

Once a Proof has been claimed, further privacy concerns arise in the form of the data encoded in that Proof. Where possible, we collected sample QR codes from government websites, requested examples or overviews of QR data from colleagues, and examined our own QR codes to understand what information is potentially shared in each verification transaction. We were specifically interested to know:            

  • How is the information encoded?
  • How is the information integrity maintained?
  • What information is encoded?
  • What information is advertised as being encoded? 
  • Can this information be misused?

As all of the provinces and territories of Canada have adopted the SMART Health Card standard, the question of how the encoding is done is relatively simple, as discussed earlier. Based on the QR codes we viewed directly and with the assistance of colleagues (BC, ON, QC, SK, YT), the structured vaccination data is signed using a strong cryptographic system (ECDSA with curve P-256). The reliability of this digitally-signed data will depend on the ability of the government healthcare information systems to safeguard the private keys used to sign the data. The exception to the SMART-based QR codes is the original Manitoba QR code, now for use only within Manitoba, which encoded only a link to the Manitoba immunization card website and a unique 32-character ID number. A username and password login is still required to access the vaccination status information.

For the most part, use of the SMART Health Card standard limited the personally-identifying information encoded in the QR codes we share (see Fig. 1 below – sample SMART data). The holder’s full name and date of birth are both required for validation against photo ID, and in most cases the remaining data is limited to vaccine types, lot numbers, and vaccination dates. There is a valid reason for keeping the lot numbers in there, in case it later turns out that a lot was bad or a placebo or there was some other reason not to treat that batch as a successful inoculation. The Quebec QR code, however, also encodes “gender” (male or female) and the name of the vaccination clinic where the vaccination was received. The other provinces and territories reviewed have used the location field more generally to note the province or territory of vaccination instead. This “gender” and geolocation information is not necessary for identification or for verification of vaccination.

Figure 1: Sample SMART Health Card Data for Saskatchewan

Verifying a Proof

With a Proof in hand – and hopefully not containing too much sensitive personal information – the next important point of contact that represents a potential privacy concern is the application used to verify the Proof. For Proof verification apps, we were concerned with the following questions

  • How is information in the Proof transmitted to the Verifier?
  • How is the integrity of the Proof verified?
  • What information is used by the Verifier to display a result?
  • What information is displayed in the Verifier?
  • Is the source code for the Verifier publicly accessible?

Not every province and territory has produced a Verifier App; Nunavut and New Brunswick are directing their citizens to their government e-health websites, while the Northwest Territories are recommending The Commons Project’s SMART Health Card Verifier app. The provinces and territory that do, however, are making use of the same SMART standard and displaying very similar information. In all of the Verifier Apps, the information is transmitted via the device’s camera to scan the QR code. Android devices require an additional dedicated app (such as the Verifier App) to parse QR codes, while iOS/iPadOS devices are natively capable of reading QR codes from the Camera app. Without a Verifier App, an Apple device reading the QR code will direct the user to the Health app, which can decode and parse SMART Health Card data; the exception being Manitoba’s URL-based QR which is directed to the device’s default browser. Once the QR code is scanned, the SHC data is decoded, and the signature should be checked to verify that the data has not been tampered with since it was signed by the issuing government healthcare information system. The app requires the public key of the cryptographic key-pair used to sign the data to verify the digital signature, allowing the verification to happen offline and reduce the attack surface of the government healthcare information systems. With no private data being sent to or from the device by the Verifier App, there is also no opportunity for an attacker to listen in on the communication. 

While a detailed review of the function of all of the available provincial and territorial Verifier Apps should be performed in the future, it is likely that they function similarly, given that they must work with nearly the same set of SMART Health Card data to confirm vaccination status. This requires the app to check the numbers and types of vaccinations and to compare the vaccination dates to ensure the requisite number of accepted vaccinations have occurred with the required minimum interval between vaccinations. The apps consistently display three items – the holder’s first and last names and the computed vaccination status (pass/fail/error) – but only Alberta and Ontario additionally display the holder’s date of birth and only British Columbia displays the signature date of the Proof. Date of birth, along with the holder’s name, is useful for comparison to government-issued photo ID to verify that the presented credential belongs to the holder. It is unclear what the purpose is in displaying the date that the proof was issued.

An additional concern around Verifier Apps, too, is their accessibility, particularly in regards to the operating system (OS) versions that support them. The minimum OS versions for iOS/iPadOS range from 8.0+ (NS, PE) to 13.0+ (AB), with most requiring iOS/iPadOS 11.0+ (BC, NL, ON, QC, SK, YT) and one requiring iOS/iPadOS 12.0+ (MB). The minimum OS versions for Android devices range from 5.0+ (MB, ON) to 8.1+ (BC, NL, YT), with most requiring Android 8.0+ (AB, QC, SK) and two requiring Android 6.0+ (NS, PE). The requirement for iOS 13 or greater for the Alberta Verifier App necessarily reduces its accessibility to iPhone 6S and later models and to 5th generation iPads, 2nd generation iPad Airs, 4th generation iPad Minis, and iPad Pros.

For ease of reference, the currently available Verifier Apps are listed here by province and territory:

For ease of reference, the currently available Verifier Apps are listed here by province and territory:

Ontario’s Proof of Vaccination and Verifier App

As the authors of this post included Ontarians with valid claim to Proofs, we were able to look closer at the Proof collection process and the iOS and Android applications for its verification. We will also discuss the process of decoding SMART-based Proofs, covered only in brief above. The analysis below goes into a few technical details; if you’re more interested in the results themselves, jump to the Conclusion and Future Directions sections at the end.

Collecting an Ontario Proof

In addition to examining the requirements for verifying one’s identity to claim a Proof, we looked at the traffic generated during the collection process. Again, as this is a governmental site for which we have no active testing permissions, modifying proxied data during the collection process was not within scope of this research.

By navigating to the https://covid19.ontariohealth.ca website to collect a proof, a GET request to the root of that domain retrieves the main COVID-19 vaccination portal page and sets a covid-portal cookie, the content of which is encrypted. An X-Azure-Ref header was also noted in the response, indicating a service running in Microsoft Azure. At this point, Google tracking pixel or Google Analytics data is also sent to the stats.g.doubleclick.net domain, followed by a GET request to http://www.gstatic.com for a HTTP code 204 contentless response. 

After confirming one has read and agreed to the terms of use to download an enhanced vaccine certificate (or book a vaccine appointment), the site makes a POST to the covid19.ontariohealth.ca domain’s /app-home endpoint including a CSRF token and a viewId parameter in the request’s body, along with an empty accountId parameter and an acceptedTerm1 parameter set to True. This viewId is referenced throughout the Proof collection process, but is not fixed for a particular user – only for the session. In response, the /app-identity endpoint returns the same covid-portal cookie as previously noted along with ASLBSA and ASLBSACORS cookies, further indicating the use of services (load balancing in this case) in Azure. The site then redirects to the /app-identity endpoint and provides the viewId parameter in the URL.

The resulting GET request to the covid19.ontariohealth.ca domain’s /app-identity endpoint results in another redirection, this time to the Ontario Ministry of Health’s ontariomoh.queue-it.net domain, a virtual waiting room platform to queue requests to the Ministry healthcare information servers. This, in turn, produces a GET request to the ontariomoh.queue-it.net domain with a series of parameters in the URL to identify the type of request that will be queued (c=ontariomoh e=covid19mohprod ver=v3-javascript-3.6.3 cver=101 man=mohprod l=CovidPortal) and includes the source of the request with its viewId (t=https%3A%2F%2Fcovid19.ontariohealth.ca%3A443%2Fapp-identity%3FviewId%3D73T382U4CWFP).

The response to the queue-it.net request sets a series of “Queue-it” cookies, of which the queueittoken is provided in the URL of a GET request once again to the covid19.ontariohealth.ca domain’s /app-identity endpoint. The response to this request sets a QueueITAccepted cookie value and redirects yet again to a GET request for the /app-identity endpoint, this time explicitly to port 443 of the webserver. With these cookies all set, the request returns the Proof collection page. 

At this stage, the user can enter the identity verification data discussed previously, and it is sent within the body of a POST request to the /app-identity endpoint along with the CRSF token and viewId as follows: 

            _csrf=0ZLNlCVO-3q_jRKx1lLReJzhXAcYduKLYoFg viewId=73T382U4CWFP hcn=1234-567-890 vcode=QQ scn=QQ3456789 dob=2000-01-01 postal=L6N+2F2

If the submitted data does not match a valid user (as in the fake credential data provided above), the response redirects to the /app-identity endpoint to repeat the identity verification data entry step. If the submitted data does match a valid user, the response redirects to the /app-menu endpoint, including the viewId parameter in the URL.

Following a successful submission of valid identity data, the response redirects to the ontariomoh.queue-it.net domain and the resulting GET request sets a Queue-it-token-v3 cookie, which will expire after 3 minutes. The site then makes a GET request to the /app-menu endpoint which includes the queueittoken cookie in the URL and the response returns the QueueITAccepted cookie before redirecting to /app-menu once again. This time, the GET request returns the page for collecting a Proof (or booking a vaccination). 

Once the user selects the option to collect their Proof, the site triggers a GET request to the covid19.ontariohealth.ca domain’s /vaccine-receipt endpoint. The response to this sets a new Issue Time and Hash value for the QueueITAccepted token and returns a page that displays the user’s vaccination history and provides a link to download the vaccine certificate Proof.

Clicking on the download option, sends a GET request to the covid19.ontariohealth.ca domain’s /vaccine-credential endpoint and specifies the format in a URL format parameter set to “shc”. The response to this request sends the Proof. 

Some minor tampering with the viewId parameter in the URL was attempted to confirm its security, and found that altering the viewId parameter before downloading the Proof certificate results in a return to the start of the whole collection process, while altering the viewId parameter after downloading the Proof certificate resulted in an error:

Sorry, something went wrong. You have tried too many times to download the certificate, and your access has been locked. Please try again after midnight.

The Ontario Proof

As discussed above, the vaccine passport is based on the SMART Health Card framework. The framework begins with JSON data containing the person’s name, date of birth, and vaccine details (such as type, date, and lot of vaccination). It is then minified and serialized using compact JSON Web Signature (JWS) Serialization (see here). Then it is digitally signed with the issuer’s cryptographic private key, in this case the Ontario Ministry of Health. 

The resulting JWS is URL-safe base64 encoded and converted to ASCII numerical data. All of the ASCII values are then shifted down by 45 to ensure that each of the characters is represented by a two-digit number pair and appended to the shc:/ prefix to indicate SMART Health Card data. To examine SMART QR-encoded data, we reversed that process in a Python script that accepts QR images and returns the SHC, JWS, and vaccination data.

The rest of the encoding process is simply taking the SHC data and creating a QR code from it. Since the QR code simply contains an SHC code, which can be easily decoded into the original JSON data, the JSON data is not private and should contain only the bare minimum of personal information. 

Verifying an Ontario Proof

The Verify Ontario app is a mobile application used to verify Proof of Vaccination QR codes created by the Ontario Ministry of Health and those of the other provinces and territories of Canada. Businesses and other establishments wanting or needing to verify vaccination status can download the Verifier App from the Apple App Store or the Google Play Store to scan the QR codes and see if they’re valid and meet the current vaccination requirements in Ontario. 

The Ontario government has also generously made the code to the app public (https://github.com/ongov/OpenVerify). The Verifier App contains a minimal few components: a QR scanner, an About and Settings page, and a response description page. The app doesn’t need to be connected to the Internet at all times in order to work. It only needs an Internet connection in order perform updates, particularly for the latest vaccination rules and cryptographic public keys. 

On start-up, the app sends compressed protobuf data (Google’s structured data serialisation format) out to the firebaselogging-pa.googleapis.com/v1/firelog/legacy/batchlog endpoint to initialise Google Firebase Analytics for this session. We see intermittent communications to Google’s app-measurement.com/a endpoint for Firebase event data transmission, also in the compressed protobuf format (see more on Google’s protobuf data below).

The app then makes GET requests to files.ontario.ca/apps/verify/ for two files – minimumVersion.json and verifyRulesetON.json. The request for minimumVersion.json returns the “minimumMandatoryVersion” information (currently 1.0) and an “effectiveDate” (currently empty). Unlike the Proof collection service, based in Azure, HTTP headers indicate this resonse is sent via AWS’ CloudFront content delivery network. The request for verifyRulesetON.json returns:

  • name, date, and terms of use information
  • ruleset for number and brand of vaccines and days since last dose to declare “Immunization completed”
  • cryptographic public keys for Ontario and other provinces and territories to verify Proofs from them
  • repeat of minimumMandatoryVersion (currently 1.0) and an effectiveDate (currently empty) data

At this point we sought to understand the possibility of a malicious attacker being able to conduct a Denial-of-Service attack on a business or event site via a Person-in-the-Middle interception of the Verifier Apps’ server communication on start-up to manipulate the signature public keys the app is receiving in the verifyRulesetON file. Starting up our own copy of the Verifier App on our device, we attempted an alteration to the copy of the Ontario public key our app used when that file is transmitted, but a valid Ontario QR code still verified successfully. It seems the developers of the app mitigated against this type of attack: any new rulesets pulled to the app must also pass a signature check against the public key hardcoded to the app before altering the Proof validation keys.

We were also interested to test the outcomes of presenting invalid Proofs to the app; that is, how does the Verifier App respond to unsigned, malformed, and manipulated data? To create test cases, we used the playbooks available here. To understand if a QR code can be manipulated or forged and accepted by the app, we generated four basic test cases:

  1. From a decoded valid Proof, create an unsigned version, re-encode it to SHC data, and make a QR from it.
  2. From a valid SHC string, add numbers to the beginning and/or the end, and make a QR from it.
  3. From a decoded valid Proof, change the date of birth, re-encode it to SHC data, and make a QR from it.
  4. From a decoded valid Proof, create a version signed with keys found in the testing folders of the open-source code repo, re-encode it to SHC data, and make a QR from it.

In each case, the app correctly failed to validate the QR code and displayed the yellow Error screen. If the data is signed properly and conforms to the vaccine verification rules found in verifyRulesetON.json, then we would expect to see it display the green Success response. If it isn’t signed properly, it will only give the yellow Error response. As limited QR samples were available and the issuer’s private keys were not, it is assumed that the red Fail response will be displayed if it was correctly signed but did not meet the vaccination rules. It appears that the app is verifying the QR codes bases on how they’re digitally signed, and in order to forge a valid QR code, an attacker would also need to acquire the issuer’s cryptographic private key. This means that it one cannot forge “valid” QR codes unless the provincial health ministry cryptographically signs them (or their cryptographic signing is itself compromised), making forgery of valid QR codes significantly harder than simply generating the OR code itself.

Finally, we looked at the privacy posture of the app, and as we noted earlier, once QR codes are scanned by the Verifier app, calls are made intermittently to Google’s app-measurement.com domain. First, a GET request is made to the /config/app/1:1007566906072:ios:c2fa5ab4f9dd1582c42dad endpoint (or its Android equivalent). The endpoint itself designates the OS’ version of the Verify Ontario app within Google Analytics. The URL also includes an app_instance_id parameter (unique to each user), along with gmp_version, runtime_version, and platform parameters. Once that connection is established, the app makes intermittent POST requests to the /a endpoint, with the compressed protobuf analytics data in the body.

The protobuf data can be decompressed and decoded. We used the tool Burp Suite to intercept data, and Burp has its own protobuf decoder. Other protobuf decoders also exist (such as https://github.com/nccgroup/blackboxprotobuf). We made use of a manual process which exports the POST requests in Burp to HTTP .trace files and extracts just the body (remove headers and newline between headers and data) to save as a separate file. This file can be gunzipped (`gunzip – < request_body_file > request_uncompressed.bin`), which should ignore any trailing garbage. The resulting data can be decoded using the protoc tool (`protoc –decode_raw < request_uncompressed.bin > sent_data`). 

Unfortunately, decoding the protobuf is not particularly helpful; the result is pretty cryptic on the whole. Serialising data allows for more efficient transmission, which is the purpose of compressed protobuf, and naturally Google knows what all of the data is intended to deserialise as. It is possible to clearly pick out the occasional item like iOS version and iPhone version and Linux epoch dates (see Figure 2 below – sample protobuf data), but most other items require some additional context. The Verify Ontario app’s privacy page claims that no personally-identifiable information is sent from the app, and that Google Analytics collects:

  •                 default Google Analytics data
  •                 number of scans completed
  •                 date and time of scans
  •                 number of positive, negative, and invalid scans
  • number of times:
    •            scanner is open for more than 20 seconds
    •            verified result screen is left inactive for 30 seconds
    •            users are prompted to connect to the internet 
    •            users are prompted to update their version of the app
    •            users click the link to the app store from the version update prompt
    •            flashlight button is clicked while in the app
    •            a link is clicked in the app
  • a “masked” IP Address
    •            only a portion of an IP address is collected
    •            used for geolocation of a user         

The analytics source code for the app and the interpretable parts of the protobuf data would seem to bear out this assertion.

A review of the Android app’s storage reveals that the app mainly writes files within the cache storage and the data directory. Overall, the app is storing Google Analytics data and no private encryption keys or Proofs were being stored within the physical device. 

The following are related directories that were discovered on the Android device:

  •             cacheDirectory           /data/data/ca.ontario.verify/cache
  •             codeCacheDirectory       /data/data/ca.ontario.verify/code_cache
  •             externalCacheDirectory   /storage/emulated/0/Android/data/ca.ontario.verify/cache
  •             filesDirectory           /data/data/ca.ontario.verify/files
  •             obbDir                   /storage/emulated/0/Android/obb/ca.ontario.verify
  •             packageCodePath          /data/app/ca.ontario.verify-1/base.apk

Within these directories the contents review was uneventful, with three of the directories being empty (codeCacheDirectory, externalCacheDirectory, obbDir). The other directories contained cached HTTP requests (cacheDirectory) and public keys for Firebase (filesDirectory). The surrounding data directories (filesDirectory and packageCodePath) contain the majority of the library and Google Analytics files: 

            /data/app/ca.ontario.verify-1

            Type       Last Modified            Read  Write  Hidden  Size       Name

  • File       2021-10-18 23:21:09 GMT  True  False  False   121.4 KiB  split_config.xhdpi.apk            
  • File       2021-10-18 23:21:12 GMT  True  False  False   24.4 KiB   split_config.fr.apk
  • File       2021-10-18 23:21:14 GMT  True  False  False   40.4 KiB   split_config.en.apk
  • File       2021-10-18 23:21:18 GMT  True  False  False   4.5 MiB    split_config.armeabi_v7a.apk
  • Directory  2021-10-18 23:21:19 GMT  True  False  False   4.0 KiB    lib
  • File       2021-10-18 23:21:07 GMT  True  False  False   6.8 MiB    base.apk

            /data/data/ca.ontario.verify

            Type       Last Modified            Read  Write  Hidden  Size     Name

  • Directory  2021-10-18 23:21:20 GMT  True  False  False   4.0 KiB  lib
  • Directory  2021-10-25 16:34:09 GMT  True  True   False   4.0 KiB  cache
  • Directory  2021-10-20 02:34:35 GMT  True  True   False   4.0 KiB  lib-3
  • Directory  2021-10-27 14:41:04 GMT  True  True   False   4.0 KiB  files
  • Directory  2021-10-20 02:34:35 GMT  True  True   False   4.0 KiB  lib-2
  • Directory  2021-10-20 02:34:35 GMT  True  True   False   4.0 KiB  lib-1
  • Directory  2021-10-28 17:19:29 GMT  True  True   False   4.0 KiB  shared_prefs
  • Directory  2021-10-20 02:34:35 GMT  True  True   False   4.0 KiB  lib-0
  • Directory  2021-10-20 02:34:35 GMT  True  True   False   4.0 KiB  lib-main
  • Directory  2021-10-20 02:35:01 GMT  True  True   False   4.0 KiB  databases
  • Directory  2021-10-28 16:46:28 GMT  True  True   False   4.0 KiB  app_webview
  • Directory  2021-10-20 02:35:01 GMT  True  True   False   4.0 KiB  app_textures
  • Directory  2021-10-28 16:32:49 GMT  True  True   False   4.0 KiB  code_cache

            /data/data/ca.ontario.verify/shared_prefs

            Type  Last Modified            Read  Write  Hidden  Size     Name

  • File  2021-10-29 15:44:47 GMT  True  True   False   920.0 B com.google.android.gms.measurement.prefs.xml
  • File  2021-10-27 14:41:03 GMT  True  True   False   188.0 B  FirebaseAppHeartBeat.xml
  • File  2021-10-20 02:35:01 GMT  True  True   False   127.0 B  WebViewChromiumPrefs.xml

A SQLite database was used to temporarily store Google Analytics Firebase data. 

            /data/data/ca.ontario.verify/database

            Type  Last Modified            Read  Write  Hidden  Size      Name

  • File  2021-10-28 17:19:55 GMT  True  True   False   36.0 KiB  RKStorage
  • File  2021-10-28 17:19:55 GMT  True  True   False   32.6 KiB  RKStorage-journal
  • File  2021-10-28 17:19:29 GMT  True  True   False   16.0 KiB  google_app_measurement_local.db
  • File  2021-10-28 17:19:29 GMT  True  True   False   8.5 KiB   google_app_measurement_local.db-journal

The following services, activities, and referenced files that were discovered while the app was running are also unconcerning:

            Services running

  • com.google.android.datatransport.runtime.backends.TransportBackendDiscovery
  • com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService
  • com.google.android.gms.auth.api.signin.RevocationBoundService
  • com.google.android.gms.measurement.AppMeasurementJobService
  • com.google.android.gms.measurement.AppMeasurementService
  • com.google.firebase.components.ComponentDiscoveryService
  • com.google.mlkit.common.internal.MlKitComponentDiscoveryService

            Files referenced during runtime

  • /data/data/ca.ontario.verify/files/generatefid.lock
  • /data/data/ca.ontario.verify/databases/google_app_measurement_local.db
  • /data/data/ca.ontario.verify/databases/google_app_measurement_local.db-journal
  • /dev/ashmem
  • /data/data/ca.ontario.verify/files/PersistedInstallation.W0RFRkFVT0MmRhZA.json
  • /data/data/ca.ontario.verify/databases
  • /data/data/ca.ontario.verify/shared_prefs/com.google.android.gms.measurement.prefs.xml
  • /data/app/ca.ontario.verify-1/split_config.xhdpi.apk

            Activities running

  • ca.ontario.verify.MainActivity
  • com.google.android.gms.auth.api.signin.internal.SignInHubActivity
  • com.google.android.gms.common.api.GoogleApiActivity
  • com.proyecto26.inappbrowser.ChromeTabsManagerActivity
  • com.zoontek.rnbootsplash.RNBootSplashActivity

Finally, examination of logging on the device using the logcat tool did not reveal any privacy concerns, either.

Conclusion

Based on our research conducted thus far, Canada’s provincial and territorial Proofs appear:

  • based in a sound standard that minimizes the inclusion of personal information
  • collected from online sources that securely verify the requestor’s identity
  • protected against tampering by strong cryptographic ciphers

The Verify Ontario application appears to:

  • reliably reject forged and malformed Proofs
  • manage data securely
  • avoid collecting unnecessary private data
  • only temporarily store and transmit non-private analytics data to Google

Future Directions

In order to provide information about these Proofs and Apps in a timely fashion – since we recognize their importance in public health measures that are intended to protect us all – we have necessarily limited our scope in this post. The use of the SMART Health Care standard for all Canadian Proofs substantially limits the inclusion of unnecessary information that risks users’ privacy. The Credential Apps that store those Proofs and the Verifier Apps of other provinces and territories, however, do warrant further security and privacy research. 

Several provinces currently offer a Credential App (MB, NL, QC, SK), and others may choose to join suit, and we would particularly like to know how the Proof is handled within device storage and where else the Proof may be sent, stored, or shared. If multiple Proof objects can be stored in the Credential App, how are they distinguished by the user? Will we see open-source code accessible for these apps?

Nearly all of the other provinces and territories have their own Verifier Apps, which is where we expect to see the greatest variability in form and function. In future research, we would like to examine them similarly to how we’ve examined the Ontario Verifier App, to understand what information is being sent, stored, and shared on the device and how the app handles unsigned, manipulated, and malformed data.