Il cifrario di PlayFair
« il: Luglio 20, 2007, 22:10 »
Salve ragazzi,
questo è il mio primo post nel nuovo forum (non che di là ne avessi scritti centinaia eh!! :) )
Allora, visto che sto preparando un esame di sicurezza molto incentrato sulla crittografia, questa sera vi propongo il famoso cifrario di PlayFair che ho implementato in Python.

Ve lo spiego brevemente:
si considera una matrice di 25 celle (5x5).

Si considera una password di tutte lettere distinte e la si comincia ad inserire per colonna nella matrice (a partire dalla prima riga, ovvio). Dopo aver inserito tale parola, che risulta essere la chiave privata, si inseriscono nella matrice tutti gli altri caratteri dell'alfabeto in ordine.

Ovviamente i caratteri dell'alfabeto inglese sono 26, quindi si decide di raggruppare in un'unica cella due caratteri che nella lingua in cui si intende effettuare cifratura e decifratura sono molto rari (da noi chessò w,k..).

La cifratura avviene suddividendo il testo da cifrare in coppie di caratteri, dopodichè su ogni coppia si applica una delle seguenti regole:
1 - se i due caratteri stanno sulla stessa riga della matrice allora entrambi i caratteri vengono sostituiti dai caratteri susseguenti ad uno shift a destra circolare nella matrice.
2 - se i due caratteri stanno sulla stessa colonna della matrice allora entrambi i caratteri vengono sostituiti dai caratteri susseguenti ad uno shift in basso circolare.
3 - se i due caratteri non sono nè sulla stessa riga, nè sulla stessa colonna, allora vengono sostituiti dai caratteri che formano con loro un rettangolo.

(spero di essere stato chiaro....)

Il mio codice:
import string

Metodo per la stampa di una matrice bidimensionale.
L'ho creata come lista di liste.

def printMatrix(aMat):
    for row in aMat:
        print row

Metodo per la generazione della chiave.
Data la chiave di input questo metodo appende alla
stringa tutti gli altri caratteri dell'alfabeto.
I caratteri meno usati nell'alfabeto italiano li ho
impostati io in 'special_chars'.

def genKey(aKey):
    special_chars = ('j','k','x','y')
   
    chars = []
   
    for x in special_chars:
        if x not in aKey:
            chars.append(x)

    charList = string.ascii_lowercase
   
    for i in charList:
        if i not in aKey and i != chars[1]:
            aKey += i
           
    return aKey, chars

Metodo che genera la matrice di PlayFair
def initMatrix(aKey):
    aMat = []
   
    long_key, special_chars = genKey(aKey)
   
    row = 0
    col = 0
   
    for i in long_key:
        if col == 0:
            aMat.append([])
            aMat[row].append(i)
            col += 1
           
        elif col == 5:
            col = 0
            row += 1
            aMat.append([])
            aMat[row].append(i)
            col += 1
                   
        else:
            aMat[row].append(i)
            col += 1
       
    return aMat, special_chars

Dato il plaintext da cifrare, questo metodo toglie
gli spazi. (Certo che in Python già ci sia...ma non so quale :sarcastic:)
Inoltre se il testo è di lunghezza dispari allora ci aggiungo un carattere
poco usato alla fine in modo da rendere la stringa pari.
Questo perchè la crittografia funziona a blocchi di 2 caratteri.

def trim(aString, aChar):
    retString = ''
   
    lowerString = aString.lower()
   
    splittedString = lowerString.split(" ")
    for i in splittedString:
        retString += i
       
    if len(retString) % 2 == 1:
        retString += aChar

    return retString

Dato un testo già trimmato ne estrae tutte le coppie.
def extractPairs(aText):

    blocks = []
    i = 0
    while i < len(aText):
        blocks.append([aText[i:i+1], aText[i+1:i+2]])
        i += 2
       
    return blocks

Questi due metodi definiscono le tre regole del
cifrario di PlayFair, un metodo è per la crittazione,
l'altro per la decrittazione.

def checkRulesEnc(aMat, aList, aPair):
   

    char1 = aPair[0]
    char2 = aPair[1]
   
    coord1 = []
    coord2 = []
   
    coord1_cipher = []
    coord2_cipher = []
   
    row_no = 0
    for row in aMat:
        if char1 in row:
            coord1.append(row_no)
            coord1.append(row.index(char1))
           
        if char2 in row:
            coord2.append(row_no)
            coord2.append(row.index(char2))
       
        row_no += 1
           
        #Se le righe sono uguali....
    if coord1[0] == coord2[0]:
        coord1_cipher.append(coord1[0])
        coord1_cipher.append( (coord1[1]+1) % 5)
        coord2_cipher.append(coord2[0])
        coord2_cipher.append( (coord2[1]+1) % 5)
       
        #Se le colonne sono uguali....   
    elif coord1[1] == coord2[1]:
        coord1_cipher.append( (coord1[0]+1) % 5)
        coord1_cipher.append(coord1[1])
        coord2_cipher.append( (coord2[0]+1) % 5)
        coord2_cipher.append(coord2[1])
           
        #Altrimenti....
    else:
        coord1_cipher.append(coord1[0])
        coord1_cipher.append(coord2[1])
        coord2_cipher.append(coord2[0])
        coord2_cipher.append(coord1[1])
           
    char1_cipher = aMat[coord1_cipher[0]][coord1_cipher[1]]
    char2_cipher = aMat[coord2_cipher[0]][coord2_cipher[1]]
   
    return char1_cipher+char2_cipher

def checkRulesDec(aMat, aList, aPair):
    char1 = aPair[0]
    char2 = aPair[1]
   
    coord1 = []
    coord2 = []
   
    coord1_cipher = []
    coord2_cipher = []
   
    row_no = 0
    for row in aMat:
        if char1 in row:
            coord1.append(row_no)
            coord1.append(row.index(char1))
           
        if char2 in row:
            coord2.append(row_no)
            coord2.append(row.index(char2))
       
        row_no += 1
           
        #Se le righe sono uguali....
    if coord1[0] == coord2[0]:
        coord1_cipher.append(coord1[0])
        coord1_cipher.append( (coord1[1]-1) % 5)
        coord2_cipher.append(coord2[0])
        coord2_cipher.append( (coord2[1]-1) % 5)
       
        #Se le colonne sono uguali....   
    elif coord1[1] == coord2[1]:
        coord1_cipher.append( (coord1[0]-1) % 5)
        coord1_cipher.append(coord1[1])
        coord2_cipher.append( (coord2[0]-1) % 5)
        coord2_cipher.append(coord2[1])
           
        #Altrimenti....
    else:
        coord1_cipher.append(coord1[0])
        coord1_cipher.append(coord2[1])
        coord2_cipher.append(coord2[0])
        coord2_cipher.append(coord1[1])
           
    char1_cipher = aMat[coord1_cipher[0]][coord1_cipher[1]]
    char2_cipher = aMat[coord2_cipher[0]][coord2_cipher[1]]
   
    return char1_cipher+char2_cipher

Metodi che lanciano la crittazione e la decrittazione.     
   
def encrypt(aText, aMat, charList):
    trimmedText = trim(aText, str(charList[len(charList)-1:len(charList)])[2:-2])
   
    print "Il testo sottoposto alla crittografia : %s " % trimmedText
    pairs_char  = extractPairs(trimmedText)
    cipher_txt = ''
   
    for pair in pairs_char:
        cipher_txt += checkRulesEnc(aMat, charList, pair)
       
    return cipher_txt

def decrypt(aText, aMat, charList):
    pairs_char  = extractPairs(aText)
    plain_txt = ''
   
    for pair in pairs_char:
        plain_txt += checkRulesDec(aMat, charList, pair)
       
    return plain_txt

key = 'matrice'
playFairMatrix, special_chars = initMatrix(key)

print "/-------------------------------------------------/"
print "I due caratteri in una sola cella sono : %c - %c" % (special_chars[0], special_chars[1])

print "La matrice di playfair e\' stata generata!"
print printMatrix(playFairMatrix)
print "/-------------------------------------------------/"

print "Pronta la fase di crittazione!!"
plainText = "Forza napoli forza ciao mamma"
print "Testo in chiaro : %s" % plainText
cipherText = encrypt(plainText, playFairMatrix, special_chars)

print "IL TESTO E' STATO CIFRATO"
print cipherText

plainText = decrypt(cipherText, playFairMatrix, special_chars)
print "IL TESTO E' STATO DECIFRATO"
print plainText

Ovviamente non è perfetto.
Mancano un paio di cose :
1° - quando il testo viene suddiviso in blocchetti, le coppie di caratteri
uguali nella stessa coppia dovrebbero essere spezzati da uno dei
caratteri rari.

2° - potevo pure mettere l'inserimento della chiave da riga di comando,
con un file col testo da cifrare, ma sono pigro :)

Se vi va, parliamone :)

Ciao,
Ennio