Il linguaggio di programmazione utilizzato per sviluppare il progetto è Java (JDK versione 1.2).
Questa scelta ci permette di utilizzare unorganizzazione ad Oggetti nella progettazione del sistema, rendendo più modulare e semplice la realizzazione dellintero sistema.
Inoltre viene garantita la portabilità su sistemi differenti (Windows, Unix ), caratteristica fondamentale per un servizio distribuito.
Per quanto concerne la parte di Crittografia (non disponibile nelle distribuzioni europee di Java), abbiamo scelto lEntrust Java Toolkit, che, rispettando le interfacce definite nel package java.security, consente una perfetta integrazione con il linguaggio Java.
Utilizzeremo, almeno in parte, il package JNDI 1.2, che definisce la struttura di un generico servizio di nomi.
Il progetto è stato suddiviso, nel suo sviluppo, in due parti principali:
sviluppate parallelamente pur mantenendo un buon grado di coesione.
Con binding del DNS si intende ogni tipo di associazione che può essere presente in un Sistema di Nomi, in generale sarà una coppia del tipo: < Name , Value >.
La classe DNSName modella lastrazione di nome del DNS, si può considerare costituita da un identificativo (il nome utente,o identificativo di un servizio, nel nostro caso NS o AS) e dal dominio di appartenenza che può essere eventualmente nullo per spazi di nomi "flat".
Type è una classe astratta, le cui sottoclassi rappresentano un tipo di nome, nel nostro caso abbiamo un identificativo di utente (UserType), di Name Server (NSType), e di Authentication Server (ASType). Per aggiungere unaltro tipo di servizio da utilizzare tramite il DNS è sufficiente aggiungere una sottoclasse corrispondente.
Domain è la classe che generalizza il nome di dominio, implementa linterfaccia javax.naming.Name che definisce le caratteristiche di un generico nome in uno spazio di nomi gerarchico.
Definiamo brevemente la sintassi dei nomi di dominio in notazione BNF:
Dominio::= root
|Nome .
Dominio root ::= Nome::= alfa | alfa Nome alfa ::= a..z | 0..9 |
I nomi di dominio sono assoluti, rappresentano dunque una lista, da destra verso sinistra, di nomi semplici, separati da . il cui primo elemento è sempre il nome di root rappresentato per convenzione dalla stringa vuota.
Per cui, in un dominio, dovrebbe esserci sempre il punto finale (ad es. "b. a."), dove non presente ("b.a") si intende sottointeso.
Lutilizzo dei nomi è libero tranne che per il nome di root che non può essere replicato (ad es. b..a. è errato).
In caso di costruzione di un nome errato viene sollevata leccezione javax.naming.InvalidNameException .
I nomi utente seguono la seguente semplice sintassi:
NomeUtente::= UserId @ Dominio |
La classe Parser data una stringa permette di ottenere il DNSName corrispondente, questa eredita dalla classe astratta NameParser in modo che sia possibile definire diversi Parser per sintassi dei nomi differenti.
Le sottoclassi PrimaryNameServer e SecondaryNameServer aggiungono il meccanismo della replicazione.
Funzionalità realizzate dalla classe NameServer:
Il NameServer è un caratteristico Server concorrente e parallelo:
La classe IterQueryResolver, che rappresenta un Thread separato, implementa tutta la logica della risoluzione iterativa dei nomi, in questo modo sarebbe possibile realizzare altri tipi di risoluzione (ricorsiva o transitiva), senza modificare la struttura del DNS.
BindingsTable è una tabella che permette di memorizzare e accedere ai Binding. Un NameServer ne utilizza due sottoclassi:
Table mantiene tutte le informazioni necessarie ad un NameServer, per cui alla tabella delle associazioni aggiunge due liste, una per i sottodomini delegati e una per quelli non delegati.
Cache gestisce il caching delle informazioni, ad ogni Binding associa un tempo di vita in secondi (quello del NameServer da cui è stato reperito), allo scadere del tempo di vita, lassociazione perde di validità e può essere eliminata.
Funzionalità realizzate dalla classe PrimaryNameServer:
Oltre alle funzionalità ereditate da NameServer , la classe PrimaryNameServer gestisce linserimento delle associazioni e la creazione dei sottodomini, con i relativi controlli di correttezza, e laggiornamento dei secondari. A questo scopo utilizza la classe UpdateThread ,che crea una porta di ascolto TCP per i secondari, ed utilizza a sua volta la classe UpdateSecondaryThread per il trasferimento dei dati.
Funzionalità realizzate dalla classe SecondaryNameServer :
La classe SecondaryNameServer richiede periodicamente una tabella aggiornata al Server Primario.
Le interrogazioni da parte dei client vengono indirizzate ad un Resolver, che invia le richieste ad un Name Server del dominio. Il Resolver è un Server multithreading che accetta solo richieste locali. In questo modo il caching effettuato dal Resolver è disponibile per tutti i client del nodo.
Le comunicazioni tra Name Server avvengono tramite scambio di messaggi:
La classe astratta PacketMsg rappresenta il generico messaggio, implementa i metodi per trasformare il messaggio in DatagramPacket e viceversa.
SignedMsg aggiunge al messaggio un meccanismo di Signature:
Il sistema di nomi potrebbe rappresentare un punto
debole per la sicurezza (soprattutto nel nostro caso di
un servizio basato sulla sicurezza), perciò deve
assicurare:
Poichè non è necessaria la segretezza delle informazioni (che sono di dominio pubblico) è sufficiente un meccanismo di Signature associato al timestamping dei messaggi, che permette di ridurre le dimensioni del pacchetto rispetto alla cifratura del messaggio. |
A ciascun pacchetto creato da SignedMsg viene quindi aggiunta la signature delle informazioni contenute, e questa viene controllata alla ricezione. In caso di violazioni della sicurezza o corruzione del pacchetto viene sollevata una eccezione.
TimestampedMsg gestisce il timestamping dei messaggi.
QueryMsg è la classe astratta che rappresenta i messaggi di richiesta di informazioni, mentre AnswerMsg i messaggi di risposta.
ReferToMsg è il messaggio che specifica qual è il Name Server cui rivolgersi per ottenere linformazione richiesta.
Classi che ereditano da QueryMsg:
Queste classi non necessitano di autenticazione del mittente, per cui non ereditano da SignedMsg, a patto che AnswerMsg riporti nella risposta anche il nome che era stato richiesto in modo che sia possibile verificare che il messaggio non sia stato sostituito con la risposta ad unaltra richiesta.
Classi che ereditano da AnswerMsg:
Lo scambio di messaggi avviene con un protocollo UDP senza connessione. La scelta è stata fatta poichè i messaggi sono generalmente di dimensioni contenute e quindi non sarebbe giustificato loverhead introdotto dalla connessione.
La semantica per linvio dei messaggi è at-least-once poichè si assume che nel breve termine tutte le richieste siano idempotenti:
Il richiedente invia il messaggio e si pone in attesa con timeout, allo scadere si effettua il reinvio del messaggio (ad una altro Server se disponibile), fino al numero massimo di ripetizioni consentite, al ricevimento di un messaggio di risposta (qualunque sia la sua provenienza), tutti i successivi vengono ignorati.
Come ipotesi di guasto fissiamo il numero massimo di tentativi di reinvio a 3.
Le classi principali sono:
PacketMsg:
Questo metodo trasforma il messaggio in un DatagramPacket affinchè possa essere inviato tramite una DatagramSocket allindirizzo addr:port.
Il metodo dato un pacchetto restituisce il messaggio corrispondente.
Per la trasformazione dei messaggi in pacchetti e
viceversa si utilizza il concetto di Serializzazione
del linguaggio Java. In questo modo è molto più agevole
estendere il sistema con nuovi messaggi poiché non ci si
deve preoccupare della codifica degli stessi in pacchetti
e viceversa. Il problema della Serializzazione è che linterprete Java inserisce molte informazioni non necessarie, per poter codificare gli oggetti negli stream. Per questo tutti gli oggetti utilizzati nei messaggi (come DnsName, Domain ecc.) dovranno ridefinire la serializzazione, in modo da ridurre la dimensione dei messaggi, tramite loverriding dei metodi:
In questo modo è possibile non serializzare le strutture che contengono i dati (come ArrayList o HashTable) per ricostruirle soltanto in seguito, oppure inviare delle rappresentazioni compatte dei dati. |
Per evitare di eccedere nella dimensione dei pacchetti, la classe PacketMsg stabilisce una dimensione massima di 1024 byte, che viene controllata alla creazione del DatagramPacket, in caso di dimensioni eccessive viene sollevata uneccezione MsgTooLongException.
SignedMsg:
Oltre alla serializzazione delloggetto, aggiunge una signature dellarray di byte che lo rappresenta, mediante loggetto iaik.java.security.Segnature.
Trasforma il pacchetto in messaggio e ne verifica lintegrità tramite la signature allegata.
Si assume che la porta utilizzata dal servizio sia nota e comuni a tutti i Server (Resolver, e NS primari e secondari).
Un Client alla necessità di risolvere un nome invia un QueryMsg (DomainQueryMsg per conoscere il proprio dominio, NameQueryMsg per ottenere lindirizzo associato ad un nome), al Resolver locale (reperibile allindirizzo locale alla porta 5656), questi conosce gli indirizzi dei NS del dominio, si occupa di risolvere la query e restituisce il risultato al Client come AnswerMsg (a seconda del risultato della query).
Il Resolver innanzitutto verifica che il nome richiesto nome richiesto non sia presente in Cache, nel caso in cui risponde immediatamente al Client. Altrimenti invia il QueryMsg ricevuto ad un Name Server del dominio, che si occuperà della ricerca iterativa e attende da questo la risposta.
Il NameServer allattivazione entra in un ciclo infinito in cui si mette in ascolto su di una DatagramSocket in attesa di QueryMsg. Allarrivo di un messaggio ne affida la gestione ad un IterQueryResolver.
La classe IterQueryResolver svolge quindi due compiti:
In realtà sono disponibili due tipi di ricerca iterativa, una normale con la prima richiesta verso la Root:
, ed una con la prima richiesta verso il dominio di livello superiore:
La scelta è fatta utilizzando una funzione euristica:
che determina la "distanza" dal dominio richiesto, intesa come numero di Name Server che devono essere coinvolti nel caso peggiore, cioè senza l'intervento della cache.
Per fare ciò si verifica qual è il dominio superiore comune a quello richiesto e a quello richiedente (nel caso peggiore il dominio di root), e si confronta la distanza tra questo e, rispettivamente, il dominio richiedente e quello di root.
Flusso di esecuzione di IterQueryResolver:
Per prima cosa IterQueryResolver controlla se il nome richiesto è locale o appartiene ad un sottodominio non delegato, in questo caso il Name Server è competente e si risponde immediatamente al richiedente.
Se ciò non avviene si controlla la cache. Se il nome è presente si risponde, altrimenti si ricerca una "scorciatoia", in altre parole si ricerca un Name Server per un dominio superiore gerarchicamente a quello richiesto, la ricerca ha termine con successo quando il NS è trovato, e comunque quando si raggiunge un dominio genitore del dominio che effettua la ricerca (compreso il dominio stesso e la root) poiché potrebbe non essere più vantaggioso.
A questo punto si decide se effettuare:
Primo passo:
Se in cache è stata trovata un Refer si utilizza, oppure se il nome richiesto appartiene ad un sottodominio delegato si invia la richiesta al NS corrispondente. Altrimenti si decide se effettuare la ricerca iterativa verso l'alto o se inviare la richiesta alla Root.
Passi Successivi:
Alla ricezione di un ReferTo si invia la query all'indirizzo corrispondente finchè non si riceve un AnswerMsg, che viene inviato come risposta al richiedente.
Si controlla che il dominio sia un sottodominio, in questo caso sia invia un ReferTo del sottodominio delegato competente. Altrimenti si tratta di una ricerca iterativa verso l'alto per cui si invia il ReferTo del dominio superiore.
In caso di nomi errati o inesistenti bisogna prevenire l'innescarsi di cicli infiniti di ricerca. E' sempre possibile determinare quando un dominio è competente per un dato nome, e quindi se un nome è corretto o meno. Inoltre poniamo un limite massimo al numero di cicli iterativi che possono essere eseguiti.
Questo è il meccanismo di funzionamento dei Name Server indipendentemente dal fatto che siano primari o secondari. L'unica differenza è il metodo di reperimento della tabella. Il Primario mantiene la propria aggiornando periodicamente un file che è letto allo startup. Inoltre avvia un Thread che apre un a porta TCP per le richieste di aggiornamento da parte dei secondari.
All'avvio il Secondario attiva la connessione e riceve dal Primario la tabella.
Periodicamente il Secondario richiede laggiornamento della tabella al Primario, il tempo che intercorre tra un aggiornamento e laltro è un parametro del Server, e può variare la consistenza dei dati. Laggiornamento non avviene indiscriminatamente, ma solo se ci sono state variazioni della tabella dallultimo aggiornamento.
Poiché la classe PacketMsg ha una dimensione massima, viene posto un limite di 10 nella registrazione di valori da associare ad un determinato nome.
Non è quindi possibile registrare più di 10 Name Server per un determinato dominio (1 Primario e 9 Secondari).
Si noti che è comunque possibile inserire altri Name Server "non registrati" con il solo compito di effettuare ricerche e caching.
I punti chiave dell AS sono:
Il Gestore mantiene una porta di ascolto dove attende le richieste da parte di:
Inoltre gestisce la lista delle Copie attualmente in funzione, mantiene la tabella delle chiavi pubbliche (sempre consistente) relativa al proprio dominio, fornisce un timestamp unico per ogni operazione in modo che tutti i messaggi inviati siano riconoscibili come inviati dal mittente Gestore e attuali (cioè non riutilizzati).
Per gestire la comunicazione, a seguito di ogni richiesta, genera un thread il quale svolge tutto il lavoro. La replicazione è realizzata mediante una serie di Gestori Secondari il cui primo passo è richiedere lo stato attuale delle Copie attive al Gestore primario; e quindi iniziare a fornire il servizio al pari del Gestore Primario.
Le nuove Copie che si rendono disponibili a fornire il servizio si registrano su tutti i Gestori del proprio dominio, in questo modo si sono evitati i messaggi di coordinazione tra il Gestore master e gli slave poiché tutti possiedono le stesse informazioni (ricordiamo che abbiamo trattato solo il problema della distribuzione delle chiavi, e non quello della registrazione di nuovi utenti, compito svolto da un autorità di certificazione esterna, e di conseguenza per quel che ci riguarda lavoriamo su una tabella di chiavi pubbliche statica).
Un ultima considerazione riguarda il fatto che il Gestore contattato dal Client non è quello relativo al proprio dominio ma è quello del Client su cui viene effettuata la richiesta; è questo infatti che possiede le informazioni di cui abbiamo bisogno.
Ottenuta dal Dns la lista dei Gestori attivi, la Copia inoltra la domanda al primo Gestore della lista (ovvero al Master, sempre in cima alla lista), e resta in attesa dellinvio della tabella mediante la Socket TCP aperta (dal Gestore) in conseguenza alla sua richiesta.
Il primo passo svolto dal Gestore è la lettura dell'Hashtable (la lettura viene effettuata da file per semplicità nella realtà la tabella dovrebbe essere sempre a disposizione del Gestore memorizzata in un area protetta della memoria). Nel caso non sia possibile accedere alla tabella viene chiusa la socket in modo che la Copia non rimanga indefinitivamente in attesa. In realtà viene terminato anche il Gestore in quanto non ha senso di esistere se non è in possesso di una tabella (consistente) da fornire alle Copie, inoltre l' assenza della tabella potrebbe significare problemi o manomissioni. Noi trattando la distribuzione delle chiavi partiamo sempre dallipotesi che il Gestore abbia, in ogni momento, a sua disposizione una tabella consistente.
Si è scelto di non inviare la tabella come singolo oggetto ma di inviare singolarmente le varie entry della tabella stessa (viene inviata su socket la dimensione della tabella da leggere quindi ogni singola entry viene firmata, e spedita). In questo modo si è ridotto il numero di byte inviati limitando il problema derivante dallinvio di tabelle di una certa dimensione (si sono così evitati piccoli problemi interni al pacchetto della sicurezza java in cui ci siamo imbattuti legati alla signature di tali pacchetti, inoltre viene così ridotto, seppure di poco, il tempo per la codifica/decodifica del messaggio).
Notare che ogni entry viene numerata a partire da un timestamp unico cioè i timestamp delle singole entry seguono un ordine logico di numerazione. Se anche il timestamp di una sola entry non coincide dalla parte della Copia, il processo di aggiornamento fallisce. Terminato linvio delle entry della tabella delle chiavi pubbliche è il Gestore che si mette in attesa che la Copia gli invii lultimo timestamp ricevuto, incrementato come riprova del fatto che solo la Copia poteva essere a conoscenza della chiave per decodificare quel messaggio, che la tabella è stata inviata correttamente in tutte le sue entry, e che i messaggi sono effettivamente attuali.
Lultimo messaggio serve infatti da conferma per verificare che le entry ricevute dalla Copia non siano TUTTI messaggi vecchi intercettati e riutilizzati.
Se anche dalla parte del Gestore tutto coincide: il messaggio è corretto, non ha cioè subito modifiche durante il suo cammino e il timestamp ricevuto coincide con lultimo timestamp inviato alla Copia incrementato di uno, la Copia viene considerata attiva (funzionante) altrimenti attende il riinoltro della richiesta da parte della Copia.
Il Client, ottenuti gli indirizzi dei Gestori attivi dal DNS, effettua una richiesta per ottenere la chiave pubblica di un altro Client ad un Gestore, il quale la invia ad una Copia. Le richieste vengono distribuite sequenzialmente in modo da mantenere un carico di lavoro abbastanza uniforme tra le varie Copie (considerando un medesimo tempo di risposta ).
Naturalmente la richiesta effettuata dal Gestore alla Copia puo avere esito positivo o negativo. Nel primo caso siamo in possesso della chiave pubblica richiesta dal Client e dobbiamo effettuare la risposta in modo che il Client sia sicuro della consistenza della chiave ricevuta. In caso negativo viene inviato al Cliente un messaggo indicante limpossibilità di reperire la chiave o risposte indicanti il rifiuto o lassenza del Cliente specificato nel database.
I messaggi di rifiuto li abbiampo solo nel caso in cui i messaggi vengano modificati durante il loro cammino, in questo caso il Gestore, o la Copia ricevendoli si rende conto della non consistenza di suddetti messaggi e li invalida.
Fondamentale in tutti i messaggi è il controllo della coincidenza dei timestamp per evitare il replying dei msg (anche in quest ultimo caso viene invalidata la risposta ricevuta e subito inviata un ulteriore richiesta).
Ma ci sono casi in cui non riceviamo risposta dalla Copia:
Nel caso una Copia non risponda (msg di timeout per 2 volte consecutive) viene cancellato il suo indirizzo da quelli disponibili e viene inoltrata la domanda a un altra Copia;
Ricevuto correttamente il messaggio dalla Copia recante la chiave del Client richiesta, il Gestore gli manda un messaggio con il SUO (del Client) timestamp e con una signature per l' autenticazione.
I passi svolti da una Copia sono:
La scelta di non generare un thread per gestire le richieste delle Copie è dettata dal fatto che:
La tabella hash è passata con il protocollo TCP. Nel caso si verifichino problemi, come ad esempio intromissioni di terzi, viene reinoltrata la richiesta di aggiornamento. La comunicazione tra Copia e Gestore per il servizio di distribuzione delle chiavi pubbliche utilizza invece UDP; si sono considerati quindi tutti i possibili casi di riemissione dei messaggi in caso di perdita o modifica degli stessi.
Per messaggi si intendono tutti i messaggi utilizzati nellambito dellAuthentication Server e quindi quelli scambiati tra Client, Gestore e Copie. E stata creata una classe astratta PacketMsg che fornisce tutte le funzioni per creare pacchetti (tale funzione la ritroviamo anche come base per i messaggi del DNS).
Descrizione messaggi:
Questi messaggi non vengono inviati direttamente perché non permetterebbero lautenticazione dellidentità del mittente (discorso a parte per il messaggio di timeout che non viene mai spedito, e il messaggio di reqCopieMsg che non necessita di autenticazione). Si è quindi preferito incapsularli allinterno di altri messaggi che contengano anche un campo signature in modo che sia sempre possibile verificare chi sia il mittente, se il messaggio è attuale e non è stato modificato . Questi messaggi contenitori sono quelli costruiti a partire dalla classe msgFirma che fornisce tutte la funzioni per lautenticazione e la verifica degli oggetti inviati.
Descrizione messaggi di autenticazione:
La classe tistamp contiene un timestamp (un intero) e le funzioni per recuperarlo. La classe tistampc aggiunge un ulteriore timestamp, mentre la classe tistamperr permette di inviare un messaggio di errore.
La classe nonce è un oggetto contenente lidentità di chi invia il messaggio e un tistamp codificato (array di byte). Fornisce anche le funzioni per preparare (preparanonce) e verificare (verificanonce) i nonce. La classe nonceritorno contiene due nonce. Naturalmente cè anche un messaggio di errore per indicare eventuali modifiche sul messaggio.
La classe entry mantiene un nome, una chiave pubblica (in pratica una entry della tabella delle chiavi pubbliche) e un timestamp
La classe indirizzo mantiene un indirizzo IP e un intero (che dovrebbe rappresentare la porta di ascolto del processo in questione).
La classe msgByte contiene un array di byte che rappresenta il messaggio del Client criptato.
La classe adrC contiene un campo indirizzo e un timestamp. Il suo utilizzo è legato esclusivamente allinvio della lista delle Copie attive dal Gestore primario al Secondario. Il protocollo di aggiornamento è identico a quello usato tra Gestore e Copia per linvio della tabella delle chiavi (si inviano gli indirizzi uno alla volta firmati e numerati).
La classe RSACipher è la classe contenente tutte le funzioni per la cifratura e decifratura di messaggi. La vedremo meglio in seguito.
Gestore gestisce la porta di ascolto;
GestoreS eretita da Gestore, lunica funzione nuova che implementa riguarda la richiesta di aggiornamento per la lista degli indirizzi delle Copie attualmente attive (classe ShtS)
msgFirma: vedi sopra.
GestoreThread: gestisce tutta la comunicazione del Gestore (dopo che è arrivata una richiesta).
connettiTCP: mantiene tutte le funzionalità per gestire una connessione TCP.
DtgRichiesta: mantiene tutte le funzionalità per gestire una comunicazione UDP:
Sht: mantiene tutte le funzionalità per gestire il trasferimento della tabella delle chiavi pubbliche da Gestore a Copia:
Client gestisce la comunicazione del Client richiedente;
ClientListen apre una porta di ascolto;
ClientThread gestisce tutta la comunicazione del Cliente richiesto;
ClientGui gestisce linterfaccia grafica del Client;
connettiTCP: vedi sopra;
BtoA gestisce tutta la comunicazione tra i due Client. mantiene le funzioni per:
RSACipher è la classe contenente tutte le funzioni per la codifica dei messaggi:
Copia gestisce tutto il lavoro della Copia;
cli mantiene tutte le funzionalità per gestire il ricevimento della tabella delle chiavi pubbliche da Gestore a Copia ed effettua tutti i controlli relativi al caso;
Il Client deve in ogni momento esser pronto a inviare e ricevere messaggi. Proprio per questo il corpo principale del Client chiama un thread che si mette in ascolto su una determinata porta. Chiunque voglia comunicare con il Client utilizzerà questa porta per instaurare una connessione. Ad ogni richiesta di connessione viene generato un ulteriore thread che gestisce la comunicazione stessa (si è cercato di evitare il più possibile la serializzazione).
Ottenuta la chiave pubblica dal Gestore (di B), il Client è pronto per iniziare la procedura di autenticazione per la comunicazione con laltro Client (naturalmente dopo averne ottenuto lindirizzo dal DNS). Il protocollo cui facciamo riferimento è quello di Needahm-Schroeder lievemente modificato:
Finito questo protocollo i due Client sono a conoscenza delle rispettive identità e mantengono una socket TCP per la comunicazione. Tale comunicazione è stata semplificata e realizzata come un unico messaggio da A a B (in quanto si è ritenuto più importante il procedimento utilizzato rispetto al numero di messaggi che si potevano inviare).
Tale messaggio non può viaggiare in chiaro nel rispetto della privacy dei due Client e quindi non si è potuta utilizzare una signature per i messaggi. Inoltre la signature viene autenticata con la chiave privata e verificata con la chiave pubblica; qui avviene lesatto contrario il messaggio viene autenticato con la chiave pubblica e verificato con la chiave privata. Si è scelto quindi di codificare il messaggio e di inviarlo(msgByte). Nella realtà il messaggio è stato suddiviso in tanti blocchi, che sono alla fine riuniti in modo che venisse spedito un unico stream di byte. Tale stream viene poi letto e decifrato allarrivo. Tutto questo avviene in modo totalmente trasparente allutente. La suddivisione in blocchi è stata realizzata in quanto la procedura di crittografia di java non supporta blocchi superiori ai 64 byte.
5.2.4.2 COMUNICAZIONE TRA CLIENT E GESTORE
Il ClientA richiede il nome del ClientB da ricercare. Successivamente manda un requestMsg al Gestore. Il messaggio del Client viene inviato al Gestore in chiaro tanto il Client non ha bisogno di qualificarsi perché richiede informazioni pubbliche, inoltre qualsiasi modifica al messaggio porterebbe allinvalidazione del messaggio da parte dello stesso Client o all invio di una chiave sbagliata, nel qual caso fallirebbe la procedura di riconoscimento da parte dell altro Client. Il Gestore comunica con una Copia per ottenere la chiave pubblica del Client richiesto (answerKey) e la invia codificata con la SUA (del Gestore) chiave privata al Client. Nel caso non sia possibile ottenere la chiave pubblica del Client il Gestore invia al richiedente un messaggio (sempre codificato con la sua chiave privata) indicante il motivo dellimpossibilità nel reperire la chiave ( nessuna Copia disponibile (notCopiaMsg), nome inesistente (notFindMsg) ).
Bisogna effettuare una distinzione tra il timestamp inviato al Gestore dal Client e quelli utilizzati tra Gestore e Copie. Il timestamp di ritorno inviato dal Gestore al Client deve coincidere con quello inviato dal Client. Allo stesso modo il Gestore genera un suo timestamp interno che viene man mano incrementato a seconda dei messaggi che invia, (o in certi casi viene creato in modo random) e questo viene utilizzato per le comunicazioni con le Copie.
5.2.4.3 COMUNICAZIONE TRA GESTORE E COPIE (per ottenimento chiave pubblica)
Una volta arrivata una richiesta e verificato che si tratta di una richiesta da parte di un Client, il Gestore deve decidere a quale Copia inviare la richiesta. Il protocollo utilizzato è, come abbiamo già detto, UDP. Il Gestore consulta la lista in suo possesso contenente le Copie in funzione, le richieste vengono inoltrate ciclicamente a tutte le Copie in modo che il lavoro sia suddiviso equamente e non si creino colli di bottiglia. Determinato lindirizzo e la porta di ascolto della Copia (tale operazione è stata resa esclusiva in modo che più richieste non vengano inoltrate contemporaneamente alla stessa Copia mentre altre Copie rimangono senza lavoro), il Gestore invia un messaggio di richiesta (requestKey) in forma di datagramma alla Copia. In caso non si riceva risposta entro un tempo prefissato (15 sec) scatta un messaggio di timeout (timeoutMsg) e il messaggio viene riinoltrato ALLA STESSA Copia. Nel caso la richiesta fallisca ancora la Copia viene considata inaffidabile e il suo indirizzo viene cancellato dalla lista mantenuta dal Gestore. Nel caso non ci siano Copie attive o le Copie presenti non rispondano viene inviato un messaggio indicante lassenza di Copie in grado di fornire il servizio (notCopiaKey). In tutti gli altri casi la Copia risponde fornendo le informazioni richieste (answerKey), o indicando lassenza del Client richiesto nella tabella (notFindKey) o avvisando che il messaggio ricevuto ha subito modifiche (refuseKey).
5.2.4.4 COMUNICAZIONE TRA COPIA E GESTORE (per la richiesta della tabella delle chiavi pubbliche)
Il primo passo svolto dalla Copia dopo la registrazione su una porta libera è linvio di una richiesta al Gestore. Nella realtà la Copia invia al Gestore il suo indirizzo (inteso come indirizzo IP e porta di ascolto è indirizzo); a questo punto il Gestore è pronto per inviare la tabella delle chiavi pubbliche. Vengono quindi inviati, in sequenza, una serie di oggetti entryKey contenenti un oggetto entry e la signature delloggetto stesso. Il protocollo utilizzato per l invio della tabella parte con:
Mediante questo scambio di messaggi siamo certi della consistenza della tabella e dellautenticità del mittente.
Il progetto è stato realizzato cercando di seguire fedelmente le specifiche, ma in fase di programmazione certi aspetti sono stati lievemente modificati.
Nel caso cada il master il servizio viene comunque reso possibile dai gestori Slave. Naturalmente il Gestore quando torna in funzione non può partire da zero, ma deve conoscere la lista delle Copie attualmente attive (si trova in una situazione duale a quella di un nuovo Gestore che deve aggiornarsi presso il master). Poiché abbiamo detto che questa distinzione tra gestori Master e Slave è solo virtuale, e il DNS conosce già il suo indirizzo, può aggiornarsi come Gestore secondario facendo richiesta a un Gestore Slave (in questo modo lo Slave funziona virtualmente da Master e passa la sua lista al nuovo entrato, che così può iniziare a lavorare; la lista che si vedrà passare avrà se stesso in testa).
La caduta di uno slave non causa invece preoccupazioni, nel momento in cui verrà riavviato inizierà la sua normale procedura di aggiornamento presso il Master. Manteniamo sempre lipotesi di guasto singolo, inoltre ricordiamo che per convenzione il primo Gestore che deve essere attivato è sempre il Master. Quindi laggiornamento dei server slave rimane comunque successivo al boot del server definito come master e fa riferimento a lui (ciò non toglie che in linea di principio uno slave potrebbe richiedere laggiornamento ad un altro slave; si è comunque preferito inoltrare queste richieste, a meno di gravi evenienze, sempre al master).
Nel caso cadano 1 o più (anche tutte) Copie i gestori se ne accorgono e invalidano gli indirizzi in loro possesso fino al momento in cui non è più possibile fornire il servizio.