Skip to content

Instantly share code, notes, and snippets.

@loreberti89
Last active November 29, 2021 15:07
Show Gist options
  • Save loreberti89/743bf8d5ab234f3b4f148adc3fe05f24 to your computer and use it in GitHub Desktop.
Save loreberti89/743bf8d5ab234f3b4f148adc3fe05f24 to your computer and use it in GitHub Desktop.
A NodeJs script wrapped for a green pass validation by translating from Verification c-19
{
"name": "ita_green_pass_validation",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
},
"dependencies": {
"axios": "^0.21.1",
"dcc-utils": "https://github.com/ministero-salute/dcc-utils",
"jsrsasign": "^10.4.0",
"jsrsasign-util": "^1.0.5",
"moment": "^2.29.1",
"node-inspect-extracted": "^1.0.8"
},
"devDependencies": {}
}
const { DCC, Rule} = require('dcc-utils');
const axios = require('axios').default;
var fs = require("fs");
const rs = require('jsrsasign');
const rsu = require('jsrsasign-util');
const moment = require('moment');
//const get from VERIFICA c-19
const APP_MIN_VERSION = "android";
const RECOVERY_CERT_START_DAY = "recovery_cert_start_day";
const RECOVERY_CERT_END_DAY = "recovery_cert_end_day";
const MOLECULAR_TEST_START_HOUR = "molecular_test_start_hours";
const MOLECULAR_TEST_END_HOUR = "molecular_test_end_hours";
const RAPID_TEST_START_HOUR = "rapid_test_start_hours";
const RAPID_TEST_END_HOUR = "rapid_test_end_hours";
const VACCINE_START_DAY_NOT_COMPLETE = "vaccine_start_day_not_complete";
const VACCINE_END_DAY_NOT_COMPLETE = "vaccine_end_day_not_complete";
const VACCINE_START_DAY_COMPLETE = "vaccine_start_day_complete";
const VACCINE_END_DAY_COMPLETE = "vaccine_end_day_complete";
const NOT_DETECTED = "260415000";
const NOT_VALID_YET= "Not valid yet";
const VALID= "Valid";
const NOT_VALID= "Not valid";
const NOT_GREEN_PASS= "Not a green pass";
const PARTIALLY_VALID = "Valid only in Italy";
const TEST_RAPID = "LP217198-3";
const TEST_MOLECULAR = "LP6464-4";
//values get from https://github.com/eu-digital-green-certificates/dgca-app-core-android/blob/b9ba5b3bc7b8f1c510a79d07bbaecae8a6edfd74/decoder/src/main/java/dgca/verifier/app/decoder/model/Test.kt
const DETECTED = "260373001";
Array.prototype.last = function() {
return this[this.length - 1];
}
/** tutte queste funzioni sono state prese da Verifica c-19 VerificationModel.kt */
function getVaccineStartDayNotComplete(rules, vaccineType){
return rules.find(it => {
return it.name == VACCINE_START_DAY_NOT_COMPLETE && it.type == vaccineType
})
}
function getVaccineEndDayNotComplete(rules, vaccineType){
return rules.find(it => {
return it.name == VACCINE_END_DAY_NOT_COMPLETE && it.type == vaccineType
})
}
function getVaccineStartDayComplete(rules, vaccineType){
return rules.find(it => {
return it.name == VACCINE_START_DAY_COMPLETE && it.type == vaccineType
})
}
function getVaccineEndDayComplete(rules, vaccineType){
return rules.find(it => {
return it.name == VACCINE_END_DAY_COMPLETE && it.type == vaccineType
})
}
function getRecoveryCertStartDay(rules, vaccineType) {
return rules.find(it => {
return it.name == RECOVERY_CERT_START_DAY
})
}
function getRecoveryCertEndDay(rules, vaccineType) {
return rules.find(it => {
return it.name == RECOVERY_CERT_END_DAY
})
}
function getRapidTestStartHour(rules) {
return rules.find(it => {
return it.name == RAPID_TEST_START_HOUR
})
}
function getRapidTestEndHour(rules) {
return rules.find(it => {
return it.name == RAPID_TEST_END_HOUR
})
}
function getMolecularTestStartHour(rules) {
return rules.find(it => {
return it.name == MOLECULAR_TEST_START_HOUR
})
}
function getMolecularTestEndHour(rules){
return rules.find(it => {
return it.name == MOLECULAR_TEST_END_HOUR
})
}
/* Function from VERIFICA-C19 */
function checkTests(rules, dcc, typeOfTest){
var obj = dcc.payload.t.last();
var message = "";
var result = false;
var now = moment();
if (obj.tr == DETECTED) {
message = NOT_VALID
}else{
try {
var typeOfTest = obj.tt;
//in app Verifica C-19 get data and parse in UTC and then parse in Local
var odtDateTimeOfCollection = moment.utc(obj.sc)
//ne devo creare due perché se poi faccio l'add, lui mi modifica la variabile.
var ldtDateTimeOfCollection = moment(odtDateTimeOfCollection).local()
var endDateLdt = ldtDateTimeOfCollection.clone();
var startDate;
var endDate;
//chekc types of Test, can be MOLECULAR or RAPID
if(typeOfTest == TEST_MOLECULAR){
startDate = ldtDateTimeOfCollection.add(parseInt(getMolecularTestStartHour(rules).value), 'hours')
endDate = endDateLdt.add(parseInt(getMolecularTestEndHour(rules).value), 'hours')
}else
if(typeOfTest == TEST_RAPID){
startDate = ldtDateTimeOfCollection.add(parseInt(getRapidTestStartHour(rules).value), 'hours')
endDate = endDateLdt.add(parseInt(getRapidTestEndHour(rules).value), 'hours')
}else{
result = false;
message = NOT_VALID
return {
result,
message
};
}
if(startDate.isAfter(now)){
result = false;
message = NOT_VALID_YET
}else if(now.isAfter(endDate)){
result = false;
message = NOT_VALID
}else{
result = true;
message = VALID;
}
} catch (e) {
result = false;
message = NOT_GREEN_PASS;
}
}
return {
result,
message
};
}
/* Function from VERIFICA-C19 */
function checkRecoveryStatements(dcc){
var obj = dcc.payload.r.last();
var now = moment();
var message = "";
var result = false;
try {
var startDate = moment(obj.df);
var endDate =moment(obj.du);
if(startDate.isAfter(now)){
message = NOT_VALID_YET;
}else if(now.isAfter(endDate)){
message = NOT_VALID;
}else{
result = true;
message = VALID;
};
} catch (e) {
result = false;
message = NOT_VALID;
}
return {
result,
message
};
}
/* Function from VERIFICA-C19 */
function validateRules(rules, dcc) {
var obj = dcc.payload.v.last();
var vaccineType = obj.mp;
var doseNumber = obj.dn;
var dateOfVaccination = obj.dt;
var totalSeriesOfDoses = obj.sd;
var rule = getVaccineEndDayComplete(rules, vaccineType);
var now = moment.now();
var message = "";
var result = false;
if(rule && rule != null){
try{
if(doseNumber < totalSeriesOfDoses){
var startDate = moment(dateOfVaccination).add(parseInt(getVaccineStartDayNotComplete(rules, vaccineType).value), 'days');
var endDate = moment(dateOfVaccination).add(parseInt(getVaccineEndDayNotComplete(rules, vaccineType).value), 'days');
if(startDate.isAfter(now)){
message = NOT_VALID_YET;
}else if(now.isAfter(endDate)){
message = NOT_VALID;
}else{
result = true;
message = PARTIALLY_VALID;
}
}else
if(doseNumber >= totalSeriesOfDoses){
var startDate = moment(dateOfVaccination).add(parseInt(getVaccineStartDayComplete(rules, vaccineType).value), 'days');
var endDate = moment(dateOfVaccination).add(parseInt(getVaccineEndDayComplete(rules, vaccineType).value), 'days');
if(startDate.isAfter(now)){
message = NOT_VALID_YET;
}else{
result = true;
message = VALID;
}
}else{
message = NOT_VALID
}
}catch (e){
message = NOT_VALID
}
}else{
message = NOT_VALID
}
return {
result,
message
};
}
function exitAndValid(message){
console.log(message);
process.exit(0);
}
const main = async function () {
try{
//first argument must be a "greenpass raw"
var code = process.argv.slice(2)[0];
//second arguments must be a json of certificates i get this certificates from https://get.dgc.gov.it/v1/dgc/signercertificate/update
//from example how get all see at https://github.com/ministero-salute/dcc-utils/issues/1
var certs = JSON.parse(fs.readFileSync(process.argv.slice(2)[1]));
//third arguments mustbe a rules that i get from https://get.dgc.gov.it/v1/dgc/settings
var rules = JSON.parse(fs.readFileSync(process.argv.slice(2)[2]));
//value sets was in https://github.com/ministero-salute/dcc-utils but for now I can't figured out how should I use this for italy and so, I use rules from https://get.dgc.gov.it/v1/dgc/settings and validate rules as VERIFICA C-19 that not use certLogic
// var valueSets = JSON.parse(fs.readFileSync(process.argv.slice(2)[3]));
var verified;
const dcc = await DCC.fromRaw(code);
let resp = null;
var certificateValid = false;
var messageError;
for(var i = 0; i < certs.length; i++){
try {
const verifier = rs.KEYUTIL.getKey(certs[i]).getPublicKeyXYHex();
verified = await dcc.checkSignature(verifier);
if(verified){
certificateValid = true;
try{
//the green pass can be different types
//if there is a V (vaccinated) I should check Vaccination with Rules, function get from VERIFICA c-19 name VerificationViewModel->checkVaccinations
if(dcc.payload.v){
const isValidRules = validateRules(rules, dcc)
if (isValidRules.result === false) {
certificateValid = false;
messageError = isValidRules.message;
break;
}else{
exitAndValid(isValidRules.message);
break;
}
}
//If there is a T (test) I should check if Test is valid, function get from VERIFICA c-19 name VerificationViewModel->checkTests
if(dcc.payload.t){
const isValidTest = checkTests(rules, dcc)
if (isValidTest.result === false) {
certificateValid = false;
messageError = isValidTest.message;
break;
}else{
exitAndValid(isValidTest.message);
break;
}
}
//If there is R (recovery) I should check recoveryStatement , function get from VERIFICA c-19 name VerificationViewModel->checkRecoveryStatements
if(dcc.payload.r){
const isValidStatement = checkRecoveryStatements(dcc)
if (isValidStatement.result === false) {
certificateValid = false;
messageError = isValidStatement.message;
break;
}else{
exitAndValid(isValidStatement.message);
break;
}
}
}catch (e){
console.log(e);
certificateValid = false;
messageError = e.message;
break;
}
}
}
catch(e) {
messageError = "This certificate has not a valid Signature";
}
}
}catch (e){
throw new Error(e.message)
process.exit(1);
return false;
}
try{
if(certificateValid == false){
console.log(messageError);
throw new Error(messageError)
}else{
process.exit(0)
};
}catch (e){
process.exit(1);
}
};
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment