From 48625efa93654a8fc12255c3f5b8162870477cc1 Mon Sep 17 00:00:00 2001 From: GG Date: Mon, 24 May 2021 17:38:28 -0400 Subject: [PATCH] Added JS version --- CovidQR-JS/covid-qr-decode.js | 62 ++++++++++++++++++++++ CovidQR-JS/package-lock.json | 98 +++++++++++++++++++++++++++++++++++ CovidQR-JS/package.json | 22 ++++++++ 3 files changed, 182 insertions(+) create mode 100755 CovidQR-JS/covid-qr-decode.js create mode 100644 CovidQR-JS/package-lock.json create mode 100644 CovidQR-JS/package.json diff --git a/CovidQR-JS/covid-qr-decode.js b/CovidQR-JS/covid-qr-decode.js new file mode 100755 index 0000000..8432252 --- /dev/null +++ b/CovidQR-JS/covid-qr-decode.js @@ -0,0 +1,62 @@ +#!/usr/bin/env node +// +// Author: Guillaume Gagnon +// Licence: Apache 2.0 +// +// Extract all payloads included in the Covid QR provided by the Quebec government (Preuve/passeport de vaccination) +// Note: The public key do not seems to be provided by the government at this point. +// Hence, it is not possible to validate QR authenticity at the time being. +// Public keys should be available here later on: +// https://covid19.quebec.ca/PreuveVaccinaleApi/issuer/.well-known/jwks.json + +// This script has been built using this very nice and detailed HOWTO: +// https://github.com/dvci/health-cards-walkthrough/blob/main/SMART%20Health%20Cards.ipynb +// +// Also, more info about the SMART Health Cards Framework can be found here: +// https://smarthealth.cards/ + + +const fs = require('fs'); +var jsQR = require('jsqr'); +var PNG = require('pngjs').PNG; +var jose = require('node-jose'); +var base64url = require("base64url"); +var zlib = require("zlib"); + + +// Extract RAW QR from picture +imageData = PNG.sync.read(fs.readFileSync('./QR.png')) +const scannedQR = jsQR(new Uint8ClampedArray(imageData.data.buffer), imageData.width, imageData.height) +console.log("RAW QR DATA:") +console.log(scannedQR.data) +console.log("") + + +// Extract JWS +const scannedJWS = scannedQR + .chunks + .filter(chunk => chunk.type === "numeric")[0] // Grab the numeric chunk + .text.match(/(..?)/g) // Split into groups of 2 numeric characters each of which represent a single JWS char + .map(num => String.fromCharCode(parseInt(num, 10) + 45)).join('') // Convert from numeric encoding to JWS +console.log("JWS DATA:") +console.log(scannedJWS) +console.log("") + + +// Extract JWS Header +JWSHeaders = base64url.decode(scannedJWS.split(".")[0]) +console.log("JWS HEAD:") +console.log(JWSHeaders) +console.log("") + + +// Extract payload +JWSPayload = scannedJWS.split(".")[1] +const payload = Buffer.from(JWSPayload, "base64"); +zlib.inflateRaw(payload, function (err, decompressedResult) { + scannedResult = decompressedResult.toString("utf8"); + console.log(scannedResult) + //const entries = JSON.parse(scannedResult) // Uncomment this bloc if you want to "beautify" the json output + // .vc.credentialSubject.fhirBundle.entry + // .map(entry => console.log(JSON.stringify(entry, null, 2))) +}); diff --git a/CovidQR-JS/package-lock.json b/CovidQR-JS/package-lock.json new file mode 100644 index 0000000..38c57a5 --- /dev/null +++ b/CovidQR-JS/package-lock.json @@ -0,0 +1,98 @@ +{ + "name": "covid-qr-decode", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "jsqr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz", + "integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, + "node-jose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.0.0.tgz", + "integrity": "sha512-j8zoFze1gijl8+DK/dSXXqX7+o2lMYv1XS+ptnXgGV/eloQaqq1YjNtieepbKs9jBS4WTnMOqyKSaQuunJzx0A==", + "requires": { + "base64url": "^3.0.1", + "buffer": "^5.5.0", + "es6-promise": "^4.2.8", + "lodash": "^4.17.15", + "long": "^4.0.0", + "node-forge": "^0.10.0", + "pako": "^1.0.11", + "process": "^0.11.10", + "uuid": "^3.3.3" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "zlib": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zlib/-/zlib-1.0.5.tgz", + "integrity": "sha1-bnyXL8NxxkWmr7A6sUdp3vEU/MA=" + } + } +} diff --git a/CovidQR-JS/package.json b/CovidQR-JS/package.json new file mode 100644 index 0000000..9a32f57 --- /dev/null +++ b/CovidQR-JS/package.json @@ -0,0 +1,22 @@ +{ + "name": "covid-qr-decode", + "version": "1.0.0", + "description": "", + "main": "covid-qr-decode.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "Guillaume Gagnon", + "license": "Apache-2.0", + "bin": { + "hello": "./covid-qr-decode.js" + }, + "dependencies": { + "base64url": "^3.0.1", + "jsqr": "^1.4.0", + "node-jose": "^2.0.0", + "pngjs": "^6.0.0", + "zlib": "^1.0.5" + } +}