Documentazione/documentazione.md

246 lines
12 KiB
Markdown
Raw Normal View History

2020-07-08 17:16:24 +00:00
# Protocollo TCN {#sec:tcn-protocol}
2020-07-06 20:31:34 +00:00
## Descrizione del protocollo
### Chiavi di autenticazione e verifica
2020-07-08 13:55:32 +00:00
### Chiave temporanea di contatto {#sec:tck}
2020-07-06 20:31:34 +00:00
### Numeri temporanei di contatto
### Report
## Implementazione del protocollo per la JMV
2020-07-08 13:55:32 +00:00
Al fine di utilizzare il protocollo precedentemente descritto all'interno dell'applicazione Android, ne è stato sviluppato un'implementazione per la *Java Virtual Machine*.
2020-07-10 10:26:40 +00:00
La gestione della coppia di chiavi derivate dalla curva ellittica *Ed25519* è stata affidata alla libreria ***ed25519-elisabeth*** [@CryptographycafeEd25519elisabeth2020].
2020-07-09 10:45:13 +00:00
Le chiavi private e pubbliche prodotte sono state *wrappate* rispettivamente nelle classi `ReportAuthorizationKey` e `ReportVerificationKey`.
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.
2020-07-06 20:31:34 +00:00
2020-07-08 13:55:32 +00:00
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.
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.
2020-07-07 20:26:33 +00:00
2020-07-08 13:55:32 +00:00
[^tck-0]: La chiave temporanea iniziale viene ricavata a partire dalla sola *rak* e da essa non è generato nessun numero temporaneo di contatto.
2020-07-07 20:26:33 +00:00
2020-07-08 13:55:32 +00:00
``` {.kotlin #lst:tck-0 caption="Derivazione della prima tck."}
2020-07-07 20:26:33 +00:00
fun baseTemporaryContactKey(): TemporaryContactKey {
val hmac = MessageDigest.getInstance("SHA-256").apply {
update(Const.H_TCK_DOMAIN_SEPARATOR)
update(key.toByteArray())
}
return TemporaryContactKey.createFromByteArray(
hmac.digest(),
0
)
}
```
2020-07-08 13:55:32 +00:00
``` {.kotlin #lst:next-tck caption="Generazione della prossima tck."}
fun nextTemporaryContactKey(
rvk: ReportVerificationKey
): TemporaryContactKey {
val hmac = MessageDigest.getInstance("SHA-256").apply {
update(Const.H_TCK_DOMAIN_SEPARATOR)
update(rvk.toByteArray())
update(key)
}
return TemporaryContactKey(
hmac.digest(),
index.inc()
)
}
```
Sempre a partire dalla tck è possibile ricavare il numero di contatto temporaneo (tcn) e da esso l'UUID utilizzato all'interno dei beacon bluetooth.
Questa operazione può essere eseguita mediante la funzione `deriveTemporaryContactNumber()` la cui implementazione è stata riportata nel @lst:derive-tcn.
``` {.kotlin #lst:derive-tcn caption="Derivazione del numero di contatto temporaneo."}
fun deriveTemporaryContactNumber(): TemporaryContactNumber {
val hmac = MessageDigest.getInstance("SHA-256").apply {
update(Const.H_TCN_DOMAIN_SEPARATOR)
update(index.toLeByteArray())
update(key)
}
return TemporaryContactNumber(
hmac.digest().sliceArray(0 until 16),
index
)
}
```
2020-07-09 10:45:13 +00:00
Un'altra componente fondamentale dell'implementazione del protocollo TCN è la classe `Report`, infatti tramite essa è possibile generare il report firmato che poi sarà inviato al server.
2020-07-08 13:55:32 +00:00
Inoltre questa classe mette a disposizione una serie di funzioni di utilità come `generateContactNumbers()` e `toReportData()` che facilitano l'estrazione delle informazioni contenute all'interno del report.
Infine la classe report fornisce anche un metodo statico, `readReportDataFromByteArray()`, attraverso il quale altre componenti applicative sono in grado di recuperare le informazioni di un report contenute all'interno di un array di bytes.
L'implementazione di questo metodo è riportata nel listato @lst:read-report.
``` {.kotlin #lst:read-report caption="Lettura dei dati di un report da un ByteArray."}
fun readReportDataFromByteArray(bytes: ByteArray): ReportData {
val buffer = ByteBuffer.wrap(bytes).apply {
order(ByteOrder.LITTLE_ENDIAN)
}
val rvk = ReportVerificationKey
.createFromByteArray(buffer.read(32))
val tckBytes = buffer.read(32)
val from = buffer.short
val until = buffer.short
val memoType = buffer.get()
val memoData = String(buffer.read(buffer.get().toInt()))
return ReportData(
rvk,
TemporaryContactKey
.createFromByteArray(tckBytes, from.dec()),
from,
until,
memoData
)
}
```
2020-07-06 20:31:34 +00:00
# Applicazione
2020-07-08 17:16:24 +00:00
L'applicazione permette di tracciare i contatti degli utenti attraverso l'impiego del Bluetooth Low Energy (BLE).
In particolare lo smartphone di ogni utente si comporta sia da trasmittente di beacon bluetooth che da ricevente.
In questo modo quando due utenti entrano nel raggio di azione del bluetooth il contatto verrà memorizzato sui rispettivi dispositivi.
2020-07-06 20:31:34 +00:00
2020-07-09 10:39:45 +00:00
L'applicazione prevede differenti modalità di funzionamento, ognuna delle quali garantisce un diverso livello di privacy.
Nella modalità di funzionamento ***A*** ogni qual volta si verifica un contatto l'applicazione si occupa di notificare immediatamente l'evento al server in modo tale che esso possa essere aggiunto al database remoto.
Questa modalità è quella meno *privacy friendly* in quanto la comunicazione avviene in *real-time* e all'interno del messaggio scambiato viene riportato sia l'UUID dell'utente sia quello della persona incontrata.
2020-07-08 17:16:24 +00:00
La modalità ***B*** prevede lo scambio delle stesse informazioni previste per la modalità precedente, ma solo se richiesto dalle autorità sanitarie.
In questo modo non solo si evita che i dati siano catturati dal server in *real-time*, ma si espongono le informazioni dell'utente solo quando queste sono strettamente necessarie.
Sia in questa modalità, che nella precedente si è scelto di non ruotare gli UUID identificativi degli utenti in modo da facilitare la generazione del grafo sul server.
2020-07-09 10:39:45 +00:00
Questa soluzione può mettere a repentaglio la privacy degli utenti ed essere sfruttata da *avversari* per ottenere informazioni sulle abitudini degli utilizzatori[^catena-negozi].
2020-07-08 17:16:24 +00:00
[^catena-negozi]: Per esempio una catena di negozi attraverso l'impiego di uno scanner bluetooth potrebbe ricostruire la *fedeltà* degli utenti, conoscere i settori del negozio preferiti ecc.
L'ultima modalità, la ***C***, è quella che tutela maggiormente la privacy degli utilizzatori attraverso due accorgimenti:
- Rotazione degli UUID
- Matching locale
2020-07-09 10:39:45 +00:00
La generazione degli UUID avviene attraverso una derivazione deterministica come visto nella @sec:tcn-protocol, in modo tale da avere lo stesso livello di privacy di una soluzione randomica, ma con una migliore scalabilità.
Mentre il matching locale permette di condividere il minor numero di informazioni possibili e solo quando questo è strettamente necessario.
2020-07-08 17:16:24 +00:00
Infatti in questa modalità l'applicazione carica le informazioni sul server solo in seguito alla richiesta delle autorità sanitarie.
Inoltre a differenza delle prime due modalità è previsto l'upload unicamente degli UUID che il dispositivo ha assunto nel tempo, in questo modo il server non è in grado di conoscere o ricavare i contatti avuti dall'utente.
2020-07-06 20:31:34 +00:00
## Bluetooth
2020-07-10 10:26:40 +00:00
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 è.
2020-07-08 17:16:24 +00:00
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.
2020-07-06 20:31:34 +00:00
2020-07-09 10:39:45 +00:00
[^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}.
2020-07-06 20:31:34 +00:00
### Trasmissione
2020-07-08 19:19:52 +00:00
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.
``` {.kotlin #lst:rotate-tcn caption="Codice necessario alla torazione del tcn."}
private fun rotateTCN() {
val advertiseHandler = Handler()
val changeTCN: Runnable = object : Runnable {
override fun run() {
tcnManager.nextTcn()
startAdvertising()
advertiseHandler.postDelayed(
this,
TCNManager.ELAPSE_BETWEEN_NEW_TCN
)
}
}
advertiseHandler.postDelayed(
changeTCN,
TCNManager.ELAPSE_BETWEEN_NEW_TCN
)
}
```
2020-07-10 10:26:40 +00:00
La scelta della frequenza di *advertising* è stata dettata dai vincoli tracciati dall'API di Android [@AdvertiseSettings].
2020-07-09 10:39:45 +00:00
Infatti la libreria permette di trasmettere un beacon con una frequenza di 1 *Hz*, 3 *Hz* o 10 *Hz*.
Fortunatamente questi vincoli non si sono rilevati troppo limitanti infatti la frequenza di un Hertz, quindi un beacon trasmetto ogni secondo, permette di avere una buona trasmissione e di risparmiare batteria.
Inoltre in fase di scanning evita che siano registrate più interazioni nello stesso ciclo.
2020-07-06 20:31:34 +00:00
2020-07-08 19:40:27 +00:00
Sempre attraverso l'API di Android è stata settata la potenza di trasmissione del beacon.
Anche in questo caso la scelta era limitata a poche alternative:
- HIGH
- MEDIUM
- 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.
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*.
2020-07-10 10:26:40 +00:00
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.
2020-07-08 19:40:27 +00:00
Per questo motivo si è scelto di utilizzare il livello LOW che permette di rilevare contatti fino a circa due metri.
[^dispositivi-non-omogenei]: D'altronde, data la natura non omogenea dei vari dispositivi Android, una stima quantitativa sarebbe stata impossibile da ottenere.
2020-07-06 20:31:34 +00:00
### Scansione
### Stima della distanza
## UI
## Memorizzazione
2020-07-10 10:48:24 +00:00
In base alla modalità di funzionamento l'applicazione deve memorizzare diversi tipi di dati.
La gestione della persistenza è stata realizzata attraverso la libreria ***Room*** [@RoomPersistenceLibrary] una componente di *Jetpack* [@AndroidJetpackAndroid], la suite di librerie sopportate da *Google*.
*Room* fornisce un layer astratto che permette di operare più facilmente con il database *SQLite* sottostante.
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 comunicano immediatamente al server per cui non è necessaria una memorizzazione locale.
``` {.markdown #lst:contact-data caption="Schema utilizzato per la memorizzazione dei dati di contatto."}
- `id`: Int [PrimaryKey | AutoGenerate]
- `uuidReceiver`: String
- `uuidSender`: String
- `rssi`: Int
- `txPower`: Int
- `timestamp`: Long
```
Nella modalità *C* è necessario memorizzare anche le tck (si veda la @sec:tck per maggiori dettagli) utilizzate nel corso del tempo.
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 utilizzato per la memorizzazione di queste informazioni è riportato nel @lst:tck-data.
2020-07-06 20:31:34 +00:00
2020-07-10 10:48:24 +00:00
``` {.markdown #lst:tck-data caption="Schema utilizzato per la memorizzazione delle tck."}
- `index`: Short [PrimaryKey]
- `timestamp`: Long,
- `tck`: ByteArray
```
2020-07-06 20:31:34 +00:00
## Rete
2020-07-10 10:26:40 +00:00
# Riferimenti