Crittografia in pratica - Qualche dettaglio
L'uso "pratico" della crittografia si basa su molti dettagli non presentati nel corso. In questa sezione sono discussi alcuni di questi molto brevemente ed in maniera molto semplificata. Possono essere utili se qualcuno vuole "provare ad usare" qualche strumento software.
Rappresentazione e memorizzazione delle chiavi
Una chiave crittografica, in pratica, viene memorizzata in un file.
Esistono molti formati diversi per rappresentare una chiave crittografica. Alcuni di questi formati sono testuali, altri sono binari.
Quando si usa uno strumento crittografico, occorre verificare in quale formato esso si aspetta di ricevere la chiave. Tipicamente uno strumento accetta molti formati che possono essere specificati con un flag di input opportuno. Se la chiave che si ha a disposizione è rappresentata in un formato diverso da quelli attesi dallo strumento, allora la chiave deve essere convertita di formato con un software appropriato.
Alcuni formati molto comuni sono:
- PEM Formato testuale (file con questo formato hanno tipicamente estensione
.pem
,.crt
,.key
). Usato spesso per la crittografia a chiave pubblica e per i certificati. Tipicamente inizia con una lineaBEGIN
e termina con una lineaEND
. La chiave è rappresentata in formato Base64-encoded. - DER Formato binario (file con questo formato hanno tipicamente estensione
.der
,.cer
). Usato spesso per certificati e per chiavi private.
Ci sono molti altri formati, ad esempio PKCS#8, PKCS#12, quelli usati dai software SSH, dai software GPG ed altri.
Protezione chiavi con password
I file che memorizzano chiavi private di solito sono associati ad una password. Per usare una chiave privata contenuta in un file è necessario dimostrare la conoscenza della password corrispondente. Il diritto di accesso in lettura al file, quindi, non deve essere sufficiente per usare la chiave contenuta nel file.
Ad esempio, un software S che deve usare una chiave privata deve ricevere in input:
- il nome del file contenente la chiave privata; e,
- la password per quel file.
I dettagli realizzativi di questo meccanismo di protezione non fanno parte di questo corso. Non sono banali: come fa S a sapere se gli è stata fornita la password corretta?
Tentativo di risposta: la memorizza nello stesso file. Ciò non avrebbe senso: chi ha diritto di accesso in lettura al file potrebbe leggerne il contenuto e quindi ottenere la password; pertanto la protezione del file con password sarebbe inutile.
Chi è interessato trova alcune note in questa pagina.
Struttura del ciphertext
Replay attack
Un network attacker potrebbe osservare un ciphertext e ritrasmetterlo successivamente. Per quello che sappiamo noi, il ricevente accetterebbe il ciphertext in quanto è autentico ed integro. Ad esempio, in TLS, il ricevente riceverebbe un messaggio con un HMAC valido, quindi non avrebbe nessun motivo per scartarlo. Ovviamente non vogliamo che ciò accada.
Nelle applicazioni pratiche della crittografia, a chiave privata e a chiave pubblica, è sempre presente una difesa nei confronti di questo replay attack.
Intuitivamente, in ogni flusso di plaintext viene sempre inserito una specie di numero di sequenza, in modo che il ricevente non accetti mai duplicati di messaggi già ricevuti.
Dictionary attack
Supponiamo di riuscire a conoscere una coppia plaintext-ciphertext per una certa chiave (p_x,c_x).
Ad esempio, con la crittografia a chiave pubblica potrei crittare p_x con una chiave pubblica nota ed ottenere c_x. Con la crittografia a chiave privata potrei sapere che in ogni interazione con un server in ascolto su una certa porta il primo plaintext è noto, quindi osservando il primo testo cifrato c_x avrei la coppia (p_x, c_x).
Generalizzando, possiamo immaginare di avere a disposizione un dictionary con molte coppie (p_x,c_x), tutte relative alla stessa chiave.
In questo modo la secrecy potrebbe non essere garantita nei confronti di un network attacker in grado di osservare traffico in cui è usata la chiave per la quale ha un dictionary: ogni volta che osserva c_x nel flusso di ciphertext, infatti, il network attacker saprebbe che il flusso di plaintext contiene p_x.
Nelle applicazioni pratiche della crittografia, a chiave privata e a chiave pubblica, è sempre presente una difesa nei confronti di questo dictionary attack.
Intuitivamente, se critto più volte uno stesso plaintext con la stessa chiave, ottengo un ciphertext sempre diverso.
Pertanto, anche se si riuscisse a costruire un dictionary come sopra, tale dictionary sarebbe inutile.
Il modo con il quale è ottenuta questa proprietà, che non approfondiamo, è più o meno questo: si suddivide il plaintext in segmenti di dimensione fissa; si inseriscono dei byte random in coda ad ognuno di tali segmenti; si crittano i segmenti. Quando si decritta si buttano via i byte random, la cui quantità e posizione sono note.
Mode of operation
In breve: se viene chiesto di specificare un "mode of operation" in una operazione crittografica, scegliere CCM oppure GCM. Il motivo è riassunto in forma intuitiva qui di seguito.
Esistono molti algoritmi di crittografia a chiave privata. Quelli più comunemente usati sono detti block ciphers perché operano su blocchi di byte, cioè prendono in input una sequenza di byte di lunghezza fissa e forniscono in output una sequenza di byte della stessa lunghezza.
Gli algoritmi block ciphers possono essere configurati per funzionare in vari modes of operation che differiscono nelle modalità con le quali i blocchi di ciphertext sono ottenuti dai blocchi di plaintext. Ad esempio:
- EBC: ogni blocco di plaintext è crittato in modo indipendente da tutti gli altri blocchi;
- CBC: ogni blocco di plaintext viene crittato dopo averne calcolato lo XOR con il precedente blocco di ciphertext (quindi ogni blocco di ciphertext dipende da tutti i blocchi di ciphertext precedenti).
Le proprietà di sicurezza dei block ciphers nei confronti di un network attacker dipendono dal mode of operation usato:
- Forniscono secrecy, authentication, integrity: CCM, CWC, EAX, GCM, IAPM, OCB.
- Forniscono solo secrecy (quindi sono raramente usati in pratica): ECB, CBC, CFB, OFB, CTR.
Authentication ed Integrity
Consideriamo un mode of operation che garantisce secrecy, authentication, integrity.
In questo caso quando il ricevente effettua la decryption ottiene, in aggiunta al flusso di byte decrittato, un'indicazione binaria:
- "ok": messaggio autentico e integro;
- "not ok": messaggio non autentico o non integro.
Authentication e integrity significano che è matematicamente certo che:
- la chiave usata dal ricevente è identica alla chiave usata dal trasmittente (la chiave è autentica);
- il ciphertext ricevuto è identico al ciphertext trasmesso (il ciphertext è integro).
Un network attacker quindi:
- non può generare un ciphertext che sarà accettato dal ricevente (perché non conosce la chiave che il ricevente userà in decryption):
- non può modificare il ciphertext trasmesso senza che il ricevente rilevi la modifica in decryption.
Notare che a questo livello di astrazione non c'è nessuna nozione di subject o di subject fisico. Autenticazione significa conoscenza di una chiave.
Secrecy senza Authentication ed Integrity
Consideriamo un mode of operation che garantisce solo secrecy, cioè non garantisce authentication ed integrity (quindi un mode of operation di scarsa utilità pratica).
In questo caso quando il ricevente effettua la decryption ottiene solo il flusso di byte decrittato, senza indicazione aggiuntiva "ok"/"not ok".
Non c'è nessuna garanzia matematica che il ricevente abbia usato la stessa chiave del trasmittente; e nessuna garanzia matematica che il ciphertext ricevuto sia identico al ciphertext trasmesso.
Ulteriore complicazione. Se provate ad usare un mode of operation che garantisce solo secrecy ed a modificare il ciphertext, ad esempio con Cyberchef, vedrete che la decryption fallisce quasi sempre. Ciò sembra garantire integrity ma non è in contraddizione con quanto sopra descritto: "molte modifiche" al ciphertext sono rilevate in fase di decryption; è però sempre possibile costruire modifiche al ciphertext che non sono rilevate in fase di decryption. Trovarle può essere più o meno facile. E' molto facile usando il mode of operation ECB (quello sopra descritto, in cui ogni blocco di plaintext è crittato in modo indipendente da tutti gli altri blocchi). Se fate qualche prova con ECB vedrete infatti che non è difficile applicare modifiche al ciphertext che non provocano errore in decryption.
Ulteriore, finale complicazione. Le modifiche al ciphertext in ECB che non provocano errore in decryption forniscono un plaintext difficile da prevedere a priori: talvolta un plaintext composto da un solo carattere o qualcosa di analogo. Un network attacker tipicamente vorrebbe modificare il ciphertext in modo che il plaintext sia esattamente una sequenza di byte scelta dall'attacker stesso. Trovare la modifica al ciphertext che oltre a non provocare errore in decryption fornisce un "chosen plaintext" è ancora più complicato (ma nei mode of operation che forniscono solo secrecy la possibilità di riuscirci non può essere esclusa matematicamente).