Using signed QR codes to digitally transform paper reports

This post follows on from my introduction about the innovative approach taken by Lumina Learning to Augmenting its paper based psychometric reports with mobile apps.

In this post I share technical detail about how this can be achieved without relying on authenticating with an online database.


In learning environments personalised paper based reports are handed out to each participant. Once in their hands the participants can view their own report and restrict who can view their report by showing it to only who they trust. We now want to represent this report digitally with a mobile app. We want a nice way to embed dynamic content in a static PDF document or printed document and have it accessed only by the intended recipient, plus who they wish to share it with. Can we keep it this simple with a mobile app?

Most apps start with authentication over a network via email password login or federated login with social media. Next most apps then control sharing to others via complex invite controls requiring an internet connection. This is too much friction in this enviroment where previously access was granted by handing them paper and showing it to others with no further support to help access the content.

Our Approach

We randomly obscure and sign the report's data and then encode it as a QR code which is printed on the portrait. This allows the digital version of the report to be viewed simply by scanning and opening with the app. The report can then be compared with others by scanning someone else's by showing the report to someone else just as they would usually.

The key point is data is not stored elsewhere in a lookup table online like you might expect. The QR code literally contains the data not a pointer to it. This retains the same privacy controls as in the physical world. Unless it has been shared publicly - you can only obtain the data if you have the report - to stop someone from seeing it you just stop them from viewing the report and only share it with who you can trust.

Once these steps have been taken if a participant scans the QR code and opens the URL in a web browser they see the web version, if they have the app the URL automatically opens in the app using deep linking using a custom URL scheme, finally the preferred option: they scan the QR code with a reader baked into the app. This means participants can view the report on any device without an internet connection.

In the unlikely event that someone tampers with the data, or tries to make up new data not generated by us, the app will always reject it even if it matches the correct format thanks to the RSA signature.


Below are the steps needed to be taken during report generation to make this a reality.

Step 1. Each report is represented by data encoded into a URI path

Each report has its data serialised into a URI Path. The path lists data in key value pairs, this allows for future expansibility without breaking existing functionality

Example URI path, all scores are 100 in this case :)

/v/1/l/euo/100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100/eu/100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100/e/100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100/u/100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100/o/100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100100/n/James/n/Acres/g/0/age/23/lc/en-GB/cc/GB/p/1000/practitioner/1000/date/2014-06-18/location/Camberley/lat/51.338613/lon/-0.743219

Regex can be used to match URI path and extract the values cross-platform.

(\/v\/[0-9]+)?(\/l((\/(e|u|o|eu|euo))?(\/([0-9]{3}){24}(([0-9]{3}){8}(([0-9]{3}){4})?)?)){5}(\/n\/[\w\s\-]{50})+(\/g\/[0-9])?(\/age\/[0-9]{3})?(\/lc\/[\w-]+)?(\/cc\/[\w -]+)?)+((\/p\/[0-9]+)?(\/practitioner\/[0-9]+)?(\/date\/[0-9]{4}-[0-9]{2}-[0-9]{2})?(\/location\/[\w -]{50}(\/lat\/-?[0-9]{2}(\.[0-9]{8})?\/lon\/-?[0-9]{3}(\.[0-9]{8})?)?)?)*\/?

Step 2. Obscure it, Sign it, Encode it

a) Obscure it - AES Symmetric Obscuration

The URI is then randomly encrypted to obscure its content and allow the app to know the origin product. The encryption method is symmetric. This means the application uses a single pre-shared public key to both encrypt and decrypt the URI path. Anyone can create the URI, and it is randomly obscured to make the URI format unpredictable. Here each product can have its own distinct key so the source can be identified. There is also a random Initialisation Vector (IV) which is used to ensure the same URI does not encrypt to the same Obscured URI, this means the same key is never used twice and means it is impossible to tell if two obscured URIs are the same without decrypting the contents.

AES Symmetric Obscuration

b) Sign it - RSA Asymmetric Digital Signature

An RSA digital signature is appended to verify authenticity and integrity of data. The signing method is asymmetric and is used to digitally sign the obscured URI. Products can each have their own distinct private key and corresponding public key, the private keys are only known by our servers and the public keys are pre-shared to the app. To generate a signature the obscured WebSafeBase64 URI is hashed then encrypted using the private key on the server. The signature is then appended to the obscured URI and is decrypted by the app using the corresponding public key. The decrypted signature is then compared against a locally generated hash of the obscured WebSafeBase64 URI, if they match then it is verified as authentic - the signature verifies the report was generated by our servers as nobody else knows the private key used to generate the signature. By checking the hash is the same it proves the contents were unaltered.

RSA Asymmetric Digital Signature

Example Signature Verification

c) Encode it - Combine into an URL, then encode as a QR Code

Next the iv, obscured and signed data is combined into a https:// URL and also a custom URL :// scheme used for deep linking in the mobile apps.

Example URL

We encode the HTTPS URL in QR code form printed on existing reports. The QR code can be scanned by the report owner with standard QR code reader to view in web browser, or a the reader baked into the iOS and Android app to open directly in the app. Having the data in the URI means it is preserved forever even if the website is offline.

The serialised report is encoded as a QR code, a form which has high capacity storage in a small area, can be read quickly from any direction and has various levels of error correction suitable for printed portraits. QR codes also have the ability to be divided into multiple data areas which is useful for storing lots of information.

The data is literally all in the QR code ready to be brought to life via a mobile app.

James Acres QR

Step 3. What if they want to share it?

For some, they want to keep their data completely private. To do this all they need to do is keep their paper report private just how they would have in the past. But for the majority, we find they are keen to compare themselves to the rest of their team and get tips for communication, so we have made it simple to share to compare.

Offline

Those with the app first scan their own QR code when on a workshop, later they can then ask someone else to share their QR code with them so they can compare themselves to them. This all happens offline privately using only the data in the QR code and the free app downloaded before or during the workshop via the app stores.

Online

There are two downsides to using the above approach where the URL literally is the data when it comes to sharing publicly online.

  1. The URL is long which is not ideal for sharing
  2. Once the URL is shared the sharer cannot later revoke access to the data as they are the same thing by design

Taking the above into consideration one solution is to allow those who optionally wish to share their own report online publicly outside of the workshop setting to still do so after accepting a warning this will make their report's data publicly accessible via a short URL pointer to it.

The URL shortening service acts as a public pointer to the data (what we originally tried to avoid). The service creates a random short key, and allows the short URL to be disabled at a later date on request.

Some extra precautions are advised with our URL shortening solution. Unlike most short URL systems if you shorten the same URL more than once it will always randomly generate you a new short URL. This makes it hard to work out if a URL has been shortened before. We also used a longer key length than usual to make it hard to brute force.


The report is viewed digitally via a web based viewer, or deep linked with the iOS and Android App. Download the Lumina Splash App or View my Splash to see this in action.