Per svolgere questo esercizio sono state utilizzate le seguente librerie (che devono essere installate, prima di eseguire il codice):
numpy
sympy
L’obiettivo di questo esercizio era quello di implementare il Cifrario di Hill in modo che permettesse all’utente, utilizzando l’alfabeto inglese “ABCDEFGHIJKLMNOPQRSTUVWXYZ”, di cifrare, decifrare e di eseguire un attacco known plaintext.
Una volta eseguito il codice, l’utente può scegliere tra una lista di funzioni disponibili che vengono stampate a schermo, semplicemente inserendo il numero corrispondente.
La funzione di Encryption consente di cifrare un messaggio inserito dall’utente (plaintext) in base ad una chiave (key) anche essa inserita dall’utente. Tali input sono presi tramite il metodo:
def get_text_input(message, alphabet):
'''some code'''
Il plaintext e la chiave devono rispettare l’alfabeto inglese e non devono contenere spazi, numeri o punteggiatura. La chiave inoltre deve avere una lunghezza quadrata, questa viene controllata tramite la funzione:
def is_square(key):
'''some code'''
Una volta inseriti i due input, la funzione stampa a schermo le matrici estrapolate dal plaintext e dalla chiave inserita, tramite le funzioni:
def get_key_matrix(key, alphabet):
'''some code'''
def get_text_matrix(text, m, alphabet):
'''some code'''
Tali matrici contengono i numeri che corrispondono alla codifica delle lettere in base all’alfabeto scelto. Nel nostro caso:
Questa mappatura da lettere a numeri viene fatta tramite la funzione:
def get_from_letters_to_numbers():
'''some code'''
return alphabet
Dopo questi passaggi, la funzione procede alla cifratura del plaintext tramite la funzione:
def encrypt(key, plaintext, alphabet):
'''some code'''
return ciphertext
che ritorna la matrice del ciphertext contenente i numeri.
Per riscrivere in lettere il messaggio di ciphertext si utilizza:
def from_matrix_to_text(matrix, order, alphabet):
'''some code'''
return text
Questo metodo prende in ingresso come alfabeto, l’alfabeto inverso che mappa i numeri nelle lettere, ottenuto tramite la funzione:
def get_from_numbers_to_letters():
'''some code'''
return reverse_alphabet
In termini matematici l’encryption è definita come segue:
La funzione di Decryption consente di decifrare un messaggio inserito dall’utente (ciphertext) in base ad una chiave (key) anche essa inserita dall’utente. Tali input sono presi tramite il metodo:
def get_text_input(message, alphabet):
'''some code'''
Il ciphertext e la chiave devono rispettare l’alfabeto inglese e non devono contenere spazi, numeri o punteggiatura. La chiave inoltre deve avere una lunghezza quadrata, questa viene controllata tramite la funzione:
def is_square(key):
'''some code'''
Per la decryption è inoltre necessario controllare che la matrice relativa alla chiave sia invertibile e se è invertibile si deve calcolare la matrice inversa. Il metodo che nel codice esegue ciò è il seguente:
def get_inverse_matrix(matrix, alphabet):
'''some code'''
Quindi una volta inseriti i due input (ciphertext e chiave), la funzione stampa a schermo le matrici estrapolate dal ciphertext e dalla chiave inserita, tramite le funzioni:
def get_key_matrix(key, alphabet):
'''some code'''
def get_text_matrix(text, m, alphabet):
'''some code'''
Tali matrici contengono i numeri che corrispondono alla codifica delle lettere in base all’alfabeto scelto. Nel nostro caso:
Questa mappatura da lettere a numeri viene fatta tramite la funzione:
def get_from_letters_to_numbers():
'''some code'''
return alphabet
Dopo questi passaggi, la funzione procede alla decifratura del ciphertext tramite la funzione:
def decrypt(key_inverse, ciphertext, alphabet):
'''some code'''
che ritorna la matrice del plaintext contenente i numeri.
Per riscrivere in lettere il messaggio di plaintext si utilizza:
def from_matrix_to_text(matrix, order, alphabet):
'''some code'''
return text
Questo metodo prende in ingresso come alfabeto, l’alfabeto inverso che mappa i numeri nelle lettere, ottenuto tramite la funzione:
def get_from_numbers_to_letters():
'''some code'''
return reverse_alphabet
In termini matematici la decryption è definita come segue:
Funzione che consente all’utente di eseguire l’attacco di tipo known plaintext al Cifrario di Hill. Viene richiesto un plaintext, un ciphertext e la dimensione degli m-grammi (m) con cui testare l’attacco (ovvero m è la grandezza dei blocchi con cui sarà suddiviso il plaintext e il ciphertext).
Il numero di m-grammi generati dal plaintext e dal ciphertext deve essere lo stesso. L'utente deve quindi inserire un plaintext e un ciphertext di lunghezza appropriati altrimenti la funzione ritorna un messaggio di errore.
Quindi una volta inseriti i tre input (plaintext, ciphertext e m), la funzione stampa a schermo le matrici estrapolate dal ciphertext (C*) e dal plaintext (P*).
La matrice generata dal plaintext (P*) deve inoltre risultare invertibile altrimenti l'attacco non può andare a buon fine e dunque in quest'ultimo caso la funzione restituisce un messaggio di errore. Per controllare che sia invertibile e per calcolare la matrice inversa viene utilizzato il seguente metodo:
def get_inverse_matrix(matrix, alphabet):
'''some code'''
Una volta invertita la matrice, la funzione protrà procedere all’esecuzione dell’attacco. Questo viene fatto nel codice tramite il seguente metodo:
def known_plaintext_attack(ciphertext, plaintext_inverse, alphabet):
'''some code'''
Terminato l'attacco, se è andato a buon fine, la funzione stamperà la matrice il testo della chiave trovata. La conversione da numeri a lettere viene eseguita nella stessa maniera esposta nei punti precedenti.
Alcune osserazioni implementative:
- Se durante la creazione degli m-grammi l'ultimo blocco risulta parzialmente vuoto viene inserita un lettera 'Z' (Z=25) per ogni lettera mancante.
- Le matrici del ciphertext e del plaintext si leggono per colonna dall'alto verso il basso, mentre le matrici delle chiavi si leggono per riga da sinistra a destra.
In termini matematici l’attacco known plaintext è definito come segue:
Allora si costruiscono 2 matrici in questo modo:
L’attaccante dunque riesce ad ottenere la chiave K solo se la matrice P* è invertibile modulo 26.
To run the project:
python3 HillCipher.py