Data Specification for the Vaccination Credential.

Sethuramantv
11 min readMar 16, 2021

Purpose

Blog written by: Sethuraman T V, Rohan Sukumaran, Tony Rose

This document describes the data fields and format of the inputs to the PathCheck Vaccination Credential pre-printed card.

JSON-LD is a lightweight syntax to serialize Linked Data in JSON [RFC8259]. Its design allows existing JSON to be interpreted as Linked Data with minimal changes. JSON-LD is primarily intended to be a way to use Linked Data in Web-based programming environments, to build interoperable Web services, and to store Linked Data in JSON-based storage engines. Since JSON-LD is 100% compatible with JSON, the large number of JSON parsers and libraries available today can be reused. In addition to all the features, JSON provides, JSON-LD introduces.

The general JSON data format :

JSON Format

When data length is not at issue, this format is simple to read and parse. With the JSON format, the payload is organized in the following schema:

{

“type”: “”,

“version”: 0,

“data”: {},

“signature”: {}

}

JSON Data Ordering

In the JSON format, blocks and key/value pairs may occur in any order. For example, a JSON document with the DATA block after the SIGNATURE block is equivalent to a document with the SIGNATURE block after the DATA block. Similarly, the key/value pairs within a block may appear in any order.

Format of the SIGNATURE Block

The signature block contains the hexadecimal ECDSA signature digest of the prepared DATA block and a keyId referencing the database and public key used to verify the ECDSA signature. For signature verification, devices should maintain indexed local key-value stores of approved public keys in PEM format. In the example below, the public key used to verify the signature is “1a9” in the “cdc” (local key/value) store. The colon character (:) is used as a delimiter to separate the key-value store identifier and the key identifier.

Signature Fields

keyId: SHORTSTRING. a string describing the database and index of the public key to be used when verifying the cryptographic signature of the DATA block.

hex: SIGNATUREHEX. The hexadecimal SHA256 digest ECDSA signature of the DATA block, calculated according to the rules above.

The general data format of the signature

Signature Block (JSON fragment)

“signature”: {

“keyId”: “cdc:1a9”,

“hex”: “3045022100f239e3cc363a0ef44a72c3458ea69620375b0875c541b3e1e4973c0d14bd4b4b02205aec76cb66eafbd0ec232c55cd95248cfa80e01dfd71ecbce7afc3196cdf178d”

}

The general URI data format

When data length is a concern, this format may use fewer bytes than JSON. Keys should be compressed according to the URI Compression rules in this document. Values in the URI format are encoded per the standard using Percent Encoding.

Serializing Optional Fields on the URI format

Unfilled fields MUST be submitted as empty between slash (/) characters. Only add empty delimiters if there is data after. (e.g. given fields A (required), B (optional), C (optional) with values 1 2 3, the implementation MUST output 1/2/3, but A only MUST be 1, A and B MUST be 1/2, A and C MUST be 1//3.

URI Schema

With URI format, payload is organized according to the following URI schema:

cred:type:version:signatureHex.keyId?payload

The payload should be represented as a series of percent-encoded values delimited by the slash (/) character.

To reduce data size, URI payloads are serialized after each field has been encoded. Serialization order is defined in each QR payload specification and key names are omitted.

URI Example

cred:coupon:1:3046022100f82e28019428220d47be9b7dc9a50b4f0e6f9a6c95852a9272827cdbd8cb38d2022100b5d8738178cc1a12b590b25933857d967eb10178c5bbe045d132ec2513ddfa94.cdc:1a9?37/5000/San%20Francisco/1B/Teacher

Pseudo-Code describing assembly of the URI:

$payload ::= [$number, $total, $city, $phase, $indicator];

for ($i ::= 0; $i < length($payload); $i += 1) do

$upcasedValue ::= upcase($payload[$i]);

$encodedValue ::= percentEncode($payload[$i]);

$payload[$i] ::= $encodedValue;

end

$payloadString ::= join(“/”, $payload);

$signatureHex ::= ecdsaSign($payloadString);

$base ::= “cred:coupon:” + $version + “:” + $signatureHex + “.” + $keyId;

$upcasedBase ::= upcase($base);

$uri ::= $upcasedBase + “?” + $payloadString;

Printed Card Specification Version 1

Terms and Definitions

For the purposes of brevity, this document refers to the following terms which are defined as follows:

  1. COUPON: the COUPON QR code is designated to communicate eligibility for vaccination.
  2. BADGE: the BADGE QR code is designated to contain information about the vaccines received by the HOLDER.
  3. STATUS: the STATUS QR code is designated to contain information about the vaccination status of the HOLDER.
  4. PASSKEY: the PASSKEY QR code contains information that can be correlated with other identification carried by the HOLDER to authenticate them.
  5. HOLDER: The HOLDER is the party who has been or will be vaccinated, and is holding a pre-printed vaccination credential card.
  6. ISSUER: The ISSUER is the party that delivers the vaccine and credential to a HOLDER.

Data Types

This document will use the following terms to define data types.

  1. NUMERIC: The NUMERIC data type is a sequence of integers between 0 and 99999999.
  2. STRING: The STRING data type is a sequence of UTF-8 characters, up to 255 bytes.
  3. HASH: The HASH data type is a sequence of alphanumeric characters containing a SHA256 cryptographic hash. It is 64 characters long.
  4. BIRTHDATE: date of birth, in ISO 8601 (YYYYMMDD) Basic Notation.

Example: 20200201 is 1 February 2020.

  1. SHORTSTRING: a STRING that is limited to 8 bytes instead of 255.
  2. SHORTNUMERIC: a NUMERIC with a maximum value of 9.

Data Formats

Data represented in QR codes can be encoded in the following formats:

  1. JSON Format. When data length is not at issue, this format is simple to read and parse, and it is widely supported by programming environments.
  2. URI Format. When data length is a concern, this format may use fewer bytes than JSON. Values in the URI format are Percent Encoded.
    With URI format, data is organized according to the following URI schema:

cred:type:version:signatureHex@keyId?data

Example:

cred:coupon:1:3046022100f82e28019428220d47be9b7dc9a50b4f0e6f9a6c95852a9272827cdbd8cb38d2022100b5d8738178cc1a12b590b25933857d967eb10178c5bbe045d132ec2513ddfa94@cdc%3A1a9?number=37&total=5000&city=San%20Francisco&phase=1B&indicator=Teacher

Data

QR Code Specifications

All QR codes contain a data set and a cryptographic signature. The cryptographic signature is a SHA256 digest in hexadecimal form, calculated using the private ECDSA key of the ISSUER. The two blocks are designated the DATA block and the SIGNATURE block.

Data Ordering

In the JSON format, blocks and key/value pairs may occur in any order. For example, a JSON document with the DATA block after the SIGNATURE block is equivalent to a document with the SIGNATURE block after the DATA block. Similarly, the key/value pairs within a block may appear in any order.

Case Sensitivity

All fields (keys as well as values) are case-insensitive in both JSON and URI format. For clarity and ease of reading, examples in this document are given in mixed cases. When performing operations such as hash comparison, a case-insensitive comparison function must be used.

Signature and Hash Verification

Due to the Alphanumeric QR code character set, cryptographic signatures and hashes must be calculated against uppercased versions of the underlying data. Data to be used for hashes is serialized in the specified order. Signatures should be calculated against the actual data and order encoded to QR to permit signature verification. In some cases, Percent encoding is used to address QR code character set limitations. This encoding should be reversed before signature or hash verification.

Format of the SIGNATURE Block

The signature block contains the hexadecimal ECDSA signature digest of the prepared DATA block and a keyId referencing the database and public key used to verify the ECDSA signature. For signature verification, devices should maintain indexed local key-value stores of approved public keys in PEM format. In the example below, the public key used to verify the signature is “1a9” in the “cdc” (local key/value) store.

Example Signature Block (JSON fragment)

“signature”: {

“keyId”: “cdc:1a9”,

“hex”: “3046022100f82e28019428220d47be9b7dc9a50b4f0e6f9a6c95852a9272827cdbd8cb38d2022100b5d8738178cc1a12b590b25933857d967eb10178c5bbe045d132ec2513ddfa94”,

}

Coupon Specification

Fields:

  1. version: NUMERIC. The version of the specification defining the data communicated in this QR code.
  2. number: NUMERIC. The unique identifying number is assigned to this coupon.
  3. total: NUMERIC. The total number of coupons issued in the batch of coupons this one was issued from.
  4. city: STRING. The name of the city, town, or other local areas which designates vaccination eligibility and delivery schedule for the HOLDER.
  5. When the city name contains characters that cannot be encoded to QR, the city name may be Percent Encoded as part of QR Code generation. Readers must decode any substitutions prior to signature verification.
  6. phase: SHORTSTRING. The vaccination phase is assigned to the HOLDER.
  7. indicator: SHORTSTRING. an indication of the priority assignment for HOLDER, or “none” if there is no priority.

Hashing Rules:

When generating a passkey hash (for inclusion in the BADGE structure), the following rules must be followed to generate consistent results:

  1. The only elements to be serialized should be the ones in the DATA block.
  2. The elements must be concatenated in the following order:
  3. number
  4. total
  5. City
  6. Phase
  7. indicator
  8. The concatenation should be a UTF-8 string.
  9. The concatenation MUST be converted to uppercase prior to hashing.
  10. The elements MUST NOT be URL Encoded prior to hashing.
  11. The output must be in hexadecimal format.

Thus, the SHA256 hash of the data in the example below would be calculated as follows:

hash(“${number}${total}${city}${phase}${indicator}”) == hash(“37500BOSTON1BTEACHER”)

-> “710183e3780fed3c48fce4b38da83775a7c47e9961b4a7ee822628e8c190359e”

JSON example:

{

“type”: “coupon”,

“version”: 1,

“data”: {

“number”: 37,

“total”: 5000,

“city”: “Boston”,

“phase”: “1B”,

“indicator”: “Teacher”

},

“signature”: {

“keyId”: “cdc:1a9”,

“hex”: “3046022100f82e28019428220d47be9b7dc9a50b4f0e6f9a6c95852a9272827cdbd8cb38d2022100b5d8738178cc1a12b590b25933857d967eb10178c5bbe045d132ec2513ddfa94”,

}

}

Badge Specification

Fields:

  1. version: NUMERIC. The version of the specification defining the data communicated in this QR code.
  2. coupon: HASH. The cryptographic hash of the data in the coupon, as defined in the Coupon specification.
  3. doseInfo: DOSEINFO. Information about the dose or doses received by the HOLDER.
  4. passkey: PathCheck Vaccination Credential
  5. JSON-LD is a lightweight syntax to serialize Linked Data in JSON [RFC8259]. Its design allows existing JSON to be interpreted as Linked Data with minimal changes. JSON-LD is primarily intended to be a way to use Linked Data in Web-based programming environments, to build interoperable Web services, and to store Linked Data in JSON-based storage engines. Since JSON-LD is 100% compatible with JSON, the large number of JSON parsers and libraries available today can be reused. In addition to all the features, JSON provides, JSON-LD introduces.

JSON Format

When data length is not at issue, this format is simple to read and parse. With the JSON format, the payload is organized in the following schema:

  1. {
  2. "type": "",
  3. "version": 0,
  4. "data": {},
  5. "signature": {}
  6. }

JSON Data Ordering

  1. In the JSON format, blocks and key/value pairs may occur in any order. For example, a JSON document with the DATA block after the SIGNATURE block is equivalent to a document with the SIGNATURE block after the DATA block. Similarly, the key/value pairs within a block may appear in any order.

Format of the SIGNATURE Block

  1. The signature block contains the hexadecimal ECDSA signature digest of the prepared DATA block and a keyId referencing the database and public key used to verify the ECDSA signature. For signature verification, devices should maintain indexed local key-value stores of approved public keys in PEM format. In the example below, the public key used to verify the signature is “1a9” in the “cdc” (local key/value) store. The colon character (:) is used as a delimiter to separate the key-value store identifier and the key identifier.

Signature Fields

  1. keyId: SHORTSTRING. a string describing the database and index of the public key to be used when verifying the cryptographic signature of the DATA block.
  2. hex: SIGNATUREHEX. The hexadecimal SHA256 digest ECDSA signature of the DATA block, calculated according to the rules above.

The general data format of the signature

Signature Block (JSON fragment)

  1. "signature": {
  2. "keyId": "cdc:1a9",
  3. "hex": "3045022100f239e3cc363a0ef44a72c3458ea69620375b0875c541b3e1e4973c0d14bd4b4b02205aec76cb66eafbd0ec232c55cd95248cfa80e01dfd71ecbce7afc3196cdf178d"
  4. }

The DOSEINFO structure. The DOSEINFO structure represents an array of DOSEs, delimited by the plus (“+”) character. Example of a DOSEINFO structure: “1 PFIZER 13a056+2 PFIZER 29a063”

A DOSE is a string containing a vaccine producer designation, a lot number, and a dose ID. Example: “1 PFIZER 13a056” indicates “Dose 1, from Pfizer, lot number 13a056.”

JSON example:

{

“type”: “badge”,

“version”: 1,

“data”: {

“coupon”: “710183e3780fed3c48fce4b38da83775a7c47e9961b4a7ee822628e8c190359e”,

“doseInfo”: “1 PFIZER 13a056+2 PFIZER 29a063”,

“passkey”: “e607c3b9b9448403a6b3cddd83f397bd17084c1db6fdeb081e9bd8392f21a1e6”

},

“signature”: {

“keyId”: “cdc:1a9”,

“hex”: “3046022100f82e28019428220d47be9b7dc9a50b4f0e6f9a6c95852a9272827cdbd8cb38d2022100b5d8738178cc1a12b590b25933857d967eb10178c5bbe045d132ec2513ddfa94”

}

}

Status Specification

Fields:

  1. version: NUMERIC. The version of the specification defining the data communicated in this QR code.
  2. vaccinated: SHORTNUMERIC. The vaccination status of the HOLDER. Currently designated values:
  • 0: The HOLDER has not received any vaccine.
  • 1: The HOLDER has received the first (of two) doses of a vaccine.
  • 2: The HOLDER has received the second (of two) doses of a vaccine.
  1. passkey: HASH. The cryptographic hash of the data in the Passkey, as defined by the Passkey Specification.

JSON example:

{

“type”: “status”,

“version”: 1,

“data”: {

“vaccinated”: 2,

“passkey”: “e607c3b9b9448403a6b3cddd83f397bd17084c1db6fdeb081e9bd8392f21a1e6”

},

“signature”: {

“keyId”: “cdc:1a9”,

“hex”: “3046022100f82e28019428220d47be9b7dc9a50b4f0e6f9a6c95852a9272827cdbd8cb38d2022100b5d8738178cc1a12b590b25933857d967eb10178c5bbe045d132ec2513ddfa94”

}

}

Passkey Specification

Fields:

  1. version: NUMERIC. The version of the specification defining the data communicated in this QR code.
  2. name: STRING. The full name of the HOLDER, to be used when authenticating the HOLDER.
  3. When the full name contains characters which cannot be encoded to QR, the name may be Percent Encoded as part of QR Code generation. Readers must decode any substitutions prior to signature verification.
  4. DoB: BIRTHDATE. The date of birth of the HOLDER, to be used when authenticating the HOLDER.
  5. salt: STRING. The cryptographic salt, nonce, or IV used for HASH calculation.

Hashing Rules:

When generating a passkey hash (for inclusion in the BADGE structure), the following rules must be followed to generate consistent results:

  1. The only elements to be serialized should be the ones in the DATA block.
  2. The elements must be concatenated in the following order:
  3. Name
  4. DoB
  5. Salt
  6. The concatenation should be a UTF-8 string.
  7. The concatenation MUST be converted to uppercase prior to hashing.
  8. The elements MUST NOT be URL encoded prior to hashing.
  9. The output must be in hexadecimal format.

Thus, the SHA256 hash of the data in the example below would be calculated as follows:

hash(“${name}${DoB}${salt}”) == hash(“JANE DOE190101011BC93AB4AXD3”)

-> “e607c3b9b9448403a6b3cddd83f397bd17084c1db6fdeb081e9bd8392f21a1e6”

JSON example:

{

“type”: “passkey”,

“version”: 1,

“data”: {

“name”: “Jane Doe”,

“DoB”: 19010101,

“salt”: “1Bc93ab4axd3”,

},

“signature”: {

“keyId”: “cdc:1a9”,

“hex”: “3046022100f82e28019428220d47be9b7dc9a50b4f0e6f9a6c95852a9272827cdbd8cb38d2022100b5d8738178cc1a12b590b25933857d967eb10178c5bbe045d132ec2513ddfa94”

}

}

Appendix A: FAQ

Q: Why does this specification support JSON and URI formats, but not XML?

A: While there are some benefits to XML for data interchange, we found it unsuitable for this purpose for the following reasons:

  1. XML-formatted messages are more verbose than JSON or URI, and space is at a premium in a QR code.
  2. Parsing XML tends to be slower and more memory-intensive than parsing JSON or URI.
  3. XML’s extensibility is not needed (or wanted) for this specification.

Q. Why does this specification use ECDSA over RSA?

A: While there are many benefits of RSA for encryption, we adopted ECDSA for the following reasons:

  1. ECDSA offers the same level of security with smaller key sizes.
  2. The encrypted message is a function of key size and data size for both RSA and ECDSA and since ECDSA key size is relatively smaller than RSA key size, thus encrypted message in ECDSA is smaller.
  3. The length of the public and private keys is much shorter in ECDSA. This results in faster processing times, and lower demands on memory(QR Codes) and bandwidth.
  4. It has easier hardware implementations and provides relatively faster operations in Key generation, signature generation, and signature verification

Q: Why does this specification target the Alphanumeric QR Code type?

A: The Alphanumeric QR Code type imposes significant limitations on the data which can be represented, but allows for the generation of a lower-resolution QR code. Smaller QR codes will be scannable with older hardware and lower-resolution scanners, and smaller data sets allow for more aggressive error correction. This promotes usability and equity.

Q: Which characters are supported by the QR Code Alphanumeric specification?

A: The following characters are supported by the specification. To manage data set size, lowercase characters should be converted to uppercase characters before QR generation. Characters in the following list should not be percent-encoded, as this increases bytes-per-character from one to three. Any characters not appearing in the following list MUST be percent-encoded before QR code generation.

0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

$ % * + — . / :

--

--