Verificare il Green Pass europeo
Da una settimana, il Green Pass (che dimostra l’immunità o comunque il non contagio dal Covid-19) è obbligatorio per entrare in buona parte degli edifici in tutta Italia. Molti gestori di attività che si svolgono al chiuso sono preoccupati di non riuscire a controllare i Green Pass del pubblico. Ma è davvero così difficile? In realtà, no. L’EU Digital COVID Certificate è infatti un certificato digitale, le cui specifiche sono pubblicamente disponibili proprio per consentire a chiunque di verificare la validità di uno di questi QR code.
Esistono delle app per smartphone che svolgono questa operazione, ma richiederebbero comunque un operatore che scansioni i vari QR code degli avventori di un locale, e non sempre questo è praticabile. Esiste, però, la possibilità di automatizzare tutto il procedimento, gestendo l’accesso all’edificio tramite un meccanismo come un tornello, o una porta azionabile elettronicamente, e un semplice programma in Python che possiamo scrivere in breve tempo. Utilizzando un computer dotato di pin GPIO come il RaspberryPi è possibile realizzare un sistema completamente automatico: si può usare una webcam per riconoscere il QRcode, un lettore di smartcard per confrontare i dati con quelli della Tessera Sanitaria, e un relay per attivare il tornello (o la porta) soltanto nel caso in cui il Green Pass risulti valido. Il ricorso alla Tessera Sanitaria è fondamentale perché altrimenti un utente potrebbe presentarsi alla porta d’ingresso con un QRcode appartenente a un’altra persona, magari fotografato e condiviso tra tanti utenti. La Tessera Sanitaria è invece una sola per ogni cittadino, quindi permette di identificare automaticamente le persone e assicurarsi che ogni accesso sia legittimo. Naturalmente si potrebbero utilizzare altri meccanismi, come la CIE o la Firma Digitale, ma la Tessera Sanitaria è l’unico ID digitale a disposizione di tutti i cittadini italiani. Il progetto che proponiamo si basa su un RaspberryPi2 o superiore, con RasperryOS Buster, una webcam USB, un lettore di smartcard USB, un altoparlante passivo con jack audio, e un eventuale modulo relay per far scattare l’apertura della porta.
Installare i requisiti
I requisiti di questo software sono parecchi, ma possiamo installarli con una serie di comandi. Da notare che ci serve lo script https://github.com/panzi/verify-ehc, e quindi dovremo anche installare tutti i suoi requisiti. Possiamo farlo con questa serie di comandi:
sudo apt-get install pcscd libpcsclite1 pcsc-tools libccid libnss3-tools opensc-pkcs11 sudo apt-get install swig -y sudo apt-get install python3-pyscard -y sudo apt-get install python3-opencv python3-sip libjasper-dev libatlas-base-dev -y sudo pip3 install opencv-contrib-python==4.1.0.25 sudo apt install libqtgui4 libqt4-test sudo apt install python3-pil sudo aptitude install python3-rpi.gpio sudo apt install python3-pip sudo apt-get install python-qrtools sudo pip3 install beepy scriptdir=$(dirname "$(readlink -f "$0")") sudo pip3 install -r $scriptdir/verify-ehc/requirements.txt sudo apt install python3-lxml
In poche parole, prima di tutto installiamo le varie librerie necessarie per leggere le smartcard, poi quelle necessarie per prelevare immagini dalla webcam e manipolare le immagini (utilizzeremo OpenCV e PIL). Poi serviranno anche le librerie per gestire i pin GPIO del Raspberry, in modo da attivare un relay, e quelle per decodificare i QRCode. Infine, la libreria per emettere suoni (beepy) e i vari requisiti di Verify EHC.
Riconoscere il QRCode
Per prima cosa, scriveremo il codice che ci serve per riconoscere il QRcode, cioè per ottenere il testo (che poi tradurremo in JSON). Qui è necessario un piccolo hack: al momento, la versione di Debian disponibile come RaspberryOs è Buster. Purtroppo questa versione è ormai molto vecchia, quindi non è possibile avere le ultime versioni dei pacchetti. E la libreria qrtool è disponibile solo per Python2 (si trova su apt come python-qrtools). Esistono ovviamente divese altre librerie, ma questa è quella che abbiamo notato essere più efficiente e semplice da utilizzare. Quindi per ora metteremo le sue poche righe di codice in uno script a parte, che verrà interpretato da Python2 invece che da Python3: in futuro, usando la nuova versione di Debian, sarà possibile usare questa libreria nella versione per Python3. Il codice è questo:
#!/usr/bin/python import sys myfile = "green_pass.png" if len(sys.argv) > 1: myfile = sys.argv[1] from qrtools import QR myQR = QR(filename = myfile) myQR.decode() print myQR.data
Il codice è estremamente semplice: lo script si aspetta in argomento il percorso di un file contenente l’immagine di un QRcode da interpretare. Con questo file si può costruire un oggetto QR, decodificabile con la funzione decode(). A questo punto la variabile .data contiene il testo che è stato riconosciuto nel QRCode, che possiamo restituire sullo standard output. E che andremo a leggere dal programma principale.
Le funzioni di servizio
Ora iniziamo a scrivere lo script principale, quello che si occuperà di svolgere tutto il processo di verifica sia del Green Pass che della Tessera Sanitaria. Cominciamo dall’importazione delle librerie:
#!/usr/bin/python3 from smartcard.System import readers import cv2 from PIL import Image import beepy as beep import subprocess import array import json import os import sys import time from datetime import datetime
Librerie extra sono sostanzialmente quelle a cui accennavamo per lo script di installazione delle dipendenze, poi servono alcune librerie standard di Python per la gestione del JSON, dei sottoprocessi e dell’orario.
relay = 17 cfgfile = os.path.abspath(os.path.dirname(sys.argv[0]))+"/green-pass.json" logfile = os.path.abspath(os.path.dirname(sys.argv[0]))+"/green-pass.log" try: import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(relay, GPIO.OUT) rpi = True except: rpi = False config = {} reader = None try: r = readers() if len(r) == 0: print("No card reader found") sys.exit() reader = r[0] except: print("Undefined error loading Smart Card Reader") pass
Continuiamo definendo alcuni oggetti che saranno utili per tutto lo script, e che quindi avranno valore globale. Per esempio, il percorso in cui trovare il file di configurazione, oppure quello in cui scrivere i log. Poi proviamo a importare le librerie per gestire i pin GPIO del RaspberryPi: saranno necessarie per attivare il relay, e quindi aprire automaticamente la porta o il tornello nel caso il Green Pass risulti valido. In realtà possiamo anche utilizzare lo script su un computer diverso dal Raspberry, e in quel caso non riusciremmo a importare le librerie dei GPIO. In questo caso impostiamo la variabile rpi a False, così sapremo che non ci troviamo su un Raspberry. Creiamo un dizionario vuoto per memorizzare la configurazione, e poi l’oggetto reader: questo rappresenterà il nostro punto di accesso al lettore di smartcard. Siccome dovrebbe essere possibile procedere anche senza il lettore, perché l’utente potrebbe decidere di usare solo la webcam per il riconoscimento del QR code e poi lasciare a un operatore l’identificazione della persona, se non riusciamo a trovare il lettore di Smart Card catturiamo l’eccezione e andiamo avanti comunque.
def getConfig(): global cfgfile global config text_file = open(cfgfile, "r") mytext = text_file.read() text_file.close() config = json.loads(mytext) def open_door(): global relay if rpi: GPIO.output(relay, GPIO.LOW) time.sleep(0.1) GPIO.output(relay, GPIO.HIGH) time.sleep(1) GPIO.output(relay, GPIO.LOW)
Definiamo due funzioni “di servizio”, non fondamentali ma utili per definire due procedure e non preoccuparsene più. La prima si occupa di leggere il file di configurazione, che sarà nel formato JSON, e memorizzare il contenuto in un dizionario. La seconda è quella che apre la porta facendo scattare il relay: ci serve il numero del pin GPIO da attivare, ma vogliamo anche assicurarci di essere davvero su un Raspberry, perché altrimenti non ci sono i GPIO e non dobbiamo fare nulla.
Decodificare il Green Pass
Iniziamo con le cose “serie”: lo script verify-ehc.py si occupa di decodificare la stringa del Green Pass (che è un testo codificato in Base45).
def checkGP_text(gpText): tmpfile = '/tmp/greenpass.json' os.system(os.path.abspath(os.path.dirname(sys.argv[0]))+"/verify-ehc/verify_ehc.py '"+gpText+"' > "+tmpfile) text_file = open(tmpfile, "r") myoutput = text_file.read() text_file.close() GPdata = {} GPpayload = "" payl = False
Lo chiamiamo direttamente con os.system, scrivendo l’output in un file. Poi leggiamo il file, memorizzandolo come testo in una variabile. Utilizziamo os.system perché il modulo subprocess ha difficoltà a leggere tutte le righe, dal momento che lo script scrive l’output a intervalli non regolari.
for line in myoutput.split("\n"): if "Is Expired" in line: if "False" in line: GPdata["expired"] = False else: GPdata["expired"] = True if "Signature Valid" in line: if "True" in line: GPdata["signature_valid"] = True else: GPdata["signature_valid"] = False if payl: GPpayload = GPpayload + line if "Payload" in line: payl = True GPdata["payload"] = json.loads(GPpayload.replace("\n", "").replace("\r", "")) return GPdata
L’output è diviso su più righe, e in realtà a noi interessano solo alcune. Nello specifico, ci interessa la riga Is Expired che, se presente, indica che il certificato era valido, ma ora è scaduto. E poi la riga Signature Valid, che è presente solo se la firma del certificato risulta corretta: questa indica che il certificato è stato generato da uno dei ministeri della salute dell’Unione Europea, e quindi possiamo considerarlo non contraffatto. Infine, cerchiamo la riga Payload, perché dopo di essa viene riportato l’intero contenuto del Green Pass vero e proprio, con i dati personali della persona. Questo payload è in formato JSON, quindi possiamo tranquillamente caricarlo in un dizionario, assicurandoci di prendere il testo e rimuovere gli invii a capo per evitare che il modulo json di Python possa avere difficoltà a interpretarlo.
Leggere la Tessera Sanitaria
Purtroppo non esiste una documentazione pratica per l’utilizzo delle informazioni presenti nella Tessera Sanitaria italiana, solo delle specifiche tecniche. Noi ci siamo basati sul lavoro di decodifica fatto alcuni anni fa da MMXForge.
def getTSdata(): global reader try: connection = reader.createConnection() connection.connect() except: return {} #Seleziona del MF #CLS 00, istruzione A4 (seleziona file), P1 = P2 = 0 (seleziona per ID), #Lc: 2, Data: 3F00 (id del MF) SELECT_MF = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00] data, sw1, sw2 = connection.transmit(SELECT_MF) #Seleziona del DF1...vedi sopra SELECT_DF1 = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x11, 0x00] data, sw1, sw2 = connection.transmit(SELECT_DF1) #Seleziona del file EF.Dati_personali... vedi sopra sopra SELECT_EF_PERS = [0x00, 0xA4, 0x00, 0x00, 0x02, 0x11, 0x02] data, sw1, sw2 = connection.transmit(SELECT_EF_PERS) #leggiamo i dati #CLS 00, istruzione B0 (leggi i dati binari contenuti nel file READ_BIN = [0x00, 0xB0, 0x00, 0x00, 0x00, 0x00] data, sw1, sw2 = connection.transmit(READ_BIN)
I dati su una tessera sanitaria sono memorizzati in un particolare filesystem, ed è possibile selezionare i file inviando una serie di comandi binari (che codifichiamo in esadecimale per leggiblità). La funzione per la lettura dei dati personali dalla tessera sanitaria deve quindi iniziare stabilendo una connessione con la smartcard e poi utilizzando quella connessione per inviare una serie di comandi.
Otteniamo come risposta una tupla di tre oggetti: il primo rappresenta i dati restituiti dalla smartcard, gli altri due eventuali codici per identificare errori. Se tutto va bene, sw1 e sw2 dovrebbero sempre contenere i valori 0x90 e 0x00 rispettivamente. Nel nostro caso non c’è bisogno di verificarli, perché siamo solo interessati a estrarre i dati dal file corretto, qualsiasi cosa vada storta indica che la tessera inserita non era corretta e possiamo considerare nulla l’identificazione.
stringa_dati_personali = array.array('B', data).tobytes() dimensione = int(stringa_dati_personali[0:6],16) dati_TS = {} prox_field_size = int(stringa_dati_personali[6:8], base=16) da = 8 a = da + prox_field_size if prox_field_size > 0: codice_emettitore = stringa_dati_personali[da:a] dati_TS["emettitore"] = str(codice_emettitore.decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], base=16) da=a a += prox_field_size if prox_field_size > 0: data_rilascio_tessera = stringa_dati_personali[da:a] dati_TS["rilascio"] = str(data_rilascio_tessera[0:2].decode("ascii"))+"/"+str(data_rilascio_tessera[2:4].decode("ascii"))+"/"+str(data_rilascio_tessera[-4:].decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: data_scadenza_tessera = stringa_dati_personali[da:a] dati_TS["scadenza"] = str(data_scadenza_tessera[0:2].decode("ascii"))+"/"+str(data_scadenza_tessera[2:4].decode("ascii"))+"/"+str(data_scadenza_tessera[-4:].decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: cognome = stringa_dati_personali[da:a] dati_TS["cognome"] = str(cognome.decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: nome = stringa_dati_personali[da:a] dati_TS["nome"] = str(nome.decode("ascii"))
A questo punto, la variabile data contiene tutti i dati dell’utente, ma come byte. Dobbiamo convertirla in stringa e poi estrarre i singoli dati. I dati sono codificati in modo abbastanza semplice: i primi due caratteri contengono il numero di byte che costituiscono il successivo dato, così sappiamo sempre esattamente quando leggere. Quindi dobbiamo leggere i primi due caratteri, trasformarli in un numero intero, e leggere quel numero di byte per estrapolare il codice dell’emettitore della tessera. Poi leggiamo i due caratteri successivi per conoscere il numero di byte da leggere per avere il cognome. Segue il nome dell’intestatario della tessera.
da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: data_nascita = stringa_dati_personali[da:a] dati_TS["nato"] = str(data_nascita[0:2].decode("ascii"))+"/"+str(data_nascita[2:4].decode("ascii"))+"/"+str(data_nascita[-4:].decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: sesso = stringa_dati_personali[da:a] dati_TS["sesso"] = str(sesso.decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: statura = stringa_dati_personali[da:a] dati_TS["statura"] = str(statura.decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: CF = stringa_dati_personali[da:a] dati_TS["CF"] = str(CF.decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: cittadinanza = stringa_dati_personali[da:a] dati_TS["cittadinanza"] = str(cittadinanza.decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: comune_nascita = stringa_dati_personali[da:a] dati_TS["comune_nascita"] = str(comune_nascita.decode("ascii")) da = a a +=2 prox_field_size = int(stringa_dati_personali[da:a], 16) da=a a += prox_field_size if prox_field_size > 0: stato_nascita = stringa_dati_personali[da:a] dati_TS["stato_nascita"] = str(stato_nascita.decode("ascii")) return dati_TS
Con la stessa logica possiamo continuare a leggere i dati personali dell’utente. Sono, in sequenza, sesso, statura, codice fiscale, cittadinanza, comune di nascita e stato di nascita (nel caso la persona non sia nata in Italia). Memorizziamo tutti questi dati in un dizionario, così sarà più facile accedere a quello che ci interessa.
Verificare se il certificato è valido
Iniziamo ora la funzione che ci permetterà di stabilire se il Green Pass sia valido.
def isCertValid(GPdata,TSdata): global config valid = True err = "" if GPdata["expired"]: valid = False err = "Il certificato è scaduto" if not GPdata["signature_valid"]: valid = False err = "Il certificato non è firmato da una autorità sanitaria"
I due oggetti che dobbiamo ricevere in argomento sono il dizionario con i dati del green pass e quello con i dati della tessera sanitaria. Possiamo considerare il green pass immediatamente non valido se dai suoi stessi dati risulta che sia scaduto (expired) o se la sua firma non risulti correttamente apposta da uno dei ministeri della salute europei (in questo caso signature_valid sarebbe False).
if len(TSdata) > 1: try: TSdob = TSdata["nato"].split("/")[2] + "-" + TSdata["nato"].split("/")[1] + "-" + TSdata["nato"].split("/")[0] except: TSdob = "" if GPdata["payload"]["nam"]["fn"].lower() != TSdata["cognome"].lower() or GPdata["payload"]["nam"]["gn"].lower() != TSdata["nome"].lower() or GPdata["payload"]["dob"].lower() != TSdob.lower(): valid = False err = "Il certificato non appartiene alla persona identificata dalla tessera sanitaria"
Se è stata fornita una Tessera Sanitaria valida, possiamo confrontare i suoi dati con quelli del Green Pass. Dobbiamo solo fare una piccola conversione sulla data di nascita, perché nella TS è memorizzata nel formato GG/MM/YYYY, mentre nel GP è memorizzata come AAAA-MM-GG. Poi possiamo confrontare data di nascita, nome, e cognome: li confrontiamo in minuscolo, per evitare problemi con eventuali lettere mauscole non corrispondenti.
else: valid = False err = "Certificato valido per "+str(GPdata["payload"]["nam"]["gn"])+ " "+GPdata["payload"]["nam"]["fn"]+" ma nessuna tessera sanitaria rilevata." if config["interactive"]: print(err+" Confermi che il certificato appartiene a questa persona?") ch = input() if "y" in ch.lower() or "s" in ch.lower(): valid = True return valid, err
Se non è stata fornita una tessera sanitaria, per esempio perché la persona non è un cittadino italiano, e la configurazione consente comunque all’operatore di verificare l’identità della persona, facciamo apparire un semplice prompt per chiedere proprio all’operatore se il Green Pass appartenga davvero alla persona che si è presentata. Se l’operatore preme i tasti y oppure s, il Green Pass è considerato legittimo, ma segnaliamo comunque che non era stata fornita una tessera sanitaria. Così nell’eventuale log viene indicato che l’identificazione è stata manuale.
Catturare il QRcode dalla webcam
Per catturare il QRcode creiamo una funzione che utilizza OpenCV, così è facile scattare foto in tempo reale dalla webcam.
def getQRfromCamera(): global config cap = cv2.VideoCapture(0) detector = cv2.QRCodeDetector() print("Waiting for QR code") while True: # get the image _, img = cap.read()
L’immagine verrà inserita nella variabile img.
#qrtools is not available for python3 on buster cv2.imwrite('/tmp/qrimage.png',img) process = subprocess.Popen([os.path.abspath(os.path.dirname(sys.argv[0]))+'/qrcodereader-py2.py', '/tmp/qrimage.png'], stdout=subprocess.PIPE) stdout = process.communicate()[0] data = stdout.decode('ascii') if len(data) > 0 and "NULL" not in data: break
Ora utilizziamo OpenCV per scrivere l’immagine su un file temporaneo (sempre lo stesso, tanto possiamo gestire un solo ingresso alla volta). Poi cerchiamo di tradurre questa immagine nel testo del GreenPass usando lo script qrcodereader. Non utilizziamo direttamente a funzione di lettura del QR code di OpenCV perché non funziona bene con webcam a bassa definizione. Se abbiamo ottenuto qualcosa, lo scriviamo nella variabile data.
# display the image preview only if we have Xorg available xorg = False if 'DISPLAY' in os.environ and config["screen"]: if os.environ['DISPLAY'] != None and os.environ['DISPLAY'] != "": xorg = True if xorg: cv2.imshow("code detector", img) if(cv2.waitKey(1) == ord("q")): break time.sleep(0.1) cap.release() cv2.destroyAllWindows() return data
Se è disponibile una sessione di Xorg, il server grafico di GNU-Linux, mostriamo una finestra con l’anteprima della foto scattata dalla webcam, così l’utente può capire se ha allineato correttamente il QR code. Chiaramente non possiamo farlo quando non c’è uno schermo. La funzione fa un ciclo continuo finché non viene riconosciuto un QRcode valido.
Mettere tutto assieme
Nella routine principale del programma possiamo riunire le varie funzioni che abbiamo scritto seguendo il filo logico della verifica del Green Pass: lettura del QRcode, lettura della tessera, confronto dei dati, responso all’utente sotto forma di segnale audio, apertura della porta, e eventuale messaggio sullo schermo.
getConfig() active = True while active: beep.beep(4) gpText = getQRfromCamera() print("QRcode:"+gpText) GPdata = checkGP_text(gpText) TSdata = getTSdata()
Nella routine principale del programma prima di tutto leggiamo la configurazione dall’apposito file. Poi attiviamo un ciclo continuo, che svolgerà le varie operazioni in sequenza: prima di tutto si riproduce un suono, per segnalare che siamo pronti a leggere un nuovo QRcode. Poi procediamo a avviare la funzione per la lettura delle immagini dalla webcam: quando questa avrà identificato e decodificato un QR code, potremo procedere a verificarne il contenuto. Fatto questo, andiamo a leggere l’eventuale Tessera Sanitaria presente nel lettore (se la tessera non è stata inserita, il dizionario risultante sarà vuoto).
val,err = isCertValid(GPdata,TSdata) msg = "" status = "ERROR" if val: msg = "OK: certificato valido e documento corrispondente per "+str(GPdata["payload"]["nam"]["gn"])+" "+str(GPdata["payload"]["nam"]["fn"])+", puoi entrare." status = "OK" beep.beep(5) open_door() else: msg = "ERRORE: "+err beep.beep(3) print(msg)
Ora abbiamo tutto quello che potrebbe servirci, quindi possiamo procedere alla verifica delle credenziali. Come abbiamo visto, la funzione isCertValid ci restituisce una tupla di due oggetti. Il primo è un semplice booleano, chiamato val, che sarà True se il Green Pass è valido e corrispondente alla Tessera Sanitaria e False negli altri casi. Mentre err è una stringa che contiene l’eventuale codice di errore ottenuto. Se il Green Pass è valido non soltanto lo segnaliamo con un suono, così è subito palese se l’accesso sia consentito oppure no, ma inneschaimo anche l’apertura della porta o del tornello con la funzione open_door.
if config["log"]: try: cf = TSdata["CF"] except: cf = "" logline = str(datetime.now()) + "," + status + "," + str(cf) +"," + msg with open(logfile, "a", encoding='utf-8') as myfile: myfile.write(logline+"\n") time.sleep(1)
Per finire, gestiamo il caso in cui la configurazione preveda di loggare i dati, per esempio per identificare. Ovviamente questa è una eventualità che richiede una certa cautela, perché si tratta di memorizzare dati privati sensibili delle persone, quindi non è detto che qualcuno voglia attivarla. Se il log è attivo, quindi, generiamo una riga di log costituita dal timestamp, lo stato della validità del green pass (OK oppure ERROR), l’eventuale codice fiscale (che però è una stringa vuota se non è stata fornita una Tessera Sanitaria), e l’eventuale messaggio di errore.
Prima di ripetere il ciclo attendiamo un secondo, per dare all’utente il tempo di togliere la propria tessera sanitaria e il QRcode dai lettori. Poi siamo pronti per un altro ciclo, verificando le credenziali di un’altro avventore..
Ciao e complimenti per la guida, la proverò speriamo mi funzioni .
volevo chiederti se collegassi invece che una web cam la PI camera , funzionerebbe?
Grazie
Grazie. In teoria puoi utilizzare la PiCamera, una webcam vale l’altra. Il problema è solo nella risoluzione: il Green Pass è un QR code molto dettagliato, quindi se non hai una webcam ad alta definizione rischi di non riuscire a distinguere nitidamente i vari punti, e la lettura del QR code non funziona. Mi pare he le PiCamera abbiano tutte una basse risoluzione (almeno, quella che ho io). Per leggere il Green Pass ho utilizzato una webcam FullHD, con quella risoluzione vai già sul sicuro.
Grazie per la risposta.
Ho provato a scaricare il repository direttamente dal terminale di raspbian con il git clone e poi ho eseguito il file install.sh.
Ha scaricato tutte le dipendenze e ha impostato il Raspberry all’esecuzione automatica dello script.
non ho collegato il lettore di smart card , solo ina PI. mi effettua solo la prima scansione e poi va in blocco , nel senso che rimane con schermo nero e devo spegnere il Raspberry. per uscire dall’escuzione del programma e capire cosa succede? oppure se non collego il lettore e volessi solo verificare il green pass ? le modifiche al file python le dovrei effettuare prima di eseguire il file install.sh? grazie mille.
Per fare modifiche e test al codice basta chiudere il processo python3 (anche col comando killall python3, tanto non dovrebbero esserci altri processi python). E poi lanciare lo script manualmente dall’interprete python (python3 ./green-pass-access.py), interrompendolo con il classico Ctrl+C.
Piccolo problema normativo, l’unico modo valido per verificare la validità del GreenPass per legge è l’app VerificaC19. Come giustifichi l’uso di questo sistema in caso di controlli?
Questo è falso. L’app Verifica C19 è l’opzione ufficiale, che giustamente il governo propone per chiunque non abbia una alternativa, essendo gratuita e a portata di tutti (o quasi, ci sono problemi su smartphone vecchi). Nulla però vieta di verificare il Green Pass con altro software, l’importante è seguire la procedura corretta e avere un set di certificati SSL aggiornato. Di fatto, la verifica di un Green Pass non è altro che la verifica di una firma digitale, cioè un processo standard riconosciuto in tutto il mondo e implementato in centinaia di applicazioni. Sono già in commercio delle soluzioni proprietarie che fanno la stessa cosa della mia soluzione Open Source: Ducati Home, per esempio, propone proprio un tornello pronto all’uso per la verifica del Green Pass, e non usa di certo l’app VerificaC19 (del resto, non è nemmeno basato su un sistema mobile).