JavaScript: Decodificare il codice QR del GreenPass

JavaScript: Decodificare il codice QR del GreenPass

javascriptingegneria

Le vacanze sono alle porte e grazie all'avanzamento delle vaccinazioni contro la pandemia da coronavirus si può finalmente riprendere a viaggiare. In particolare, a partire dal primo Luglio, sarà possibile viaggiare liberamente all'interno dei confini dell'Unione Europea grazie al rilascio dei cosiddetti "green pass".

Proprio in questi giorni milioni di italiani hanno iniziato a ricevere il messaggio da parte del Ministero della Salute che segnala la disponibilità del green pass all'interno dell'app IO o dell'app Immuni.

Ma cosa è contenuto nel codice QR che viene inviato agli utenti? Grazie alla pubblicazione di tutte le specifiche del pass vaccinale mi sono divertito a realizzare uno script JavaScript per decifrarne il contenuto.

Ma prima di spiegarti come ho fatto a leggere il codice QR del green pass mi presento: sono Lorenzo Millucci e sono un ingegnere del software che ama lavorare con Symfony e a cui piace condividere in questo blog le cose che impara. Non perderti anche il mio canale Telegram in cui ogni martedì pubblico la curiosità tecnologica che più mi ha colpito nella settimana!

Lettura del codice QR #

Per poter realizzare uno script di decodifica del codice QR del green pass la prima cosa da fare è preparare l'ambiente installando alcune dipendenze richieste:

npm install base45 cbor jpeg-js jsqr pako

A questo punto sei pronto per importarle nello script:

const base45 = require('base45');
const cbor = require('cbor');
const fs = require('fs')
const jpeg = require('jpeg-js');
const jsQR = require("jsqr");
const pako = require('pako');

Ora è possibile iniziare a decodificare il file vero e proprio contenente il green pass. Per quanto riguarda questo codice ho scaricato il file immagine direttamente dall'applicazione IO e l'ho salvato all'interno della cartella usando il nome greenpass.jpg. NOTA: se hai usato un nome differente o hai salvato il file in un'altra posizione aggiusta il codice di conseguenza

const greenpassJpeg = fs.readFileSync(__dirname + '/greenpass.jpg');
const greenpassImageData = jpeg.decode(greenpassJpeg, {useTArray: true});

NOTA 2: l'opzione useTArray passata al decoder serve per fare in modo che l'immagine venga decodificata come Uint8Array

Una volta fatto ciò puoi passare il file al decoder di codici QR

const decodedGreenpass = jsQR(greenpassImageData.data, greenpassImageData.width, greenpassImageData.height);

A questo punto la stringa che si ottiene è una cosa del tipo:

HC1:6BFOXM%TS3DHPVO13J /G-/2YKVA.R/K86PP2FC1J9M$DI9C3 [....] CS62GMVR +B1YM K5MJ1K:K:2JZLT6KM+DTVKPDUG$E7F06FA3O6I-VA6Y05C712G

Per procedere con lo script devi rimuovere i primi 4 caratteri della stringa (che indicano l'utilizzo del protocollo HCERT)

const greenpassBody = decodedGreenpass.data.substr(4);

A questo punto, per poter avere i dati in formato leggibile, bisogna prima decodificarli dal formato Base45 e poi decomprimerli usando zlib

const decodedData = base45.decode(greenpassBody);
const output = pako.inflate(decodedData);

Siccome il certificato è criptato usando il formato COSE (CBOR Object Signing and Encryption) devi decodificarlo

const results = cbor.decodeAllSync(output);
[headers1, headers2, cbor_data, signature] = results[0].value;

All'interno del certificato sono contenute varie tipologie di dati utili per garantirne la validità ma la parte che contiene i dati dell'utente è quella contenuta nella variabile cbor_data

const greenpassData = cbor.decodeAllSync(cbor_data);

A questo punto, finalmente, è possibile stampare il JSON con i dati dell'utente:

console.log(JSON.stringify(greenpassData[0].get(-260).get(1), null, 2));

Ad esempio questo è il contenuto del mio green pass:

{
  "t": [
    {
      "sc": "2021-06-[]",
      "ma": "1606",
      "tt": "LP217198-3",
      "co": "IT",
      "tc": "Dott. [....]",
      "ci": "[....]",
      "is": "Ministero della Salute",
      "tg": "840539006",
      "tr": "26041[....]"
    }
  ],
  "nam": {
    "fnt": "MILLUCCI",
    "fn": "MILLUCCI",
    "gnt": "LORENZO",
    "gn": "LORENZO"
  },
  "ver": "1.0.0",
  "dob": "1992-08-10"
}

Dove:

  • sc indica data e ora del test
  • ma indica "Marketing Authorization Holder" che indica semplicemente l’ente che ha messo sul mercato il test
  • tt indica il tipo di test
  • tc indica il luogo dove è stato eseguito il test
  • ci il numero univoco del certificato (Unique Certificate Identifier o UVCI)
  • is l'ente che ha emesso il certificato
  • tg è il tipo di agente contro cui il vaccino agisce (al momento l'unico valore ammesso è 840539006 e cioè COVID-19)
  • tr è il risultato del test

Per avere tutti i dettagli su come leggere il file possono essere trovati nel JSON Schema ufficiale

NOTA: il mio pass vaccinale è stato ottenuto per aver eseguito il tampone rapido e quindi i dati in esso contenuti fanno riferimento ad un test antigenico. I dati contenuti in un green pass relativo ad un vaccino sono differenti.

Il codice completo dello script lo trovi qui


Se questo post ti è piaciuto e ti è stato utile ti invito ad iscriverti al mio canale Telegram. Se invece hai domande o vuoi lasciare un commento puoi contattarmi direttamente su Telegram o su Twitter. A presto!