Tipi di pacchetti
Sul bus transitano pacchetti di dati (frame) contenenti l'indirizzo del destinatario, una serie di flag, un payload ed un checksum (schema oramai noto e collaudato).
Tutte le comunicazioni possono essere a payload criptato. Questo significa che ogni nodo può sapere molto facilmente se una comunicazione è indirizzata a lui (l'header è in chiaro), e quindi non caricarsi di decodifiche inutili.
Il primo protocollo a cui ho pensato è stato:
- enq HDR[1], ADD[2], FLG[2], PLD[16], CRC[2], Tans
- uan (unsafe ans) HDR[1], ADD[2], ACK[1]
- san (safe ans) HDR[1], ADD[2], PLD[16], CRC[2]
Purtroppo ha vari problemi che lo rendono inadatto per l'adozione come "definitivo":
- Payload decisamente ridotto e non espandibile
- Scarsa sicurezza dell'autenticazione (CRC-16)
- ...
Nel payload di enq ci possono essere 4 byte random di "key" (k) da usare nella risposta san, all'interno del payload criptato. Tali byte NON vanno usati così come sono, ma:
- Si generano 4 byte random (R)
- Si calcola/legge il valore da 4 byte (per es. lo stato degli interruttori) da trasmettere (M)
- Si crea la risposta con [R^k, R^M, 8 byte liberi]
Il ricevente non fa altro che calcolare R (ha già k) e, quindi, M. In questo modo si evitano pattern facilmente prevedibili che potrebbero fornire materiale ad un attaccante. Il contro è che il calcolo della risposta non può partire fin quando non è stato decodificato il pacchetto di enq. Una possibilità da valutare è che ogni enq fornisce il seed per la risposta alla successiva enq. Così il PIC del sensore ha a disposizione molto più tempo per decriptare la enq e calcolare la risposta, a costo di una (poco) maggiore occupazione di RAM. Se si usa il protocollo di autenticazione dei messaggi tutto questo non è necessario.
In nessun caso vanno trasmessi "troppi" messaggi criptati con la stessa chiave. "Troppi" è 2N/2 (N è il numero di bit della chiave). Questo, per chiavi di 128bit, ci costringe ad usare un contatore di 8 byte, che comunque può avere anche altri usi (per esempio contatore di timestamping.
Una possibilità da tenere presente è di evitare di indicare l'indirizzo di destinazione (del sensore) usando un protocollo tipo Zero Knowledge (utilizzabile anche per i tag RFID): in questo modo il sensore indirizzato saprà che ad interrogarlo è proprio la centralina mentre nessuno degli altri potrà sapere CHI viene interrogato, e quindi non potranno neppure falsificare la risposta, non sapendo quale chiave usare.
Un protocollo più versatile e non particolarmente complesso può essere qualcosa del genere (ispirato da "Zero-knowledge Device Authentication: Privacy & Security Enhanced RFID preserving Business Value and Consumer Convenience" di Stephan J. Engberg, Morten B. Harning e Christian Damsgaard Jensen):
- cnt->sens: IDc, TS, H(H(TS)||M) xor H(H(TS) xor Kcs), H(H(TS||M) xor Kcs), M
La centralina trasmette il proprio ID, un ID di sequenza (mai ripetuto, tipo timestamp) TS, la propria autenticazione "offuscata" dal timestamp, una prova della conoscenza del segreto condiviso Kcs ed il messaggio a cui viene aggiunta un po' di entropia (il TimeStamp; || indica la concatenazione).
- sens->cnt: R, H(H(R) xor H(M) xor Kcs xor DT)
Il sensore prova alla centrale di essere presente, di conoscere il segreto condiviso (Kcs, identificato tra le chiavi in memoria grazie ad IDc), di aver ricevuto correttamente il messaggio ed autentica la propria risposta R
- cnt e sens aggiornano Kcs: Kcs'=H(Kcs xor H(TS))
Passo delicato! Va mantenuta la vecchia Kcs caso mai fosse andata persa la risposta (la centrale risponde ripetendo ESATTAMENTE la prima query e deve ricevere la stessa risposta); comunque dopo 1s la vecchia Kcs può essere distrutta.
Questo approccio ha dalla sua un'estrema velocità (richiede solo operazioni di hash, nessuna crittografia), un'ottima robustezza (un attaccante dovrebbe essere in grado di generare collisioni per la funzione di hash utilizzata), non richiede molta memoria, il messaggio e la risposta possono essere criptati con chiavi sempre diverse. Dei "contro" non ne ho trovati, se non un possibile "birthday paradox" nel caso due sensori vengano ad avere la stessa chiave dopo l'update, ma a questo si può ovviare inserendo in M un identificativo statico del sensore, che viene verificato prima che il sensore invii la propria risposta. Si potrebbe considerare un "contro" anche la dimensione di pacchetti: 4 byte IDc, 12 byte TS, 16 byte di hash (128 bit, il minimo) per 2 (quindi 32 byte), 16 byte di messaggio = 64 byte ogni pacchetto che permettono poco più di un centinaio di interrogazioni al secondo nel caso migliore (linea a 75Kbps) con una sola centralina... Ed ogni centralina dovrebbe interrogare ogni sensore al più ogni 2 secondi, ma applicazioni di domotica richiedono una reattività molto superiore (è inaccettabile che ci voglia un secondo o più per accendere la luce...). E questo senza contare il tempo e la banda necessari per la risposta del sensore (a cui bisogna dare il tempo di calcolare tutti gli hash, effettuare le dovute verifiche, preparare la risposta ed inviarla)!
Comunque, essendo una mia modifica al protocollo citato, richiede un'attenta analisi: non vorrei aver introdotto qualche debolezza, in particolare usando come secondo nonce (RSK, nel testo originale) l'hash del messaggio (che nel caso di messaggi in chiaro sarebbe noto all'attaccante!). I problemi di velocità si possono attenuare parecchio usando linee più veloci (anche se così si riduce la massima distanza che il bus può coprire).
Una nota a proposito del lavoro da cui ho tratto l'idea: questo tipo di autenticazione non è pratica per un'applicazione di "carrello intelligente" se gli articoli sono tanti e con prezzi diversi! Il reader dovrebbe effettuare una ricerca esaustiva per sapere cosa il cliente ha acquistato... La privacy del cliente non va molto d'accordo con il registratore di cassa. Ma è un ottimo sistema se il reader sa già "in qualche modo" con quale articolo sta "parlando" (per poter selezionare il giusto segreto condiviso).
Altra nota: visto che 4 byte sono decisamente tanti per IDc, se ne potrebbe usare una parte per indicare vari flag (per esempio se M è criptato o meno o se si richiede che R sia criptato).
Ovviamente la scansione deve essere eseguita in ordine casuale, o l'offuscamento del destinatario non ha senso.