Archivi categoria: Crittografia

CentOS 6 e PHP 5.3.3: cifratura del codice sorgente mediante BLENC (Blowfish Encoder for PHP source scripts)

A quanti di voi sarà capitato di dover cifrare del codice PHP scritto di vostro pugno, magari perchè contenente informazioni sensibili, quali, ad esempio, username e password di accesso al database?

Ebbene, un metodo semplice per ottenere quanto sopra consiste nell’installare il modulo PHP BLENC (che è ancora in versione beta – qui trovate il manuale). Tale operazione può essere effettuata in modo semiautomatico utilizzando il tool pecl presente sulla nostra macchina.

PHPPrima di procedere, è necessario verificare che il suddetto applicativo sia già installato, digitando il comando:

[root@linuxbox ~]# which pecl

il cui output dovrebbe essere:

/usr/bin/pecl

Successivamente, potremo procedere con l’installazione vera e propria del modulo BLENC, lanciando il comando:

[root@linuxbox ~]# pecl install -f blenc

A questo punto potremo creare il file blenc.ini da posizionare all’interno della dir /etc/php.d, il cui contenuto dovrà  essere simile al seguente:

; Enable blenc extension module
extension=blenc.so

All’interno del file /etc/php.ini, utilizzando la entry blenc.key_file, possiamo definire il percorso in cui trovare le chiavi per decifrare gli scrip criptati, ovvero:

[blenc]

blenc.key_file = /usr/local/etc/blenckeys

Ora passiamo alla creazione del file encrypt.php, il cui contenuto dovrà essere:

<?php

$file_to_crypt=file_get_contents("security.php");

$key=blenc_encrypt($file_to_crypt, "connect.php");

$key_file = ini_get('blenc.key_file');

file_put_contents($key_file, $key."\n", FILE_APPEND);

?>

il quale ci consentirà di cifrare gli scrip di nostro interesse.

In particolare, security.php contiene il codice sorgente che vogliamo criptare, connect.php sarà il nostro file cifrato e la chiave generata mediante la funzione blenc_encrypt() verrà salvata all’interno dell’apposito file /usr/local/etc/blenckeys, ricavato mediante il parsing della direttiva blenc.key_file presente in /etc/php.ini.

Da notare che all’interno della funzione file_put_contents(), il secondo argomento contiene il carattere \n (newline), poichè per il corretto funzionamento del modulo è necessario che per ogni riga sia presente una sola chiave (dove ogni chiave si riferisce univocamente ad un file cifrato). Tale “accortezza” non è presente all’interno del manuale ufficiale.

Eseguiamo, quindi, il file encrypt.php da linea di comando:

[root@linuxbox ~]# php encrypt.php

il quale genererà il file cifrato connect.php.

Come ultimo step riavviamo httpd:

[root@linuxbox ~]# service httpd reload

ed abbiamo finito.

Alla prossima.

SSL e Diffie-Helman

Il protocollo SSL (Secure Socket Layer) prevede l’uso di alcuni algoritmi di cifratura nell’ambito dell’handshake. Uno di questi è il Diffie-Helman, che può essere adoperato in 3 varianti:

1) Anonymous Diffie-Helman (A_DH): il numero primo p e la radice primitiva a ad esso associata vengono inviati senza autentica. In realtà questa è la modalità di funzionamento “standard” del Diffie-Helman;

2) Fixed Diffie-Helman (F_DH): p ed a del server https vengono firmati mediante un certificato X.509 trusted. La chiave segreta è statica, ovvero è la medesima per ogni sessione.

3) Ephemeral Diffie-Helman (E_DH): viene generata una secret key (relativa al protocollo DH) temporanea (one-time o ephimeral), ovvero valida per una sola sessone, firmata mediante la propria chiave segreta RSA (o DSS). La controparte ne verificherà l’autenticità attraverso la chiave pubblica del mittente. Anche in questo caso si possono utilizzare dei certificati X.509 contenenti i paramentri a e p per semplificare le operazioni di autentica dei due peer.

Fine del post, alla prossima.

SHA-1, questo sconosciuto…

Usando continuamente lo SHA-1 come algoritmo di cifratura per le password salvate in MySQL, ho deciso di capire meglio come funziona e qual è la logica secondo la quale vengono creati i digest (impronte). Infatti, lo SHA-1 (acronimo di Secure Hash Algorithm), è il tipico esempio di algoritmo di cifratura one-way, ovvero non reversibile. Ciò significa che una volta cifrato il messaggio, non deve essere possibile dal punto di vista computazionale risalire al testo in chiaro. Nonostante questo, lo SHA-1 presenta comunque delle vulnerabilità: recentemente è stato infatti scoperto un attacco basato sulla crittoanalisi in grado di generare facilmente delle collisioni (sono proprio le collisioni il tallone di Achille di tutti gli algoritmi di cifratura one-way). Nella fattispecie, si ha una collisione quando due testi in chiaro producono lo stesso digest, compromettendo quindi in tutto o in parte la sicurezza delle informazioni criptate.

Fatta questa breve premessa, vediamo più da vicino il meccanismo utilizzato dallo SHA-1 per cifrare i messaggi.

Supponiamo che la stringa che si vuole cifrare abbia dimensione (in bit) inferiore a 2^64. In questo caso sarà necessario effettuare del padding (ovvero del riempimento), in modo da rendere la lunghezza L del messaggio espanso un multiplo intero di 512 bit (Nx512).

Il padding avviene nel seguente modo: si aggiunge in coda alla sequenza di bit che identifica il messaggio cifrato un bit pari a 1, diversi bit pari a 0 e due word da 64 bit ciascuna (128 bit in tutto). Esse servono proprio a rappresentare la lunghezza L del messaggio prima che su di esso venga effettuato del riempimento. Ma andiamo per ordine, supponiamo che il messaggio da cifrare sia identificato dalla seguente stringa di bit:

00010001 11001100 111001101 11110011 11101111

come si può facilmente notare, la lunghezza L della sequenza di bit è pari a 40 (L = 40). Poichè tale lunghezza è inferiore a 2^64 bit, allora devo usare del padding.

1) Aggiungo un bit pari a 1 in coda alla sequenza di bit sopra riportata:

00010001 11001100 111001101 11110011 11101111 1

2) Aggiungo tanti bit pari a 0 quanti ne servono per ottenere una dimensione pari a 448 bit (448 – 41 = 407 zeri), in modo tale da avere, successivamente all’aggiunta delle due word da 128 bit complessivi, una lunghezza del messaggio espanso pari a 512 bit (dimensione usata appositamente per rendere più esplicativo l’esempio).

00010001 11001100 111001101 11110011 11101111 10000000 00000000 00000000 00000000 ecc.

3) Creo le due word da 64 bit ciascuna. Se la dimensione del messaggio (ovvero L) è inferiore a 2^32 bit, la prima word sarà costituita da 64 bit pari a 0. La seconda word, invece, è la conversione in binario del numero intero 40, che è proprio la lunghezza L. Quindi avrò:

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 prima word;

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00101000 seconda word.

Una volta che il padding verrà completato, il testo espanso ci apparirà nel modo seguente:

00010001 11001100 111001101 11110011 11101111 10000000 00000000 00000000 00000000 ecc… 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00101000

Bene, supponiamo ora che il messaggio espanso abbia una dimensione che sia un multiplo di 512 bit. Esso verrà quindi suddiviso in Mn (leggasi M con n, dove n è il pedice) blocchi da 512 bit ciascuno. Tali blocchi andranno in ingresso ad una funzione F, il cui funzionamento verrà descritto più avanti. Occorre notare che la prima funzione F, quella cioè relativa al blocco M1, riceverà in ingresso 5 word da 32 bit ciascuna (160 bit in tutto) appartenenti ad un IV (vettore di inizializzazione), che chiameremo h0, h1, h2, h3 ed h4.

In particolare, esse vengono inizializzate ai seguenti valori (in esadecimale):

h0 = 67452301

h1 = EFCDAB89

h2 = 98BADCFE

h3 = 10325476

h4 = C3D2E1F0

Vediamo come appare lo schema di cifratura dello SHA-1:

SHA-1.JPG

Come potete notare, la prima funzione F (da sinistra) riceve in ingresso M1 e l’IV, la seconda riceve in ingresso M2 ed il risultato della somma tra l’IV e l’output della prima funzione F, generando H1 e con questa logica vengono generati H2,H3, H4, fino ad arrivare ad Hn, ovvero il digest vero e proprio (da 160 bit).

Ma cosa fa effettivamente F? Per capirlo osserviamo il seguente schema che si riferisce al primo blocco, ovvero M1:

 

Sha-1-2.JPG

Il blocco M1 viene suddiviso in 16 blocchetti w (da 0 a 15). Poichè però la funzione F consta di 80 iterazioni, i 64 blocchetti rimanenti (80 in tutto) vengono generati nel modo seguente:

w[t] = (w[t-3] XOR w[t-8] XOR w[t-14] XOR w[t-16]) << 1 con 16<=t<=79

dove << 1 rappresenta uno shift verso sinistra del risultato ottenuto (ed è proprio questa l’unica differenza esistente tra lo SHA, altrimenti conosciuto come SHA-0, e lo SHA-1).

Ogni iterazione riceve in ingresso le word a, b, c, d, ed e (ovviamente coincidenti con h0, h1, h2, h3 ed h4 per la prima iterazione), il blocco w[t] ed una costante additiva Kt, con Kt inizializzata nel modo seguente:

Kt = 5A827999  per 0 <= t <= 19

Kt = 6ED9EBA1 per 20 <= t <= 39

Kt = 8F1BBCDC per 40 <= t <= 59

Kt = CA62C1D6 per 60 <= t <= 79

Ad ogni iterazione viene generata una variabile temporanea TEMP, dove TEMP = (a << 5) + f + e + Kt + w[t] e la funzione f, che dipende da t, ovvero dall’iterazione considerata e dalle word b, c, ed e, sarà data da:

(b AND c) OR ((NOT b) AND d) per 0 <= t <= 19

b XOR c XOR d per 20 <= t <= 39

(b AND c) OR (b AND d) OR (c AND d) per 40 <= t <= 59

b XOR c XOR d per 60 <= t <= 79

Una volta creata la variabile TEMP, l’iterazione effettuerà le seguenti operazioni di sostituzione e di shifting:

1) la variabile TEMP viene salvata in a;

2) la word a viene salvata in b;

3) la word b viene salvata in c e successivamente c viene spostata di 30 posizioni verso sinistra;

4) la word c viene salvata in d;

5) la word d viene salvata in e.

Le word così ottenute andranno in ingresso all’iterazione immediatamente successiva.

Inutile dirvi che comprendere a pieno tale algoritmo di cifratura non è banale come non è per niente semplice cercare di spiegarlo in modo chiaro. Spero comunque di esservi stato d’aiuto.

A presto. 

Algoritmo Diffie-Helman per lo scambio delle chiavi

L’algoritmo Diffie-Helman rappresenta il primo esempio di crittografia a chiave pubblica. In particolare, esso consente a 2 utenti che vogliono comunicare in modo “sicuro”, di scambiarsi una chiave segreta da usare in fase di cifratura/decifratura.

La robustezza di tale algoritmo si basa sostanzialmente sulla difficoltà di calcolo dei logaritmi discreti. Quindi, per capire bene il funzionamento dell’algoritmo in questione, occorre definire in modo chiaro cos’è un logaritmo discreto.

Sia a la radice primitiva di un numero primo p, dove per radice primitiva si intende il numero le cui potenze generano tutti gli interi compresi tra 1 e p-1. In altre parole, i seguenti valori:

a mod p, a^2 mod p, …, a^p-1 mod p

saranno tutti compresi nell’intervallo [1, p-1]. Notate che sto utilizzando il modulo in quanto abbiamo a che fare con numeri discreti.

Ora, dato un qualunque numero intero b ed una radice primitiva a di un numero primo p, allora è possibile trovare un unico esponente i tale che:

b = a^i mod p

dove 0 <= i <= (p-1). L’esponente i rappresenta proprio il logaritmo discreto di b rispetto alla base a mod p e può essere rappresentato mediante la seguente notazione:

i = dloga,p(b)

Bene, ora che sappiamo cosa si intende per logaritmo discreto, possiamo illustrare il funzionamento dell’algoritmo Diffie-Helman.

Supponiamo che 2 utenti (A e B) vogliano instaurare una comunicazione bidirezionale cifrata. Essi utilizzeranno due valori pubblici, ovvero q ed a, con q numero primo ed a radice primitiva di q.

A questo punto, l’utente A sceglierà un intero casuale XA, con XA < q, che manterrà segreto. Successivamente, calcolerà YA, ovvero la chiave pubblica, dove:

YA = a^XA mod q

Tale operazione verrà effettuata anche dall’utente B, per il quale avremo:

YB = a^XB mod q

Adesso che entrambe le parti hanno generato le rispettive chiavi pubbliche, esse calcoleranno la chiave condivisa (K) nel modo seguente:

Utente A

K = YB^XA

Utente B

K = YA^XB

Ora, si può dimostrare che le due chiavi K così generate sono identiche:

1) K = YB^XA mod q

Sapendo che

2) YB = a^XB mod q

sostituendo la 2) nella 1) avremo:

K = (a^XB mod q)^XA mod q = (a^XB)^XA mod q = (a^XA)^XB mod q = (a^XA mod q)^XB mod q = YA^XB mod q

Facendo il punto della situazione, abbiamo 2 valori privati, ovvero XA ed XB, e 4 valori pubblici: a, q, YA ed YB. Affinchè l’attaccante possa individuare la chiave privata dell’utente B, egli dovrà calcolare:

XB = dloga,q(YB)

che, come affermato in precedenza, è un’operazione molto complessa dal punto di vista computazionale.

Attacchi bruteforce

Nel caso in cui il numero primo q scelto da una delle due parti che vogliono condividere una chiave K sia troppo piccolo, l’attaccante può provare, mediante un approccio a forza bruta, a calcolare uno dei due valori segreti (XA oppure XB).

Ma facciamo un esempio. Supponiamo che q sia pari a 353. Per definizione di algoritmo discreto abbiamo che:

b = a^i mod p

dove b è la chiave pubblica, i il logaritmo discreto in base a mod p ed a rappresenta la radice primitiva di q.

Come già detto in precedenza, i valori associati a b, a e p sono noti, nella fattispecie:

a = 3;

b = 90;

q = 353.

Quindi l’attaccante dovrà risolvere la seguente equazione:

90 = 3^i mod 353

Per trovare il valore di i (che rappresenta proprio la chiave segreta XA o XB, a seconda di quale dei due utenti sia la vittima dell’attacco) gli basterà calcolare tutte le potenze di 3 mod 353 fino a quando non otterrà 90 come risultato. A questo punto calcolerà K utilizzando la chiave pubblica della controparte. Ovviamente tale operazione diventa improponibile per valori di q elevati.

Attacchi replay

Ma come avviene lo scambio dei valori pubblici a e q tra l’utente A e l’utente B? Una possibile soluzione consiste nel concordarli a priori, oppure nel fare in modo che l’utente A (che inizializza la comunicazione) ne calcoli il valore e lo includa, successivamente, nel primo messaggio inviato alla controparte. Un altro approccio prevede l’uso di una directortory condivisa e fidata (per garantire in qualche modo l’autenticità della fonte), in cui salvare le chiavi pubbliche dei vari utenti. In questo caso possono comunque essere attuati degli attacchi di tipo replay.

Attacchi man-in-the-middle

Analizziamo il seguente scenario: supponiamo che Alice (utente A) e Bob (utente B) vogliano scambiarsi una chiave segreta, e che Darth (l’attaccante, ovvero l’utente D) sia in grado di intercettare le chiavi pubbliche di Alice (YA) e di Bob (YB) (nel momento in cui avviene lo scambio delle stesse).

Prima di intercettare le chiavi, l’utente D genererà una coppia di valori privati, ovvero XD1 ed XD2, che verranno usati per creare rispettivamente le chiavi pubbliche YD1 ed YD2. Nel momento in cui A invierà a B la propria chiave pubblica (ovvero YA), l’utente D la intercetterà e la sostituirà con YD1.

A questo punto, l’utente D è in possesso della chiave pubblica di A e la utilizzerà per calcolare la chiave condivisa K2:

K2 = YA^XD2 mod q

L’utente B, avendo ricevuto YD1 come chiave pubblica dell’utente A, calcolerà anch’egli K2:

K2 = YD1^XB mod q

A questo punto l’utente B invierà all’utente A la propria chiave pubblica YB. Essa verrà intercettata dall’utente D e sostituita dallo stesso con YD2.

L’utente A, convinto che YD2 sia la chiave pubblica dell’utente B, calcolerà la chiave condivisa K1:

K1 = YD2^XA mod q

Anche l’utente D calcolerà la chiave condivisa K1:

K1 = YB^XD1 mod q

Ora, l’utente A e l’utente B crederanno di condividere una chiave segreta K. In realtà, l’utente A condivide con l’utente D la chiave K1, mentre l’utente B condivide, sempre con l’utente D, la chiave K2. Darth può quindi: intercettare le comunicazioni provenienti da Alice o da Bob e/o alterare le informazioni dirette da Alice verso Bob e viceversa (tampering). Da ciò si deduce che l’algoritmo Diffie-Helman non può essere utilizzato nell’ambito della cifratura simmetrica (dato che non garantisce l’autenticazione della fonte), tornando comunque utile per lo scambio di una chiave segreta ad autenticazioni delle parti avvenuta.

Fine del post, a presto.

HMAC

In questo post abbiamo visto cosa si intende per MAC (Message Authentication Code) e come esso venga realizzato mediante alcune tecniche HASH. Adesso ci soffermeremo sul progetto HMAC, il cui scopo è quello di consentire l’uso di un qualunque algoritmo di cifratura one-way per l’autenticazione dei messaggi.

Ma perchè utilizzare algoritmi HASH con scopi di autenticazione di messaggio? Perchè sono ottimizzati per la loro esecuzione a livello software e sono facilmente reperibili su Internet sottoforma di librerie. Nonostante ciò, essi presentano una limitazione, ovvero non utilizzano una chiave segreta e quindi non possono essere direttamente utilizzati nell’ambito del MAC.

Ecco allora che proprio per sopperire a tale limitazione viene realizzato il progetto HMAC. Esso, in sintesi, si prefigge 5 obiettivi, ovvero:

1) utilizzare, senza procedere con la loro modifica, le funzioni HASH già esistenti (soprattutto quelle caratterizzate da buone prestazioni software);

2) sostituire facilmente, in caso di necessità, la funzione HASH utilizzata con un’altra che prevede migliori prestazioni ed una maggiore robustezza;

3) preservare le prestazioni originarie HASH ;

4) gestire facilmente le chiavi segrete;

5) garantire un elevato livello di sicurezza, anche grazie alle caratteristiche intrinseche dell’algoritmo di cifratura one-way utilizzato.

I primi due punti sono garantiti dall’HMAC, poichè esso vede la funzione HASH come una scatola nera. In questo modo, anche nel caso in cui la sicurezza della funzione di cifratura fosse compromessa, la si potrebbe sostituire facilmente.

Inoltre, il meccanismo HMAC può considerarsi sicuro nel momento in cui l’algoritmo di cifratura one-way è sicuro.

Vediamo adesso qual è il funzionamento complessivo di HMAC:

HMAC.JPG

dove:

1) il blocco “hash” rappresenta la funzione hash contenuta (ad esempio MD5);

2) M rappresenta il messaggio in ingresso ad HMAC;

3) L rappresenta il numero di blocchi in cui è stato suddiviso il messaggio M;

4) Yi rappresenta l’i-esimo blocco di M, con 0 <= i <= (L-1)

5) b è il numero di bit di un blocco;

6) n è la lunghezza del codice HASH prodotto dalla funzione di cifratura one-way contenuta;

7) K è la chiave segreta: se la lunghezza della chiave è maggiore di b, allora essa verrà data in pasto alla funzione HASH per generare il digest di n bit; la lunghezza consigliata è >= n.

8) K+ è la chiave completata con una serie di zeri a sinistra (padding) in modo da raggiungere la lunghezza minima indispensabile, ovvero b bit;

9) ipad è pari a 00110110 (36 in hex) ripetuto b/8 volte (se ad esempio b=16 avrò 0011011000110110, ovvero 00110110 ripetuto due volte);

10) opad è pari a 01011100 (5C in hex) ripetuto b/8 volte;

Il byte che identifica opad ed ipad viene ripetuto b/8 volte poichè non è possibile “xorare” sequenze di bit di lunghezza diversa.

Ecco, nello specifico, le operazioni effettuate nell’ambito dell’HMAC:

1) aggiungo una serie di 0 a sinistra della chiave K per ottenere la lunghezza minima di b bit, in questo modo otterrò K+;

2) effettuo lo XOR tra K+ e l’ipad;

3) accodo al risultato dello XOR, ovvero Si, il messaggio M suddiviso in L blocchi dalla lunghezza di b bit;

4) applico la funzione HASH ad (Si || M), sfruttando un opportuno vettore di inizializzazione IV;

5) se l’HASH generato ha dimensione inferiore a b bit effettuo uno zero-padding portando la sua lunghezza a b bit;

6) calcolo lo XOR tra K+ e l’opad, generando S0;

7) accodo ad S0 l’hash precedentemente generato e il risultato di tale operazione lo do in pasto alla funzione HASH, inizializzata mediante il cosiddetto IV;

8) ottengo HMAC(K,M) (costituito da n bit).

Queste operazioni possono essere riassunte nella formula:

HMAC(K, M)= H[(K+ XOR opad) || H[(K+ XOR ipad || M–

Da notare che i tempi di esecuzione di HMAC sono approssimativamente uguali a quelli della funzione HASH utilizzata.

A presto.

MAC (Message Authentication Code) e tecniche HASH

Abbiamo già visto come la cifratura convenzionale riesca a garantire un adeguato livello di protezione contro gli attacchi di tipo passivo, volti cioè all’intercettazione (sniffing) dei dati. Vediamo adesso quali sono le tecniche utilizzate per difendersi contro l’eventuale alterazione (tampering) di uno o più messaggi (attacchi di tipo attivo).

Sia ben chiaro che la cifratura convenzionale, oltre a garantire la riservatezza, riesce anche a soddisfare i requisiti associati all’autenticazione dei messaggi; supponiamo, a tal proposito, che solo mittente (A) e destinatario (B) condividano la chiave di cifratura KAB. Quando il destinatario B andrà a decifrare il messaggio inviato dal mittente A utilizzando la chiave KAB sarà certo che la sorgente del messaggio è quella legittima e che il messaggio stesso non è stato alterato. L’aggiunta di un eventuale numero di sequenza (nel caso di più messaggi) e di un timestamp, preservano il messaggio da eventuali repliche o ritardi di natura malevola.

L’uso della cifratura convenzionale per l’autenticazione dei messaggi, però, ha i suoi pro ed i suoi contro. Infatti, non conviene utilizzare tale approccio nel momento in cui uno stesso messaggio cifrato debba essere inviato in broadcast (portando tutte le macchine della rete ad effettuare operazioni di decifratura dopo averlo ricevuto), oppure nel caso in cui vengano inviati più messaggi cifrati ad una stessa macchina (magari congestionata), la quale si troverebbe costretta ad effettuare ulteriori operazioni di calcolo volte alla decrittazione del messaggio stesso.

Ecco allora che in questi casi risulta molto più conveniente utilizzare altre tecniche per l’autenticazione dei messaggi, quali il MAC (Message Authentication Code).

Tale tecnica prevede che solo mittente (A) e destinatario (B) condividano la chiave segreta KAB (detta SID). Il mittente A, prima di inviare il messaggio M, calcola il MAC, mediante una funzione che ha come argomenti il contenuto del messaggio M e la chiave segreta KAB: MAC=F(KAB, M). Il MAC così generato verrà accodato al messaggio in chiaro M e verrà inoltrato al destinario B.

Una volta ricevuto tale messaggio, B calcolerà a sua volta il MAC del messaggio utilizzando sempre KAB ed il contenuto del messaggio in chiaro (M). Se i due MAC coincidono, il destinatario ha la certezza che la sorgente è quella legittima e che il messaggio non è stato contraffatto.

Il messaggio, comunque, potrebbe essere ancora oggetto di eventuali attacchi di tipo reply oppure di eventuali ritardi dolosi.

Un’altra tecnica ampiemante utilizzata nell’ambito dell’autenticazione dei messaggi prevede l’uso di funzioni HASH. Tali funzioni devono presentare le seguenti caratteristiche:

1) devono essere applicabili a testi in chiaro di lunghezza variabile;

2) devono generare un digest (impronta) di lunghezza fissa;

3) devono possedere una complessità computazionale non elevata, in modo da semplificare la loro implementazione sia a livello software che a livello hardware;

4) dato h (il digest) è computazionalmente impossibile trovare x tale che H(x)=h. In altri termini dato il digest è computazionalmente impossibile trovare il testo in chiaro (x) a partire dal quale il digest stesso è stato generato. Tale proprietà è detta di uniderezionalità o one-way (senso unico);

5) è computazionalmente impossibile trovare y diverso da x tale che H(x) = H(y). In questo caso parliamo di resistenza debole alle collisioni (si ha una collisione quando due testi in chiaro differenti generano lo stesso digest);

6) è computazionalmente impossibile trovare una coppia (x, y) tale che H(x) = H(y). In questo caso parliamo di resistenza forte alle collisioni. La resistenza forte implica la resistenza debole, ma non vale il viceversa.

Le prime 3 proprietà sono di carattere pratico, la quarta e la quinta garantiscono discreti livelli di sicurezza, mentre la sesta consente alle funzioni HASH di resistere ad attacchi piuttosto sofisticati, tra cui l’attacco birthday, il quale riesce a ridurre la robustezza delle funzioni a m-bit da 2^m a 2^m/2.

HASH e cifratura simmetrica

Per aumentare ulteriormente i livelli di sicurezza offerti dalle funzioni HASH si potrebbe utilizzare la cifratura convenzionale (simmetrica). Ad esempio, il mittente A, dopo aver calcolato il digest del messaggio in chiaro M, lo cifra utilizzando un algoritmo (ad esempio DES) ed una chiave K (conosciuta, ovviamente, anche dal destinatario). Una volta ricevuto il messaggio, il destinatario B userà M (il messaggio in chiaro) per calcolare il digest H(M). Successivamente B decifrerà mediante K il digest calcolato dal mittente A ed accodato ad M ed infine effettuerà un confronto tra il digest calcolato direttamente da lui e quello calcolato ed inviatogli dal mittente.

Dal punto di vista computazionale, le funzioni HASH hanno costo lineare (sono sostanzialmente delle somme modulari) ed il DES è molto semplice da implementare a livello hardware, attribuendo a tale approccio un elevata ottimizzazione dal punto di vista delle prestazioni. C’è da dire, però, che il DES è un cifrario a blocchi ed in quanto tale garantisce l’integrità di ogni singolo blocco e non dell’intera sequenza. A titolo di esempio, se il mittente inviasse la sequenza 1|2|3|4 il desinatario potrebbe ricevere 2|1|3|4, alterando il significato del messaggio nella sua interezza. Per ovviare a ciò si dovrebbero utilizzare delle tecniche di concatenazione, le quali, però, sono molto costose.

Infine, in questo caso la resistenza debole alle collisioni deve essere rispettata, in quanto se si riuscisse a trovare un testo in chiaro M’ che generi lo stesso digest del messaggio lecito M, l’attaccante potrebbe sostituire M con M’ senza che il destinatario se ne accorga, compromettendo l’integrità del messaggio (e della fonte). L’unidirezionalità e la resistenza forte, invece, non sono fonte di preoccupazione.

crittosim.jpg

 

 

HASH e cifratura asimmetrica

Un altro approccio per irrobustire ulteriormente le funzioni HASH si basa sull’uso della cifratura asimmetrica. Sia, come al solito, A il mittente e B il destinatario. A calcola il digest (H(M)) sul messaggio in chiaro M e successivamente cifra il digest stesso attraverso la sua chiave segreta SA, generando H(M)’. A questo punto accoda il digest cifrato al messaggio in chiaro e lo inoltra al destinatario. Il destinatario, ovvero B, riceve il messaggio in chiaro M concatenato al digest cifrato H(M)’, calcola a sua volta il digest su M e decifra H(M)’ utilizzando la chiave pubblica del mittente, ovvero PA. Se il digest calcolato dal destinatario coincide con quello del mittente allora dovrebbe essere garantita l’autenticità della sorgente e l’integrità del messaggio. Ma è davvero così? La risposta è no. Infatti supponiamo che un attaccante intercetti M||H(M)’ e sostituisca H(M)’ con H(M)”, deliberatamente creato da egli stesso. A questo punto decifra H(M)” utilizzando PA e genera il messaggio in chiaro M”. Il messaggio risulta così alterato e stesso discorso vale per la fonte, anche se il destinatario non si è accorto di quanto avvenuto.

crittasim.jpg

 

HASH e valore segreto

Un altro metodo per rendere ancora più sicure le funzioni HASH riguarda l’uso di un valore segreto, che chiameremo SAB, condiviso da mittente A e destinatario B. Prima di calcolare l’HASH sul messaggio in chiaro M, il mittente concatena SAB ad M e solo dopo ne calcola l’HASH:

C=H(SAB||M). A questo punto A trasmetterà M||C ed appena tale messaggio verrà ricevuto dal destinatario B, quest’ultimo provvederà a concatenare ad M SAB, a calcolare l’HASH ed infine a confrontare il digest così ottenuto con quello inviatogli dal mittente. Se il confronto va a buon fine allora sia messaggio che sorgente possono considerarsi integri.

digests.jpg

 

Una variante di tale approccio viene utilizzato nell’HMAC, che esamineremo in dettaglio nei prossimi post. A presto.

Modalità di funzionamento dei cifrari a blocchi

Abbiamo già visto, nei precedenti post, cosa sono i cifrari a blocchi. Adesso cercherò di descrivere le loro possibili modalità di funzionamento.

ECB (Electronic CodeBook)

La ECB rappresenta sicuramente la più elementare tra le modalità di funzionamento dei cifrari a blocchi. In particolare, il testo in chiaro, altrimenti conosciuto come plaintext, viene suddiviso in N blocchi di lunghezza fissa, pari a 64 bit, aggiungendo del padding all’ultimo blocco in caso di necessità (ovvero nel caso in cui la lunghezza totale del plaintext non sia un multiplo intero di 64 bit).

 

Ecb_encryption.png

 

E’ bene notare che ogni blocco viene cifrato utilizzando la stessa chiave, quindi, per ogni blocco da 64 bit di testo in chiaro vi sarà il corrispettivo testo cifrato. Ecco perchè parliamo di CodeBook: si può immaginare tale modalità come un grande dizionario in cui è presente una “traduzione” per ogni blocco da 64 bit di testo in chiaro. Questa “traduzione” è appunto il testo cifrato.

Tale modalità di funzionamento dei cifrari a blocchi presenta però una grossa vulnerabilità. Infatti, nel caso in cui occorra cifrare dei testi di grandi dimensioni in cui sono presenti parti ricorrenti, il testo cifrato si ripeterà anch’esso facilitando eventuali attacchi basati sulla crittoanalisi. Ecco allora che risulta necessario utilizzare la ECB per crittografare esclusivamente testi di piccole dimensioni, come le chiavi di cifratura.

CBC (Cipher Block Chaining)

Un’altra modalità di funzionamento dei cifrari a blocchi è la CBC. Essa può essere schematizzata nel modo seguente:

Cbc_encryption.png

Il testo in chiaro viene suddiviso in blocchi di lunghezza fissa, pari a 64 bit (in modo del tutto analogo alla ECB). Però, prima di dare in pasto il plaintext all’algoritmo di cifratura, esso viene “xorato” con il testo cifrato proveniente dal blocco immediatamente precedente. Per ciò che concerne il primo blocco, non essendoci, ovviamente, blocchi precedenti ad esso, il testo in chiaro viene “xorato” con un vettore di inizializzazione (IV) e successivamente passato in ingresso all’algoritmo di cifratura, il quale utilizzerà sempre la medesima chiave K per ogni blocco. In definitiva, per l’operazione di cifratura avremo:

Cj = E(K, [Cj-1 XOR Pj] ovvero il testo cifrato ricavato dal blocco j-esimo è stato ottenuto cifrando (E) mediante la chiave K il risultato dello XOR tra il testo cifrato del blocco immediantamente precedente (Cj-1) ed il plaintext del blocco considerato (Pj).

La decifratura avviene in modo altrettanto semplice: il testo cifrato viene decifrato utilizzando sempre la stessa chiave K e successivamente il risultato di tale operazione viene “xorato” con il testo cifrato proveniente dal blocco immediatamente precedente. Nel caso del primo blocco, lo XOR viene calcolato considerando il vettore di inizializzazione utilizzato durante la fase di cifratura.

Nella figura sottostante è riportata una schematizzazione dell’operazione di decifratura:

Graphic3.jpg

Per dimostrare la validità di tale schema basta fare alcune considerazioni. Si voglia ad esempio decifrare Cj (ovvero il testo cifrato ricavato dal blocco j). Avremo:

Pj = Cj-1 XOR D(K, Cj) = Cj-1 XOR D(K, E(K, [Cj-1 XOR Pj])) = Cj-1 XOR Cj-1 XOR Pj (poichè cifratura e decifratura sono diametralmente opposte ed è come si si “annullassero” a vicenda). Poichè lo XOR di un elemento per se stesso restituisce 0 avremo:

Pj = 0 XOR Pj = Pj (dato che l’elemento neutro dello XOR è proprio lo 0).

Ovviamente, affinchè il testo cifrato possa essere decifrato correttamente, il vettore di inizializzazione deve essere conosciuto sia dal mittente che dal destinatario. Inoltre, l’IV deve essere ben protetto, almeno quanto la chiave. Tale affermazione può essere giustificata dal fatto che un eventuale attacker potrebbe far utilizzare al ricevente un valore diverso associato al vettore di inizializzazione, provocando un’alterazione delle informazioni inviate. A titolo di esempio basta osservare le seguenti formule:

Cj = E(K, [IV XOR P1]

P1 = IV XOR D(K, C1)

P1[i] = IV[i] XOR D(K, C1)[i]

dove l’indice i rappresenta l’i-esimo bit del primo blocco di plaintext (P1), del vettore di inizializzazione (IV) e dell’operazione di decifratura mediante la chiave K del blocco cifrato C1 (D(k, C1)).

Per una proprietà dello XOR si può affermare che:

P1[i]’ = IV[i]’ XOR D(K, C1)[i]

dove l’apice denota il complemento di bit. Questo implica che se l’attaccante modifica i bit del vettore di inizializzazione (calcolandone il complemento, ovvero i bit pari a 1 vengono posti a 0 e viceversa), allora verrà modificato anche il primo blocco di testo in chiaro, cioè P1. In tal caso avremo la modifica dell’intero messaggio, poichè tale alterazione andrebbe ad influire negativamente sulla decifratura dei rimanenti blocchi di plaintext.

CFB (Cipher Feedback)

Tale modalità consente di convertire un qualsiasi cifrario a blocchi in un cifrario a flusso. I vantaggi di tale operazione sono molteplici: ad esempio, con i cifrari a flusso non è necessario operare su blocchi di testo aventi lunghezza prefissata, evitando quindi eventuali operazioni di padding e consentendo una cifratura in tempo reale.

Una caratteristica che qualsiasi cifrario a flusso dovrebbe avere riguarda la dimensione del testo cifrato. In particolare, si dovrebbe usare lo stesso numero di bit per rappresentare ciascun carattere del testo in chiaro e del testo cifrato. Nel caso in cui ciò non si verifichi si andrebbe incontro ad uno spreco di potenza in trasmissione.

Durante la fase di cifratura, l’ingresso della funzione di crittografia è rappresentata da un registro a scorrimento (shift register) da 64 bit, all’interno del quale, inizialmente, viene memorizzato un vettore di inizializzazione (IV). L’intero contenuto dell’IV (64 bit) viene dato in pasto alla funzione di cifratura. Successivamente, gli s bit più significativi (ovvero quelli più a sinistra) in uscita dalla funzione di crittografia verranno “xorati” con i primi s bit associati al primo blocco di testo in chiaro (P1). In tal modo viene prodotta la prima unità di testo cifrato (C1) che verrà salvata all’interno dello shift-register utilizzato durante la seconda fase di cifratura. Nella fattispecie, gli s bit relativi a C1 rappresenteranno gli s bit più a destra (ovvero quelli meno significativi) dello shift register. Il processo viene reiterato fino all’esaurimento di tutte le unità di testo in chiaro.

Graphic1.jpg

 

 

 

Per la decifratura si utilizza lo stesso schema visto in precedenza, solo che in questo caso lo XOR viene calcolato tra gli s bit della funzione di cifratura e le unità di testo cifrato precedentemente calcolate (ovvero C1, C2, ecc.). Il perchè venga utilizzata la funzione di cifratura anche durante la fase di decifratura può essere intuito guardando le seguenti espressioni:

C1 = P1 XOR Ss[E(K, IV)] dove Ss sono gli s bit più significativi (più a sinistra) in uscita dalla funzione di cifratura. Allo stesso modo si ha che:

P1 = C1 XOR Ss[E(K, IV)].

Identico ragionamento va fatto per i passi successivi del procedimento.

Graphic2.jpg

 

Counter (CTR)

Tale modalità è stata introdotta nell’ambito di alcuni protocolli quali ATM (Asynchronous Transfer Mode) ed IPSec (IP Security).

Il suo funzionamento si basa sull’utilizzo di un contatore che (solitamente) ha le stesse dimensioni del blocco di plaintext da cifrare. Questo contatore viene inizializzato ad un certo valore e successivamente viene incrementato di un’unità per ogni blocco successivo. Ciò si rende necessario poichè è essenziale che il valore associato al contatore sia diverso per ciascun blocco da cifrare. Il valore del contatore deve essere diverso anche per ciascun testo che si intende cifrare (mediante una stessa chiave).

Ma perchè questa restrizione? Supponiamo che in ingresso alla funziona di cifratura vi sia un contatore inizializzato sempre allo stesso valore. In questo modo la funzione di crittografia produrrebbe sempre lo stesso output che andrebbe “xorato” con l’i-esimo blocco di testo in chiaro. Nel caso in cui, però, due blocchi di testo in chiaro fossero identici (ad esempio vi è una parola nel testo che si ripete più volte) ciò causerebbe la creazione di due blocchi di testo cifrato identici, facilitando notevolmente la messa in atto di attacchi basati sulla crittoanalisi.

Vediamo ora come viene effettuata l’operazione di incremento su ogni contatore. Supponiamo che ogni blocco di testo in chiaro sia composto da un numero di bit pari a b. L’incremento del contatore può basarsi indifferentemente sull’intero numero di bit che costituisce il blocco di testo in chiaro, ovvero b, oppure su una porzione di esso, che chiameremo m, dove, ovviamente, m<=b. Ogni stringa di m bit può essere facilmente rappresentata mediante un intero x che è strettamente minore di 2^m. A titolo di esempio, supponiamo che b sia pari ad 8 e che m sia pari a 5. Ciò significa che degli 8 bit che costituiscono l’intero blocco di testo in chiaro considero solo gli ultimi 5 bit (che costituiranno il mio contatore), ovvero:

10111110 bit dell’intero blocco;

***11110 bit scelti da me per inizializzare il contatore ed effettuare l’operazione di incremento.

Il massimo valore intero rappresentabile mediante questi cinque bit è pari a 31. Ovviamente 31<32, ovvero di 2^5, ecco perchè ogni stringa di m bit può essere rappresentata mediante un intero x strettamente minore di 2^m.

Incrementiamo adesso la stringa di m bit e vediamo cosa succede:

***11110

effettuo il primo incremento

***11111

effettuo il secondo incremento

***00000

effettuo il terzo incremento

***00001

e così via.

Come schematizzare tale procedura? Semplice, dato x numero intero che rappresenta la stringa di m bit iniziale, la stringa di m bit immediatamente successiva avrà come valore x+1 mod 2^m. Infatti:

***11110 = 30

***11111 = 31

***00000 = 0 (il valore massimo che può assumere la stringa è 31, ergo pongo nuovamente tutto a 0. In altri termini 32 mod 2^5 è uguale a 0).

***00001 = 1

e così via.

Abbiamo già detto che ogni contatore deve essere scelto in modo da non permettere la presenza di contatori uguali all’interno dello stesso messaggio oppure tra messaggi diversi criptati mediante la stessa chiave. A questo punto risulta banale immaginare che tale restrizione si basi principalmente sulla scelta del contatore da utilizzare per il primo blocco del messaggio. In particolare, questo contatore dovrà essere strutturato in modo tale da non assumere valori uguali nel contesto dello stesso messeggio o di tutti i messaggi cifrati con la stessa chiave.

Per soddisfare tale condizione può essere assegnato ad ogni messaggio un nonce, ovvero una stringa univoca (solitamente pari a b/2, se il risultato di tale operazione è un numero dispari bisogna arrotondare) che viene incorporato all’interno di ciascun contatore utilizzato nell’ambito dei blocchi di testo in chiaro. Quindi ogni contatore sarà costituito da N, ovvero il nonce, concatenato con gli m bit che dovranno subire l’incremento:

Tj = N | [j]m, con j che va da 1 a n, dove Tj è il j-esimo contatore, [j]m è la j-esima stringa di m bit da incrementare ed n è il numero di blocchi di plaintext.

Per completezza ecco una schematizzazione del funzionamento associato al CTR (in fase di cifratura):

1287655371.png

 

Durante la fase di decifratura, l’uscita della j-esima funzione di crittografia viene “xorata” con il j-esimo blocco di testo cifrato per dar luogo al j-esimo blocco di testo in chiaro.

I vantaggi che l’uso di tale modalità di funzionamento dei cifrari a blocchi comporta sono molteplici, ovvero:

– semplicità;

– efficienza a livello software;

– efficienza a livello hardware;

– sicurezza dimostrabile.

A presto.

Cifrari a flusso: RC4

Il cifrario a flusso RC4 fu ideato nel 1987 da Ron Rivest e rimase segreto (per ragioni commerciali) fino al 1997, anno in cui l’algoritmo che ne regola il funzionamento fu inviato al remailer anonimo Cypherpunks. Oggi, tale cifrario, è ampiamente utilizzato nell’ambito di moltissime applicazioni, quali SSL/TLS (per il Web) oppure WEP e WPA (per le reti wi-fi).

Sostanzialmente RC4 effettua tre operazioni:

1) inizializzazione del vettore S;

2) permutazione iniziale di S;

3) generazione del keystream (altrimenti detta generazione del flusso).

Inizializzazione di S (in pseudocodice)

for i = 0 to 255 do

S[i] = i;

Temp[i] = K[i mod keylen];

Inizialmente tutti gli elementi di S verranno posti uguali alla posizione da essi occupata. Ad esempio:

S[0] = 0, S[1] = 1, S[2] = 2, ...  ,S[255] = 255

dove, come si può facilmente notare, S è un vettore di 256 posizioni (da 0 a 255).

Successivamente, se la chiave K data in pasto allo pseudo random byte generator ha dimensione (keylen) pari a 256 byte, essa verrà completamente copiata all’interno del vettore Temp. Invece, nel caso in cui keylen fosse inferiore a 256 byte (poichè RC4 prevede l’uso di chiavi di lunghezza variabile), la chiave K verrà copiata tante volte in Temp fino a riempirlo completamente.

Permutazione iniziale di S (in pseudocodice)

j = 0;

for i = 0 to 255 do

j = (j + S[i] + T[i]) mod 256;

Scambia (S[i], S[j]);

Ad esempio, per i = 0, S[0] = 1 e T[0] = 3 avrò:

j = (0 + S[0] + T[0]) = (0 + 1 + 3) = 4;

Scambia (S[0], S[4]); // Scambio il byte in posizione 0 con il byte in posizione 4

Stesso procedimento va fatto per i = 1, i = 2 fino ad i = 255.

A questo punto l’array Temp non mi servirà più, in quanto la chiave era necessaria solo per calcolare la permutazione iniziale di S.

Generazione del keystream (in pseudocodice)

i, j = 0;

while (true) //continuo ad effettuare tale operazione fino a quando ho cifrato tutti i byte del plaintext

i = (i + 1) mod 256;

j = (j + S[i] ) mod 256;

Scambia (S[i], S[j]) mod 256;

k = S[t]; // k (da non confondere con K) è il byte del keystream che andrò a "xorare" con un byte del plaintext.

Osservazioni

Sia i che j sono variabili a 8 bit (quindi rappresentano un byte). Il massimo valore (in base 10) che può assumere un byte è pari a 256 (8 bit tutti pari a 1). All’interno dello pseudocodice, quindi, l’uso dell’aritmetica modulare si è rivelato necessario, in quanto sia i che j non devono mai assumere valori superiori a 256.

Facciamo un esempio:

j = (38 + S[128] + T[128]) = (38 + 64  + 192 ) = 294;

ma questo numero non è rappresentabile con 8 bit. Quindi calcolo 294 mod 256 che avrà come risultato 38. 

Vengono inoltre usati 8 bit per le variabili i e j in quanto il vettore S che le utilizza come indici consta di 256 posizioni (come detto in precedenza).

Per maggiori informazioni relative all’aritmetica modulare vi rimando a questo indirizzo.

Nei prossimi post vedremo come un uso sbagliato di RC4 possa portare a seri problemi legati alla sicurezza. A presto.

Cifrari a flusso: una breve panoramica

Fino ad ora mi sono soffermato a descrivere alcuni esempi di cifrari a blocchi, quali DES o 3DES, che suddividono il plaintext in blocchi di lunghezza fissa per poi processarli. Questa tipologia di cifrari però possiede diversi punti deboli, tra i quali una certa difficoltà di implementazione a livello software ed una certa lentezza. Ecco allora che quando l’obbiettivo principale della cifratura è, oltre alla segretezza delle informazioni criptate, anche un buon grado di efficienza, conviene utilizzare i cosiddetti cifrari a flusso.

Sostanzialmente i cifrari a flusso operano in continuo su un byte di plaintext per volta (anche se esistono alcune implementazioni che agiscono su unità superiori al byte). Essi utilizzano una chiave da dare in pasto ad un generatore di byte pseudocasuali, il quale, a sua volta, produrrà in output una chiave di flusso, detta appunto keystream. Quest’ultima verrà “xorata” byte per byte con il plaintext (che, come detto in precedenza, viene processato un byte per volta).

La fase di cifratura può essere schematizzata nel modo seguente:

flusso.jpg

 

mentre quella di decifraturà apparirà nel modo seguente:

 

Flusso-1.jpg

dove M è il messaggio in chiaro, C è il messaggio cifrato.

Bene, ora sappiamo che i cifrari a flusso sono in genere più efficienti di quelli a blocchi, e soprattutto richiedono meno codice per la loro implementazione. Ma quali sono i possibili punti deboli dei cifrari in questione? Ovviamente, uno dei punti deboli di maggior rilevanza potrebbe essere il periodo associato al generatore di byte pseudocasuali. Infatti, se i byte generati si ripetono in un periodo troppo breve, la crittoanalisi diviene un’operazione piuttosto semplice da effettuare. Discorso analogo riguarda lo pseudo random byte generator in quanto tale: se i byte generati non hanno tutta l’apparenza di byte completamente casuali, allora gli attacchi di crittoanalisi diverranno molto meno complessi. Infine, la chiave da dare in pasto al generatore di byte pseudocasuali deve essere di almeno 128 bit, poichè sappiamo che chiavi di dimensioni maggiori riescono a garantire una maggiore robustezza nei confronti di attacchi a forza bruta.

Nel prossimo post parlerò del cifrario a flusso attualmente più utilizzato, ovvero RC4. A presto.

3DES: vantaggi e svantaggi

Dopo che le vulnerabilità del DES contro attacchi a forza bruta furono messe in luce, l’NSA dovette correre ai ripari. Fare in modo che il DEA utilizzasse chiavi di lunghezza superiore ai 64 bit era improponibile (sarebbe stato necessario cambiare un pò tutto l’algoritmo), quindi si scelse un altro approccio, che portò alla realizzazione del 3DES (altrimenti detto TDES, ovvero Triple DES). Tale sistema di cifratura non è altro che la tripla implementazione del DES stesso: poichè una singola implementazione utilizza chiavi da 64 bit, il 3DES utilizza chiavi da 192 bit (di cui 168 effettivi e 24 parità, 8 per ogni implementazione).

La cifratura mediante triplo DES si articola nei seguenti passi:

C = E(K3,D(K2,E(K1,M)))

dove M è il messaggio in chiaro, E(K1,M) indica la cifratura di M mediante la chiave K1, D(K2,E(K1,M)) indica la decifratura del messaggio precedentemente criptato, utilizzando questa volta la chiave k2, ed infine,  E(K3,D(K2,E(K1,M))) indica la cifratura del messaggio appena decriptato, però mediante la chiave K3. Per capire meglio tale sequenza di operazioni, è conveniente illustrare la seguente schematizzazione:

TDES-1.jpg

 

 

Occorre notare che l’operazione di decifratura non viene eseguita con la finalità di aumentare la robustezza del 3DES, ma solo per renderlo retrocompatibile con il DES. Ciò significa che coloro che fanno uso del 3DES come algoritmo di cifratura/decifratura, possono tranquillamente decifrare messaggi criptati mediante DES puro.

La decifratura consiste nelle stesse operazioni della cifratura, ma utilizza le chiavi in ordine inverso:

M = D(K1, E(K2, D(K3,C)))

dove C è il messaggio cifrato. Tale operazione viene rappresentata di seguito:

TDES-2.jpg

 

Esiste inoltre una variante del DES triplo, in cui le chiavi K1 e K3 sono identiche. Ecco allora che la chiave complessiva sarà composta da 128 bit, di cui 112 effettivi (e 16 di parità).

Ma anche il 3DES, nonostante unisca resistenza contro attacchi di tipo bruteforce ad un elevata robustezza nei confronti di attacchi basati sulla crittoanalisi (ereditata dal DEA), ha i suoi svantaggi. Quello maggiore riguarda l’uso di blocchi dalla dimensione di 64 bit, quando per ottenere un buon margine di sicurezza occorre utilizzare blocchi di almeno 128 bit. L’altro punto debole riguarda la complessità di implementazione a livello software. Infatti, poichè il DES è stato sviluppato in modo da essere eseguito facilmente sui dispositivi hardware, la sua esecuzione a livello software è poco efficiente e , di conseguenza, questa sua pecca viene ereditata inevitabilmente dal DES triplo.

Spero di essere stato esaustivo. A presto.