first working version

This commit is contained in:
Gianmarco Pettinato 2021-09-24 15:21:24 +02:00
parent d8a836aa90
commit 0c5d06cfc1
15 changed files with 3650 additions and 133 deletions

3294
cerificate_collection.json Normal file

File diff suppressed because it is too large Load Diff

119
package-lock.json generated
View File

@ -9,7 +9,9 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"axios": "^0.21.4",
"body-parser": "^1.19.0",
"dayjs": "^1.10.7",
"dcc-utils": "^0.2.0",
"express": "^4.17.1",
"jsrsasign": "^10.4.0",
@ -2533,6 +2535,19 @@
"node": ">=4.0.0"
}
},
"node_modules/@react-native-community/cli-tools/node_modules/node-fetch": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
"optional": true,
"peer": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/@react-native-community/cli-types": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-6.0.0.tgz",
@ -3653,6 +3668,14 @@
"node": ">= 4.5.0"
}
},
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/b64-lite": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/b64-lite/-/b64-lite-1.4.0.tgz",
@ -4720,9 +4743,7 @@
"node_modules/dayjs": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==",
"optional": true,
"peer": true
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
},
"node_modules/dcc-utils": {
"version": "0.2.0",
@ -6098,6 +6119,25 @@
"integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==",
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.14.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -8182,6 +8222,19 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/metro/node_modules/node-fetch": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
"optional": true,
"peer": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/metro/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -8400,19 +8453,6 @@
"node": ">=4.0.0"
}
},
"node_modules/node-fetch": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
"optional": true,
"peer": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/node-hkdf-sync": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-hkdf-sync/-/node-hkdf-sync-1.0.0.tgz",
@ -13961,6 +14001,16 @@
"integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==",
"optional": true,
"peer": true
},
"node-fetch": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
"optional": true,
"peer": true,
"requires": {
"whatwg-url": "^5.0.0"
}
}
}
},
@ -14723,6 +14773,14 @@
"optional": true,
"peer": true
},
"axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"requires": {
"follow-redirects": "^1.14.0"
}
},
"b64-lite": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/b64-lite/-/b64-lite-1.4.0.tgz",
@ -15569,9 +15627,7 @@
"dayjs": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==",
"optional": true,
"peer": true
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
},
"dcc-utils": {
"version": "0.2.0",
@ -16681,6 +16737,11 @@
"integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==",
"dev": true
},
"follow-redirects": {
"version": "1.14.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g=="
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -18073,6 +18134,16 @@
"graceful-fs": "^4.1.6"
}
},
"node-fetch": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
"optional": true,
"peer": true,
"requires": {
"whatwg-url": "^5.0.0"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -18559,16 +18630,6 @@
"optional": true,
"peer": true
},
"node-fetch": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
"integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
"optional": true,
"peer": true,
"requires": {
"whatwg-url": "^5.0.0"
}
},
"node-hkdf-sync": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-hkdf-sync/-/node-hkdf-sync-1.0.0.tgz",

View File

@ -9,7 +9,9 @@
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.21.4",
"body-parser": "^1.19.0",
"dayjs": "^1.10.7",
"dcc-utils": "^0.2.0",
"express": "^4.17.1",
"jsrsasign": "^10.4.0",

1
rules.json Normal file
View File

@ -0,0 +1 @@
{"rules":[{"name":"vaccine_end_day_complete","type":"EU/1/20/1525","value":"365"},{"name":"vaccine_start_day_complete","type":"EU/1/20/1525","value":"15"},{"name":"vaccine_end_day_not_complete","type":"EU/1/20/1525","value":"365"},{"name":"vaccine_start_day_not_complete","type":"EU/1/20/1525","value":"15"},{"name":"vaccine_end_day_complete","type":"EU/1/21/1529","value":"365"},{"name":"vaccine_start_day_complete","type":"EU/1/21/1529","value":"0"},{"name":"vaccine_end_day_not_complete","type":"EU/1/21/1529","value":"84"},{"name":"vaccine_start_day_not_complete","type":"EU/1/21/1529","value":"15"},{"name":"vaccine_end_day_complete","type":"EU/1/20/1507","value":"365"},{"name":"vaccine_start_day_complete","type":"EU/1/20/1507","value":"0"},{"name":"vaccine_end_day_not_complete","type":"EU/1/20/1507","value":"42"},{"name":"vaccine_start_day_not_complete","type":"EU/1/20/1507","value":"15"},{"name":"vaccine_end_day_complete","type":"EU/1/20/1528","value":"365"},{"name":"vaccine_start_day_complete","type":"EU/1/20/1528","value":"0"},{"name":"vaccine_end_day_not_complete","type":"EU/1/20/1528","value":"42"},{"name":"vaccine_start_day_not_complete","type":"EU/1/20/1528","value":"15"},{"name":"rapid_test_start_hours","type":"GENERIC","value":"0"},{"name":"rapid_test_end_hours","type":"GENERIC","value":"48"},{"name":"molecular_test_start_hours","type":"GENERIC","value":"0"},{"name":"molecular_test_end_hours","type":"GENERIC","value":"48"},{"name":"recovery_cert_start_day","type":"GENERIC","value":"0"},{"name":"recovery_cert_end_day","type":"GENERIC","value":"180"},{"name":"ios","type":"APP_MIN_VERSION","value":"1.1.0"},{"name":"android","type":"APP_MIN_VERSION","value":"1.1.0"}],"lastupdateDate":1632418898529}

View File

@ -1,10 +1,14 @@
import { Request, Response } from 'express';
import Verifier from '../../../utils/dgcVerifier/Verifier';
import Verifier from '../../utils/dgcVerifier/Verifier';
let verifier:Verifier;
Verifier.instanceVerifier().then((ver)=>verifier=ver).catch(console.error);
Verifier.instanceVerifier().then((ver:Verifier)=>verifier=ver).catch(console.error);
export const get = (req: Request, res: Response):void => {
const cert = req.body;
const result = verifier.checkCertificate(cert);
res.status(200).send({'data':result});
export const get = async (req: Request, res: Response):Promise<void> => {
const cert = req.body['key'];
try {
const result = await verifier.checkCertificate(cert);
res.status(200).send(result);
} catch (error) {
res.status(200).send({message:'unsigned certificate',error});
}
};

View File

@ -10,7 +10,7 @@ export default class GreenApi implements API {
this.router = router;
}
setupApi():void {
setupApi(): void {
this.router.post('/green',middleware.canGet,controller.get);
}
}

View File

@ -1,6 +1,5 @@
import { Request, Response, NextFunction } from 'express';
export const canGet = (req: Request, res: Response, next: NextFunction):void => {
return next();
};

View File

@ -6,8 +6,8 @@ export const setupApis = (application: Express):void => {
const exampleApi = new GreenApi(router);
exampleApi.setupApi();
application.use('/api', router);
console.log('inited');
};
export interface API {

View File

@ -1,12 +1,11 @@
import { setupApis } from './api';
import express from 'express';
import bodyParser from 'body-parser';
const app = express();
app.set('port', process.env.PORT || 3000);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
import './api';
app.use(express.json());
app.use(express.urlencoded());
setupApis(app);
export default app;

View File

@ -1,5 +1,4 @@
import app from './app';
const server = app.listen(app.get('port'), () => {
console.log(
'App is running at http://localhost:%d in %s mode',

View File

@ -1,30 +1,39 @@
import { response } from 'express';
import fs from 'fs/promises';
import axios, { AxiosResponse } from 'axios';
export class CertificateDownloader{
// static instance: CertificateDownloader;
private readonly baseUrl = 'https://get.dgc.gov.it';
private readonly updateApi = '/v1/dgc/signercertificate/update'
private readonly statusApi = '/v1/dgc/signercertificate/status'
private readonly keyStorage = './cerificate_collection.json';
private readonly timeSpan = 86400000;
private cerficateCollection:string[] = [];
private currentValidKids = [];
// private readonly timeSpan = 86400000;
private readonly timeSpan = 1;
private cerficateCollection:unknown = {};
private currentValidKids:string[] = [];
public constructor(){
this.getCertificates();
}
public async getCertificates(): Promise<string[]> {
public async getCertificates(): Promise<unknown> {
let data = '{}';
try {
const file = await fs.open(this.keyStorage,'r');
const data = await file.readFile();
const savedData = JSON.parse( data.toString() || '{}');
if(savedData.lastupdateDate == null || Date.now() - savedData?.lastupdateDate > this.timeSpan){
this.getAllCertificate()
.then(() => { console.log('could not read the certificates from the local file'); return this.cerficateCollection; })
.catch(console.error);
}
console.log('cerficates collection is valid loading from local source');
data = (await file.readFile()).toString('utf-8');
await file.close();
const savedData = JSON.parse( data || '{}');
// if(savedData.lastupdateDate == null || Date.now() - savedData?.lastupdateDate > this.timeSpan){
// await this.getAllCertificate();
// } else {
this.cerficateCollection = savedData.certificates;
// }
return this.cerficateCollection;
} catch (error) {
console.log(error);
if(error.errno == -2){
await fs.writeFile(this.keyStorage,'{}');
} else {
console.log(error);
}
}
}
// public static getCertificateDownloader():CertificateDownloader{
@ -34,45 +43,54 @@ export class CertificateDownloader{
// return CertificateDownloader.instance;
// }
// async getAllCertificate(): Promise<void> {
// this.cerficateCollection = {};
// const response = (await fetch('https://raw.githubusercontent.com/lovasoa/sanipasse/master/src/assets/Digital_Green_Certificate_Signing_Keys.json'));
// if(response.ok){
// this.cerficateCollection = await response.json();
// console.log(response);
// const lastupdateDate = Date.now();
// const file = await fs.open(this.keyStorage,'rw');
// file.writeFile(JSON.stringify({'certificates':this.cerficateCollection, lastupdateDate}));
// }else{
// throw new Error(response.statusText);
// }
// }
async getAllCertificate(): Promise<void> {
let exit = false;
let headers = {};
this.cerficateCollection = [];
while(!exit){
const response = await fetch(this.baseUrl+this.updateApi,{headers});
headers = {'X-RESUME-TOKEN': response.headers.get('X-RESUME-TOKEN')};
const currentKid:string = response.headers.get('X-KID') || '';
if(this.currentValidKids.includes(currentKid as never)){
const cert = await response.text();
this.cerficateCollection.push('-----BEGIN CERTIFICATE-----\n' + cert + '-----END CERTIFICATE-----');
}
exit = (response.status !== 200);
}
this.cerficateCollection = {};
const response:AxiosResponse<JSON> = (await axios.get('https://raw.githubusercontent.com/lovasoa/sanipasse/master/src/assets/Digital_Green_Certificate_Signing_Keys.json'));
if(response.status == 200){
console.log(response.data);
this.cerficateCollection = response.data;
console.log(response);
const lastupdateDate = Date.now();
const file = await fs.open(this.keyStorage,'rw');
const file = await fs.open(this.keyStorage,'w');
file.writeFile(JSON.stringify({'certificates':this.cerficateCollection, lastupdateDate}));
console.log(this.cerficateCollection);
await file.close();
}else{
throw new Error(response.statusText);
}
}
async updateKids(): Promise<void> {
try {
const resp = await fetch(this.baseUrl+this.statusApi);
this.currentValidKids = await resp.json();
} catch (error) {
console.log('could not get keyChild ', error);
}
}
// async getAllCertificate(): Promise<void> {
// let exit = false;
// let headers = {};
// this.cerficateCollection = [];
// while(!exit){
// // const response = await fetch(this.baseUrl+this.updateApi,{headers});
// const response:AxiosResponse = await axios.get(this.baseUrl+this.updateApi,{headers});
// // console.log(response.headers);
// headers = {'X-RESUME-TOKEN': response.headers['x-resume-token']};
// const currentKid:string = response.headers['x-kid'];
// if(this.currentValidKids.includes(currentKid)){
// // console.log('=========AGGIUNG===========');
// const cert = `-----BEGIN CERTIFICATE-----${response.data}-----END CERTIFICATE-----`;
// // console.log(cert);
// this.cerficateCollection.push(cert);
// }
// exit = (response.status !== 200);
// }
// const lastupdateDate = Date.now();
// const file = await fs.open(this.keyStorage,'w');
// file.writeFile(JSON.stringify({'certificates':this.cerficateCollection, lastupdateDate}));
// console.log(this.cerficateCollection);
// await file.close();
// }
// async updateKids(): Promise<void> {
// try {
// const resp = await axios.get(this.baseUrl+this.statusApi);
// this.currentValidKids = await resp.data as string[];
// } catch (error) {
// console.log('could not get keyChild ', error);
// }
// }
}

View File

@ -1,34 +1,49 @@
import fs from 'fs/promises';
import axios, { AxiosResponse } from 'axios';
export class RuleDownloader {
static instance: RuleDownloader;
private readonly baseUrl = 'https://get.dgc.gov.it';
private readonly timeSpan = 86400000;
private readonly keyStorage = 'rules'
// private readonly timeSpan = 1;
private readonly keyStorage = 'rules.json'
// private readonly timeSpan = 1000;
public rules:unknown = {}
public rules:unknown[] = []
// eslint-disable-next-line @typescript-eslint/no-empty-function
constructor(){
this.getRules();
}
constructor(){}
public getRules(): unknown {
const savedData = JSON.parse(localStorage.getItem(this.keyStorage) || '{}');
public async getRules(): Promise<unknown[]> {
let data = '{}';
try {
const file = await fs.open(this.keyStorage,'r');
data = (await file.readFile()).toString('utf-8');
await file.close();
const savedData = JSON.parse(data);
if(savedData.lastupdateDate == null || Date.now() - savedData?.lastupdateDate > this.timeSpan){
this.getSettings()
.then(() => { console.log('could not read the certificates from the local file'); return this.rules; })
.catch(console.error);
await this.getSettings();
} else {
this.rules = savedData.rules;
}
console.log('cerficates collection is valid loading from local source');
// console.log(dataRead.certificates);
this.rules = savedData.certificates;
return this.rules;
} catch (error) {
console.log(error);
if(error.code == 'ENONET'){
await fs.writeFile(this.keyStorage,'{}');
} else {
console.log(error);
}
}
}
private async getSettings(): Promise<unknown>{
const response = await fetch(`${this.baseUrl}/v1/dgc/settings`);
const jsonData = await response.json();
private async getSettings(): Promise<unknown[]>{
const response:AxiosResponse<unknown[]> = await axios.get(`${this.baseUrl}/v1/dgc/settings`);
const jsonData = response.data;
// this.rules = Rule.fromJSON(jsonData,{valueSets, validationClock:new Date().toISOString(),})
this.rules = jsonData;
localStorage.setItem(this.keyStorage, JSON.stringify({rules:this.rules,lastupdateDate:Date.now()}));
// localStorage.setItem(this.keyStorage, JSON.stringify({rules:this.rules,lastupdateDate:Date.now()}));
const file = await fs.open(this.keyStorage,'w');
file.writeFile(JSON.stringify({rules:this.rules,lastupdateDate:Date.now()}));
await file.close();
return jsonData;
}
}

View File

@ -0,0 +1,129 @@
import {DCC} from 'dcc-utils';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
interface checkResult {
valid: boolean;
message: string;
type: string;
}
export class VaccineVerifier {
private readonly vaccineStartDayNotComplete = 'vaccine_start_day_not_complete';
private readonly vaccineEndDayNotComplete = 'vaccine_end_day_not_complete';
private readonly vaccineStartDayComplete = 'vaccine_start_day_complete';
private readonly vaccineEndDayComplete = 'vaccine_end_day_complete';
private readonly recoveryCertStartDay = 'recovery_cert_start_day';
private readonly recoveryCertEndDay = 'recovery_cert_end_day';
private readonly rapidTestStartHour = 'rapid_test_start_hours';
private readonly rapidTestEndHour = 'rapid_test_end_hours';
private readonly molecularTestStartHour = 'molecular_test_start_hours';
private readonly molecularTestEndHour = 'molecular_test_end_hours';
private readonly positiveTest = '260373001';
private readonly negativeTest = '260415000';
private readonly rapidTest = 'LP217198-3'; // RAT, Rapid Antigen Test
private readonly molecularTest = 'LP6464-4'; // NAAT, Nucleic Acid Amplification Test
settings=[];
certTypes=['r','v','t',]
private checkVaccine = (payload:any):checkResult => {
const inoculationDate = dayjs(payload.dt);
const validRulesSet = this.getRulesSet(payload.mp);
const vaccineDiff = payload.sd - payload.dn;
const baseRuleIndex = validRulesSet.findIndex((elem:any)=>{ return elem.name == this.vaccineEndDayComplete;});
if( baseRuleIndex == -1)
return {valid:false, message:'Invaild set of rules check with operator', type:'vaccine'};
if(vaccineDiff <= 0){
return this.getLogicValidityDays(validRulesSet, this.vaccineStartDayComplete, this.vaccineEndDayComplete,inoculationDate);
} else {
return this.getLogicValidityDays(validRulesSet, this.vaccineStartDayNotComplete, this.vaccineEndDayNotComplete,inoculationDate);
}
}
private checkRecovery = (payload:any):checkResult => {
const now = dayjs();
const dateFrom = dayjs(payload.df);
const dateEnd = dayjs(payload.du);
if(now.isAfter(dateFrom) && now.isBefore(dateEnd)){
return{valid:true, message:'Certificate is valid', type:'recovery'};
}
return {valid:false, message:'toimplement', type:'recovery'};
}
private checkTest = (payload:any):checkResult => {
const validRulesSet = this.getRulesSet('GENERIC');
const testType = payload.tt;
if(payload.tr === this.positiveTest)
return {valid:false, message:'The test detected the virus',type:'test'};
const collectionDateTime = dayjs.tz(payload.sc,'UTC').tz(dayjs.tz.guess());
if(testType == this.rapidTest){
return this.getLogicValidityHours(validRulesSet,this.rapidTestStartHour,this.rapidTestEndHour,collectionDateTime);
}
if(testType == this.molecularTest){
return this.getLogicValidityHours(validRulesSet,this.molecularTestStartHour,this.molecularTestEndHour,collectionDateTime);
}
return {valid:false, message:'unknown test type',type:'test'};
}
private functionSelector = {
'v':this.checkVaccine,
'r':this.checkRecovery,
't':this.checkTest
}
constructor(settings:unknown[]) {
dayjs.extend(utc);
dayjs.extend(timezone);
this.settings = settings;
}
public checkCertifcate(pass:DCC):checkResult {
console.log(pass.payload);
const payloadAndType = this.getPayloadAndType(pass);
const result: checkResult = this.functionSelector[payloadAndType.key](payloadAndType.payload);
return result;
}
private getPayloadAndType(pass:DCC): {key:string,payload:unknown}{
const result:{key:string,payload:unknown} = {key:'',payload:[]};
this.certTypes.forEach((key:string) => {
if(pass.payload[key] != undefined){
if((pass.payload[key] as unknown[]).length != 0){
result.key =(key);
result.payload = (pass.payload[key][pass.payload[key].length -1]);
}
}
});
return result;
}
private getRulesSet(type:string): unknown[]{
return this.settings.filter((rule:unknown)=>{
return rule['type'] = type;
});
}
private getLogicValidityDays(validRulesSet:unknown[],startKey:string, endKey:string, inoculationDate: dayjs.Dayjs): checkResult {
const now = dayjs();
const ruleStart = validRulesSet.find((elem:any)=>{return elem.name == startKey;});
const ruleEnd = validRulesSet.find((elem:any)=>{return elem.name == endKey;});
const startValidity = inoculationDate.add(parseInt(ruleStart['value']),'days');
const endValidity = inoculationDate.add(parseInt(ruleEnd['value']),'days');
if(startValidity.isAfter(now)) return {valid:false, message:'Certificate not yet valid', type:'vaccine'};
if(now.isAfter(endValidity)) return {valid:false, message:'Certificate not more valid', type:'vaccine'};
return {valid:true, message:'Certificate is valid', type:'vaccine'};
}
private getLogicValidityHours(validRulesSet:unknown[],startKey:string, endKey:string, inoculationDate: dayjs.Dayjs): checkResult {
const now = dayjs();
const ruleStart = validRulesSet.find((elem:any)=>{return elem.name == startKey;});
const ruleEnd = validRulesSet.find((elem:any)=>{return elem.name == endKey;});
const startValidity = inoculationDate.add(parseInt(ruleStart['value']),'hours');
const endValidity = inoculationDate.add(parseInt(ruleEnd['value']),'hours');
if(startValidity.isAfter(now)) return {valid:false, message:'Certificate not yet valid', type:'test'};
if(now.isAfter(endValidity)) return {valid:false, message:'Certificate not more valid', type:'test'};
return {valid:true, message:'Certificate is valid', type:'test'};
}
}

View File

@ -1,14 +1,12 @@
import { CertificateDownloader } from './CertificateDownloader';
import { RuleDownloader } from './RuleDownloader';
import { VaccineVerifier } from './VaccineVerifier';
import {DCC} from 'dcc-utils';
import jsrsasign from 'jsrsasign';
export default class Verifier {
static instance: Verifier|undefined = undefined;
private certDownloader: CertificateDownloader;
private ruleDownloader: RuleDownloader;
private certificateList: string[] = [];
private constructor(){
this.certDownloader = new CertificateDownloader();
this.ruleDownloader = new RuleDownloader();
@ -17,21 +15,19 @@ export default class Verifier {
public static async instanceVerifier(): Promise<Verifier>{
if (Verifier.instance == undefined){
Verifier.instance = new Verifier();
Verifier.instance.certificateList = await Verifier.instance.certDownloader.getCertificates();
await Verifier.instance.certDownloader.getCertificates();
await Verifier.instance.ruleDownloader.getRules();
}
return Verifier.instance;
}
async checkCertificate(certificate:string): Promise<unknown>{
console.log(certificate);
const dcc = await DCC.fromRaw(certificate);
let result: unknown;
this.certificateList.forEach(async (cert: string) => {
const verifier = jsrsasign.KEYUTIL.getKey(cert);
if (typeof verifier == typeof jsrsasign.KJUR.crypto.ECDSA ){
const xyCoord = (verifier as jsrsasign.KJUR.crypto.ECDSA).getPublicKeyXYHex();
result = await dcc.checkSignature(xyCoord);
}
});
let result = await dcc.checkSignatureWithKeysList(await this.certDownloader.getCertificates());
const vaccineVerifier = new VaccineVerifier(await this.ruleDownloader.getRules());
result = {signature: result, valid:vaccineVerifier.checkCertifcate(dcc)};
console.log(result);
return result;
}

View File

@ -73,7 +73,7 @@
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */