NCommander, updated 🕥 2022-01-21 13:20:56

Vaksina COVID QR Validation Checker

NOTE: Under best-case scenarios, when combined with the national ID of some sort, a COVID QR checker can provide assurances that a given person is properly vaccinated. However, especially in certain areas with non-centralized issuing, it is possible to have cases where an individual will not have a valid digital record because it was split into multiple locations or registries. As such this application can only be used to streamline the best scenario, and not as a replacement for manual checking.

Vaksina is a general-purpose library intended to validate multiple COVID QR codes from multiple countries and issuers. It is intended to streamline and help make validation of codes from multiple sources easier for public venues and other events.

This was primarily written because at the time I started this project, there is still no comprehensive COVID checking platform in existence. While some exist for Canada's SMART Health Cards and the EU's Digital COVID Pass, no framework can handle them as a single source of truth.

Vaksina is the intended proof of concept to handle this and is intended to work with a REST-based API deployed on a venue, but maybe adopted for use in mobile applications and similar as a future goal, but the intent is implementation with a kiosk-like system that is used in combination with a national ID card since none of the COVID-19 QR codes are usable as an ID in and of themselves.

Version 0.1 Goals

Zero-Knowledge/Zero-Record Model

It is intended that no data submitted to Vaksina is stored beyond the time it takes to parse the record. The only external data loaded and kept are PKI information from a set of "known good" trusted sources, that is covered by the next bullet point.

Creation of Public Trust Store

The creation of a "known good" set of sign keys will be provided ala Mozilla store.

Creation of validation of SMART Health Card standard

There is no one specific standard for COVID-19 QR validation. Canada and Japan have standardized on SMART, as have several states, pharmacies, hospitals and more within the United States. While Vaksina is intended as a general COVID-19 credential verification library, the initial MVP will only handle SMART Health Cards. Support for additional standards such as the EU's Digital COVID Pass, the UK's NHS Pass, and others like the New York State Excelsior Pass will be added with time.

Implementation of VaccinePolicy Framework, to determine the status

Given the ongoing needs of various venues, and other changes, it is planned that a given VaccinePolicy can be coded for and changed. Vaksina is intended to have a "generic" frontend with current best practices but is meant to be easily customized without having to fork the library.

Basic Front End for testing purposes

For purposes of testing and validation, a simple REST frontend + javascript page for testing on a local install is intended as part of the MVP.

Full code coverage and unit tests

Because we don't want to have untested code paths.

Usecases That I Want To Handle

Reference implementation

To the extent possible, I was this to be a comprehensive reference for all known major vaccine cards. While these exist in multiple places, they're usually either tied to a browser-based framework or a given mobile platform. As such, it is difficult to repurpose for other needs or to be used for educational purposes relating to how information is stored on COVID Cards.

For people that want independent verification of what their cards contain, this is intended also to provide insight into an otherwise murky world, with hopefully well readable and understandable code that they run on their system.

Handle non-Android/iOS uses

In general, the only applications that appear to exist are iOS/Android-based as of right now. There are some nodejs implementations, but they don't work on universal cards. As such, it makes it very hard to use a QR code scanner as part of a larger application, such as a streamlined check-in registration. The original envisioned use case was streamlined registration for a conference, vs. dealing with either taking QR codes on faith, or manual validation of each and every card.

It should be noted, however, that COVID 19 vaccine cards are not valid without an external ID.

Allow interface/embedding in other applications easier

For instance, COVID-19 Checker BadgeLife(tm) would be an amazing thing to see.

Understanding the connection between objects

While seemingly simple on the surface, the relationship between a person, their immunizations, and a given card is non-obvious. As originally implemented, vaksina was coded to handle SMART Health Cards as a rule. In a general sense, this also means it needs to handle FHIR Patient/Immunization data, as that is how the internal data is represented.

A given COVID card can, at least by specification, have multiple people contained within, with differing immunization records, PCR results (not modelled as of yet), and/or meeting the criteria for a given set of rules. As such, data needs to modeled in such a way that a card can be decoded to a set of patients, and validation status is handled independent per patient on a per card basis.

This is not entirely intuitive, but it specifically handles various types of test results and more, which should be kept in mine when dealing with the interfaces.

Because Vaksina is intended store-no-data solution, identifiers given to objects are reused, and are only valid in the context of that object in and of itself. This is an intentional design decision to re-enforced that this information is not to be stored by a validator application.

Useful links

Issues

Possible solution to #16

opened on 2022-01-21 13:20:56 by Guiorgy

The result of running test.py:

json { "cards":{ "card0":{ "card_type":"smart_health_card", "issued_by":"https://spec.smarthealth.cards/examples/issuer", "persons":{ "person0":{ "name":[ "Jane C. Anyperson" ], "dob":"1961-01-20", "immunizations":[ { "vaccine_administered":"PFIZER_COMIRNATY", "date_given":"2021-01-01", "lot_number":"0000002" }, { "vaccine_administered":"PFIZER_COMIRNATY", "date_given":"2021-01-29", "lot_number":"0000008" } ] } } } }, "validations":[ { "validation_method":"osha_1910_501", "results":{ "Jane C. Anyperson":"success" } } ] }

Note:

In your example in #16 in results you refered to the person using their reference id (person0), however assuming several cards get passed to the validator, wouldn't this id be duplicated in every card? Since the id is assigned locally separately for each card:

```py class Card(object): def init(self): ... self.persons = {} self._person_count = 0

def add_person(self, card):
    person_key = "person" + str(self._person_count)
    self.persons[person_key] = card
    self._person_count = self._person_count + 1

```

Thus, for now I am using the persons name(s) instead. However, this of course has the potential for trouble if several people share the same card and name (parent and child for example).

Removed the need to have `_shc_parent_object` reference

opened on 2022-01-21 12:09:20 by Guiorgy None

Typos in code/comments, some cleanup and fixed parse_immunization_record?

opened on 2022-01-19 14:01:30 by Guiorgy
  • fixed typos in code
  • cleaned up some code (PyCharm linter errors/warnings)
  • fixed parse_immunization_record?

On the last one (parse_immunization_record), the comment inside it states:

It's possible that multiple vaccines can be given in single day. This isn't done for COVID per se, but because FHIR is a general purpose specification, we should handle this, especially if there are future multishot COVID vaccinations that are given at later point, because data structures are important

If I understand this correctly, the method should handle multiple immunization records, store them in an list and return them all. If that is the intended behaviour, then I fixed it. If not then this should be reverted, and the method refactored, since before it created a list of imunizations that was filled but never used, and the method only ever returned the last parsed immunization, and if the received dictionarry was empty/had no immunizations, the method would return a variable before initialized (so my PyCharm linter was freaking out on that :P)

Typos and small changes

opened on 2022-01-19 13:07:21 by Guiorgy
  • typos in docs
  • added some links (Alpine, Ubuntu Core and etc.) to the docs
  • typos in tests
  • fixed duplicate test names
  • changed file paths from relative to absolute. Otherwise, tests would raise FileNotFound in PyCharm (on Windows)

Implement Proper Key Management + Revocation

opened on 2022-01-07 08:59:46 by NCommander

So as of right now, Vaksina is using a very simplistic design which has a JSON file with all know keys from the VCI issuers list. This doesn't handle key revocation (which is a custom rolled thing in SHC and is semi-complex), but essentially, we need to do the following.

Each SHC has a specific signing key in the iss field, but that doesn't specifically denote who actually issued a given card, and that should be available in plain text if possible (this will also be true for other card types later) through the API. For fully offline operation, we need to be able to generate a datafile that has all the information in a single go, and then load it as needed.

In practice, the key management tool needs to do the following: - Download all known VCI key signers from (https://github.com/the-commons-project/vci-directory)'s metadata - The keys are specifically at VCI base path + /.well-known/jwks.json, and are represented as a JSON Web Keyset format - However, the CRL system is unique - Merge VCI metadata with the VCI signers in such a way that all key information is available in a single go - If CRL support is defined (aka crlRevision: 1 is present in the JWK object), then we need to do additional steps - CRLs are defined on a per key basis, and refer to the rid object on a given card. - We need to download `/.well-known/crls/kid.json to get a list of revoked keys - This file needs a validation check before incorporated in the dataset. - Have a local database of keys. As of last run, the key database is 210 kb JSON file. That may or may not be acceptable to load at library instance.

While we could dynamically fetch a pubkey for an unknown issuer, I question if that's really a door I want to open ...

The following tools need to be implemented: - Define a serialization type for the key signer database - Implement a tool to download the VCI list and serialize it - Tools to create a local test signing CA for development purposes

We need to test the following scenarios to make sure we're handling this properly - Download and validate a JWS claim for a given key (this is coded, but not unit tested) - Ensure expiration and NBF (not valid before) is handled - Check revocation status (example03 of SHC data is a revoked example) - Model the issuer somewhere in Card information properly

There's probably more I'm forgetting, but this is a relatively good baseline in which to start

Should the API specifically handle multiple validation tests at once? (Validation Data Modelling)

opened on 2022-01-07 08:22:01 by NCommander

While I was beginning to refactor code to handle vaccinationresults, I had a thought. Currently, in API.md, we define an example result as such:

json { "card_validation_status": "good", "card_decoded_content": { "card_type": "smart_health_card", "persons": [ { "names" : [ "John A. Person" ], "dob": "01-05-1950", "immunizations": [ { "vaccine": "MODERNA", "given_on": "01-01-2021", "lot_number": "1" }, { "vaccine": "MODERNA", "given_on": "01-29-2021", "lot_number": "20" } ] } ], "issued_by": "Example Issuer" }, "validation_error": "" }

However, this doesn't work properly because a validation result needs to handle a person, and it is legal per FIRS/SHC for multiple persons/patients to be within one dataset, and when I brought this up on SHC (https://github.com/smart-on-fhir/health-cards/issues/223), this seems to be by design.

As such, we refactored the card representation/results to look like this:

json { "card_type": "smart_health_card", "issued_by": "https://spec.smarthealth.cards/examples/issuer", "persons": { "person0": { "name": [ "John B. Anyperson" ], "dob": "1951-01-20", "immunizations": [ { "vaccine_administered": "MODERNA", "date_given": "2021-01-01", "lot_number": "0000001" }, { "vaccine_administered": "MODERNA", "date_given": "2021-01-29", "lot_number": "0000007" } ] } } }

with the expectation that the end result would look like this (abbreviated):

json { "cards": { "card0": { "persons": { "person0": { "person_object": "data goes here" }, } } }, "validation": { "person0": "good" } }

This seemed fine at the time, but I don't think its flexible enough. For one, it makes the assumption that a given vaksina instance only has a single validator, and well, I can envision user stories where I can see multiple validators may need to be run (i.e., someone having to be checked for multiple criteria for different locations).

I'm thinking we need to model this list so: json { "cards": { "card0": { "persons": { "person0": { "person_object": "data goes here" } } } }, "validations": [ { "validation_method": "osha2022", "results": { "person0": "good" } } ] }

This might be more a job for the API than the library itself, but I don't want to make this difficult to implement either. It may be worth having REST endpoints for different validations, but that doesn't handle cases where something is interfacing w/ the library directly. I'm undecided on how best to represent the validation results as of right now, or if I should handle it in the core library at all ...

NCommander (Michael Casadevall)

An endless seeker, who created Mixer's FTL protocol, founded @SoylentNews, and documents history as they see it

GitHub Repository