Add correction
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Raffaele Mignone 2020-07-21 11:42:37 +02:00
parent 087a21eb07
commit 8fc33302e9
Signed by: norangebit
GPG Key ID: F5255658CB220573
1 changed files with 26 additions and 39 deletions

View File

@ -2,15 +2,15 @@
## Descrizione del protocollo
Il protocollo TCN [@TCNCoalitionTCN2020], Temporary Contact Number, è un protocollo di tracciamento dei contatti decentralizzato, ideato dalla TCN Coalition. Quest'ultima è una comunità di persone che durante l'emergenza COVID-19 hanno sviluppato questo supporto per lo sviluppo di applicazioni per notificare l'esposizione al contagio.
Il protocollo non richiede, informazioni personali ed è compatibile con autorità sanitarie.
I dispositivi degli utenti inviano un identificativo agli utenti vicini tramite Bluetooth e successivamente, se un utente è risultato positivo al contagio, può notificarlo ai suo contatti.
Il protocollo TCN [@TCNCoalitionTCN2020], Temporary Contact Number, è un protocollo di tracciamento dei contatti decentralizzato, ideato dalla TCN Coalition. Quest'ultima è una comunità di persone che durante l'emergenza COVID-19 hanno sviluppato questo protocollo per lo sviluppo di applicazioni per notificare l'esposizione al contagio.
Il protocollo non richiede informazioni personali ed è compatibile con autorità sanitarie.
I dispositivi degli utenti inviano un identificativo agli utenti vicini tramite Bluetooth e successivamente, se un utente è risultato positivo al contagio, può notificarlo ai suo contatti.
Per aumentare la scalabilità la TCN Coalition ha scelto di non generare randomicamente tcn, ma di generarli in modo deterministico da un seed.
In questo modo si riduce la dimensione del report da inviare, in quanto contiene solo le informazione necessarie per calcolare i tcn e non l'elenco intero di tcn.
L'utente può caricare diversi report di dimensioni minori in modo da non caricare l'intera cronologia dei contatti.
### Chiavi di autenticazione e verifica
### Chiavi di autorizzazione e verifica
L'user agent crea una chiave di autorizzazione *rak* (report authorization key) e una chiave di verifica *rvk* (report verification key) attraverso la curva ellittica *Ed25519*.
@ -26,7 +26,7 @@ dove `H_tck` è una funzione di hash con 256 bit di output che utilizza come sep
Ogni report può contenere al massimo $2^{16}$ chiavi di contatto.
Partendo un tcn se ne può derivare il prossimo tcn come mostrato di seguito:
Partendo da un tcn può essere ricavato il prossimo come mostrato di seguito:
```
tck_i ← H_tck(rvk || tck_{i-1})
@ -36,7 +36,7 @@ dove `||` indica una concatenazione.
### Numeri temporanei di contatto {#sec:tcn}
Per ogni tck viene generato un numero di contatto temporaneo come mostrato di seguito:
Per ogni tck viene generato un unico numero di contatto temporaneo (*tcn*) come mostrato di seguito:
```
tcn_i ← H_tcn(le_u16(i) || tck_i)
@ -47,7 +47,7 @@ Dalla chiave rak è possibile generare la chiave rvk e la chiave tck_0, dalle qu
### Report {#sec:report}
Quando viene richiesto di caricare i dati dell'utente, è possibile eseguire l'upload di un report compatto. Infatti grazie alla derivazione deterministica, è possibile inviare tutti gli identificativi utilizzati nel periodo `j1` e `j2`, caricando unicamente la rvk, tck_{j-1}, e i due indici `j1` e `j2`.
Quando viene richiesto di caricare i dati dell'utente, è possibile eseguire l'upload di un report compatto. Infatti grazie alla derivazione deterministica, è possibile inviare tutti gli identificativi utilizzati nel periodo `j1` e `j2`, caricando unicamente la `rvk`, `tck_{j1-1}`, e i due indici `j1` e `j2`.
Di seguito è illustrato come costruire il report:
```
@ -57,7 +57,7 @@ report ← rvk || tck_{j1-1} || le_u16(j1) || le_u16(j2) || memo
dove `memo` è una stringa di byte di lunghezza variabile che va da 2 a 257 byte.
Inoltre si utilizza il rak per produrre una firma `sig` per il report e verrà inviata al server la concatenazione di sig e report.
Poiché la rvk è contenuta all'interno del report, chiunque può verificare l'integrità del report e ricavare i vari.
Poiché la rvk è contenuta all'interno del report, chiunque può verificare l'integrità del report e ricavare i vari tcn.
## Implementazione del protocollo per la JMV
@ -67,7 +67,7 @@ Le chiavi private e pubbliche prodotte sono state *wrappate* rispettivamente nel
Questa scelta non solo ha permesso di utilizzare nomi dal maggiore significato rispetto al dominio applicativo, ma anche di nascondere l'implementazione della curva *Ed25519* in modo da disaccoppiare l'interfaccia della libreria crittografica da quella utilizzata per il protocollo TCN.
Come illustrato nella @sec:tck, a partire dalla chiave di autorizzazione è possibile ricavare la `TemporaryContactKey` iniziale[^tck-0].
Ciò può essere fatto traverso la funzione `baseTemporaryContactKey()`, la cui implementazione è riportata nel @lst:tck-0.
Ciò può essere fatto attraverso la funzione `baseTemporaryContactKey()`, la cui implementazione è riportata nel @lst:tck-0.
Invece, partendo da una chiave di contatto generica, è possibile ricavare la successiva chiave di contatto attraverso il metodo `nextTemporaryContactKey()` riportato nel listato @lst:next-tck.
[^tck-0]: La chiave temporanea iniziale viene ricavata a partire dalla sola *rak* e da essa non è generato nessun numero temporaneo di contatto.
@ -187,7 +187,7 @@ Entrambi i protocolli ricavano gli identificativi (*rolling proximity identifier
I due protocolli differiscono nel numero di identificativi ricavati da ciascuna chiave; come visto nella @sec:tcn il protocollo TCN ricava un unico tcn per chiave, mentre l'altro protocollo permette di ricavare un numero imprecisato di identificativi.
Un'ulteriore differenza risiede nella derivazioni delle chiavi, che nel protocollo TCN è deterministica, mentre in quello di Apple e Google non è specificato.
Oltre al protocollo, le due società, hanno fornito agli sviluppatori un API che permettesse alle applicazioni di di utilizzare il protocollo descritto nel paragrafo precedente.
Oltre al protocollo, le due società, hanno fornito agli sviluppatori un'API che permettesse alle applicazioni di utilizzare il protocollo descritto nel paragrafo precedente.
Sfortunatamente l'API non ci avrebbe permesso di implementare tutte e tre le modalità di funzionamento, ma solo l'ultima che si basa su un matching locale.
Questa caratteristica, unita alle regole stringenti di utilizzo imposte della libreria[^regole-api-apple-google] ci ha condotto alla scelta di implementare le varie funzionalità in maniera indipendente.
@ -199,7 +199,7 @@ Dalla documentazione ufficiale non è ben chiaro se questi vincoli riguardino so
L'interazione tra l'hardware bluetooth del dispositivo e l'applicazione è stata gestita attraverso l'impiego della libreria *Android Beacon Library* [@AndroidBeaconLibrary] che permette di gestire più facilmente le operazioni con beacon bluetooth.
Inoltre per rendere l'applicazione più funzionale, e quindi garantirne il funzionamento anche in background o a schermo spento è stato utilizzato un *foreground service* [@ServicesOverview], che consente di mantenere in *primo piano* le operazioni di trasmissione e scansione anche quando l'applicazione non lo è.
Data la natura variegata di Android, le diverse implementazioni del sistema operativo adoperate dai vari produttori non si comportano sempre nello stesso modo, motivo per il quale alcuni dispositivi tenderanno a terminare, o mettere in pausa ugualmente l'applicazione[^dont-kill-my-app].
Potendo opera unicamente nello spazio utente non è stato possibile superare questi limiti.
Potendo operare unicamente nello spazio utente non è stato possibile superare questi limiti.
[^dont-kill-my-app]: Molti produttori Android per aumentare la durata della batteria dei propri dispositivi tendono a stoppare e ridurre le funzionalità delle applicazioni. Maggiori dettagli possono essere trovati al seguente link \url{https://dontkillmyapp.com}.
@ -214,7 +214,7 @@ Per questo motivo è stata sviluppata anche una classe `BluetoothApplication` ch
Il dispositivo dell'utente deve eseguire il broadcast di un beacon bluetooth contenete l'UUID identificativo.
Questa operazione è stata svolta attraverso la classe `BeaconTransmitter` messa a disposizione dalla *Android Beacon Library*.
Inoltre per la modalità di funzionamento *C* è stato necessario prevedere un meccanismo di rotazione delle chiavi.
Questa rotazione viene settata attraverso la funzione `rotateTCN()` che sfrutta un `Handler` per programmare la rotazione dell'UUID.
Questa rotazione viene settata attraverso la funzione `rotateTCN()` (@lst:rotate-tcn) che sfrutta un `Handler` per programmare la rotazione dell'UUID.
``` {.kotlin #lst:rotate-tcn caption="Codice necessario alla rotazione del tcn."}
private fun rotateTCN() {
@ -250,8 +250,8 @@ Anche in questo caso la scelta era limitata a poche alternative:
- LOW
- ULTRA_LOW
Com'è possibile dedurre anche dai nomi dei vari livelli, l'API non fornisce nessuna stima quantitativa[^dispositivi-non-omogenei], ma solo delle indicazioni qualitative delle intensità del segnale trasmesso.
L'individuazione del livello più adatto è stata svolta per via sperimentale utilizzando cinque dispositivi differenti.
Come è possibile dedurre anche dai nomi dei vari livelli, l'API non fornisce nessuna stima quantitativa[^dispositivi-non-omogenei], ma solo delle indicazioni qualitative delle intensità del segnale trasmesso.
L'individuazione del livello più adatto è stata svolta per via sperimentale utilizzando quattro dispositivi differenti[^dispositivi-usati].
I due livelli più alti sono stati immediatamente scartati in quanto permettevano di rilevare i beacon a distanze elevate cosa che avrebbe minato la bontà dell'applicazione.
Con il livello ULTRA_LOW si è notato che venivano rilevate unicamente le interazioni inferiori al metro in contesti *free space*.
Poiché l'organizzazione mondiale della sanità raccomanda una distanza di almeno un metro [@AdvicePublicCOVID19] questo livello di trasmissione non consente di rilevare contatti potenzialmente a rischio.
@ -259,34 +259,18 @@ Per questo motivo si è scelto di utilizzare il livello LOW che permette di rile
[^dispositivi-non-omogenei]: D'altronde, data la natura non omogenea dei vari dispositivi Android, una stima quantitativa sarebbe stata impossibile da ottenere.
[^dispositivi-usati]: Sono stati usati i seguenti dispositivi; One Plus 3T con versione di Android 9, Xiaomi Mi 5 con Android 8, Xiaomi Mi A2 Lite con Android 10, Samsung Galaxy A5 con Android 7.
### Scansione
Le operazioni di scansione sono meno limitate dalle funzionalità dell'API di Android e per questo motivo c'è stata maggiore libertà di scelta.
Le operazioni di scansione, effettuate tramite l'ausilio della classe `BeaconManager`, sono meno limitate dalle funzionalità dell'API e per questo motivo c'è stata maggiore libertà di scelta.
In particolare è stato possibile settare sia l'intervallo temporale che deve intercorrere tra una scansione e la successiva, sia la durata della singola scansione.
Si è scelto di far trascorrere un minuto tra una scansione e la prossima e di avere una scansione della durata di un secondo.
Per quanto detto già in precedenza in base ai vari dispositivi e alle varie condizioni di funzionamento l'intervallo tra una scansione e la successiva potrebbe essere più ampio rispetto a quello stabilito.
Entrambi i parametri sono stati settati tramite una costante in modo tale da poter configurare facilmente il comportamento dell'applicazione.
Quando l'applicazione rivela un beacon nelle vicinanze esso viene trasmesso ad un'ulteriore componente applicativa tramite l'impiego del `LocalBroadcastManager` @BroadcastsOverview.
Quando l'applicazione rivela un beacon nelle vicinanze esso viene trasmesso ad un'ulteriore componente applicativa tramite l'impiego del `LocalBroadcastManager` [@BroadcastsOverview].
Questa componente non consuma direttamente il beacon, ma ha il compito di smistarlo ad ulteriori componenti in base alla modalità di funzionamento dell'applicazione.
Il codice necessario a smistare i dati di contatto è stato riportato nel @lst:contact-receiver.
Come si può notare nella linea 2, la prima operazione consiste nel recupero dei dati di contatto dall'`Intent`, mentre dalla linea 7 si seleziona la funzione da invocare in base alla modalità di funzionamento.
``` {.kotlin .numberLines #lst:contact-receiver caption="Codice necessario allo smistamento dei dati di contatto."}
// ...
val contactData = intent?.getSerializableExtra(CONTACT_DATA_KEY)
as ContactData?
val mode = getMod(context)
val onMode = when (mode) {
Mode.MOD_A -> this::contactOnModeA
Mode.MOD_B -> this::contactOnModeB
Mode.MOD_C -> this::contactOnModeC
}
onMode(context, contactData)
```
Nel caso della modalità *A* il beacon viene trasmesso alla classe `NetworkReceiver` che si occupa di trasmettere il contatto al server remoto.
Mentre nel caso delle modalità *B* e *C* il beacon viene consumato dalla classe `StoreReceiver` la quale si occupa della memorizzazione permanente del contatto all'interno di un database locale.
@ -304,7 +288,7 @@ $$ {#eq:distanza}
$TxPower$ è la potenza di trasmissione nominale che si misurerebbe alla distanza di un metro dalla sorgente del segnale.
Il valore di $TxPower$ deve essere precedentemente ricavato per ogni emettitore e deve essere inviato all'interno del beacon bluetooth.
Lavorando con dispositivi eterogenei tra di loro non è stato possibile calcolare in modo esatto questo valore ma si è scelto di utilizzare un valore che mediamente si adattasse a tutti i dispositivi utilizzati in fase di test.
Lavorando con dispositivi eterogenei tra di loro non è stato possibile calcolare in modo esatto questo valore, ma si è scelto di utilizzare il valore $-59$ poiché si adattava mediamente a tutti i dispositivi utilizzati in fase di test.
## UI
@ -320,7 +304,7 @@ La schermata principale dell'applicazione, riportata in @fig:ui-main, si compone
- `TextView` che indica la modalità di funzionamento.
- `Button` *start/stop* che consente di avviare o stoppare il servizio bluetooth.
Questo pulsante viene abilitato unicamente se sono stati concessi i permessi di accesso alla posizione.
- `Button` *upload*, abilitato solo nelle modalità *B* e *C*, consente all'utente di raggiungere l'activity attraverso il quale è possibile caricare sul server le informazioni locali al dispositivo (@fig:ui-upload).
- `Button` *upload*, abilitato solo nelle modalità *B* e *C*, consente all'utente di raggiungere l'activity attraverso la quale è possibile caricare sul server le informazioni locali al dispositivo (@fig:ui-upload).
![Schermata principale.](fig/main.jpg){#fig:ui-main width=130}
@ -334,6 +318,7 @@ La gestione della persistenza è stata realizzata attraverso la libreria ***Room
La memorizzazione dei contatti è avvenuta tramite lo schema riportato nel @lst:contact-data.
Questi dati vengono conservati solo nella modalità di funzionamento *B* e *C* poiché nella modalità *A* il contatto viene comunicato immediatamente al server per cui non è necessaria una memorizzazione locale.
Per il timestamp si è scelto di utilizzare un `Long` che misura i secondi trascorsi dalla *epoch unix* e che fa riferimento al *Greenwich Mean Time* (GMT).
``` {.markdown #lst:contact-data caption="Schema utilizzato per la memorizzazione dei dati di contatto."}
- `id`: Int [PrimaryKey | AutoGenerate]
@ -347,6 +332,7 @@ Questi dati vengono conservati solo nella modalità di funzionamento *B* e *C* p
Nella modalità *C* è necessario memorizzare anche le tck utilizzate nel corso del tempo (si veda la @sec:tck per maggiori dettagli).
Oltre alla memorizzazione della tck, tramite un array di byte, è necessario memorizzare anche l'indice associato ad essa e il timestamp di primo utilizzo.
Lo schema della tabella utilizzata per la memorizzazione di queste informazioni è riportato nel @lst:tck-data.
Anche in questo caso, per quanto riguarda il timestamp, restano valide le considerazioni fatte in precedenza.
``` {.markdown #lst:tck-data caption="Schema utilizzato per la memorizzazione delle tck."}
- `index`: Short [PrimaryKey]
@ -399,13 +385,14 @@ L'applicazione, all'interno dell'architettura, svolge il ruolo di *publisher* e
Il contenuto di questi messaggi coincide con la rappresentazione JSON dei dati di contatto illustrati nel @lst:contact-data.
- ***Messaggi di report***:
utilizzati esclusivamente nella modalità *C*.
Questi messaggi trasportano come *payload* la rappresentazione esadecimale del report TCN discusso nella @sec:report e vengono inviati solo quando l'*upload* è richiesto dall'utente.
Questi messaggi trasportano come *payload* la rappresentazione esadecimale del report TCN firmato discusso nella @sec:report e vengono inviati solo quando l'*upload* è richiesto dall'utente.
Anche in questo caso si è scelto di *wrappare* la libreria utilizzata all'interno di una classe sviluppata in proprio.
Poiché l'unica funzionalità di nostro interesse è la *publish* è stato necessario scrivere un'unica funzione statica che si occupa di eseguire quest'operazione.
Questa funzione, riportata nel @lst:mqtt-publish, permette di specificare il topic e il contenuto del messaggio da pubblicare.
Inoltre attraverso una *lambda expression* è possibile specificare come comportarsi in caso di errori.
Sempre dal @lst:mqtt-publish è possibile notare l'utilizzo del parametro `cleanSession` impostato a `false` e l'utilizzo dei *QoS* a livello uno, ciò si è reso necessario per garantire un corretto funzionamento con il resto dell'infrastruttura.
Sempre dal @lst:mqtt-publish è possibile notare l'utilizzo del parametro `cleanSession` impostato a `false` e l'utilizzo dei *QoS* a livello uno.
Queste scelte sono state necessarie per garantire un corretto funzionamento con il resto dell'infrastruttura.
``` {.kotlin #lst:mqtt-publish caption="Funzione wrap che consente la publicazione di un messaggio MQTT."}
fun publish(
@ -447,7 +434,7 @@ fun publish(
}
```
Le funzionalità di rete sono state testate attraverso l'impiego di un brocker pubblico e l'utilizzo del topic `untori`.
Le funzionalità di rete sono state testate attraverso l'impiego di un brocker pubblico (`tcp://broker.hivemq.com:1883`) ed un'istanza locale di *ActiveMQ Artemis* inoltre è stato utilizzato il topic `untori` per la pubblicazione dei messaggi.
Entrambi i parametri sono stati settati tramite delle costanti e quindi possono essere modificati facilmente.
# Riferimenti