Historia
A lo largo de los años se ha venido evidenciando la gran importancia de la criptografía para la evolución y el desarrollo del mundo desde sus inicios hasta la actualidad. Desde proteger nuestra información confidencial, hasta la protección y transmisión de mensajes de guerra son aplicaciones útiles que ha brindado la criptografía. Con la codificación de los cifrados ingenuos se comprenderá en el contexto histórico la relevancia que tuvo esta herramienta para dicha época. En contexto con el mundo moderno, se corroborará la importancia de utilizar un método de cifrado seguro con el fin de promover uno de los pilares de la seguridad de la información que es la confidencialidad a partir de la sencilla implemenetación de un cifrado antiguo muy reconocido denominado cifra de Vigenère.
Implementación
Se implementa el cifrado vigenere que fue roto por la máquina de babage en el lenguaje python. Se realiza el criptoanálisis obteniendo la llave para descifrar el mensaje automáticamente.
Se importa del paquete itertools el plugin cycle, con el fin de optimizar funciones que crean iteradores para recorrer de forma eficiente la lista donde se tiene almacenado el alfabeto y para otras utilidades tales como la función cycle que repite la llave automáticamente.
Zip es una función para reorganizar listas en python. Como parámetros admite un conjunto de listas. Lo que realmente hace es tomar el elemento iésimo de cada lista y unirlos en una tupla, después une todas las tuplas en una lista para manejar mas cómodamente la asignación de cada letra cifrada con su correspondiente letra en el alfabeto.
Se define el alfabético con que que va a trabajar el cifrado.
Hay una función para cifrar y otra para descifrar, iniciaremos explicando la función cifrar.
Esta recibe dos argumentos, la clave y el mensaje, una vez tenemos tanto el mensaje como la clave vinculadas en una lista en forma de tuplas, por medio de un ciclo se recorre la lista para calcular la letra correspondiente al alfabeto de babage, esto se logra realizar utilizando la siguiente función matemática en forma general:
Código
from functools import reduce
from itertools import cycle
string='''wubefiqlzurmvofehmymwtixcgtmpifkrzupmvoirqmmwozmpulmbnyvqqqmvmvjleimhfefnzpsdlppsdlpevqmw
cxymdavqeefiqcaytqowcxymwmsemefcfwyeyqetrliqycgmtwcwfbsmyfplrxtqyeexmruluksgwfptlrqaerlqvpmvyqycxtw
fqlmtelsfjpqehmozciwciwfpzslmaeziqvlqmzvppxawcsmzmorvgwwqszetrlqzpbjazvqiyxewwoiccgdwhqmmvowsgntjpf
ppaibiybjutwelqklllmdpyvacdcfqnzpifppksdvptidgxmqqvebmqalkezmgcvkuzkizbzliuammvz'''
gramLength=4 # Pattern Length
locations = []; #Lista para las posiciones de los repetidos
distances = []; # Espaciamientos de los repetidos
def longitudLlave(string,gramLength):
#Asiganacion del subpatron
for i in range(len(string)):
gram = string[i:i+gramLength];
# make sure we don't get stuck with shorties
if len(gram) < gramLength: break #Validacion final del subpatron
locations.append([]); #Inicializa una lista dentro de una lista vacia.
find = string.find(gram,0); # PENDIENTE
while find != -1:
locations[i].append(find); #Cuando encuentre un patron repetitivo lo añade a lista
find = string.find(gram,find+1); #Desplazamiento del subpatron
if len(locations[i]) > 1: #SI hay dos o mas repeticiones, se calcula la distancia
for j in range(1,len(locations[i])):
distances.append(locations[i][j] - locations[i][0]) #Hallar la distancia Yf - Yi
miMcd = McdArray(distances); # MCD de las distancias
return miMcd # Tamaño de la llave
print(distances) #
def mcd(a,b):# calcular MCD
while b != 0:
t = b;
b = a % b;
a = t;
return a
def McdArray(mcdz):# Si recibe un array y lo calcula
""" Takes the greatest common divisor of an entire array """
if len(mcdz) < 2: return mcdz[0]; #El MCD es el mismo numero (Cuando el numero es indivisible)
miMcd = mcd(mcdz[0],mcdz[1]); #Se empieza a hallar el MCD con los dos primeros numeros, con el mod
for i in range(2,len(mcdz)): # Es el mismo de arriba, pero con las posiciones siguientes hasta el final
miMcd = mcd(mcdz[i],miMcd);
return miMcd;
def separarString(cipherText,longitudLlave): #Se debe separar el texto cifrado en subtextos del tamaño de la llave.
freqList=[] #Lista donde se guardan las divisiones del texo cifrado
for i in range(longitudLlave):freqList.append(''); # se inicializa la llave con strings vacios, con el fin de que la reconozca como string y no como int
for i in range(len(cipherText)):
index=i%longitudLlave #Correspondencia del texto cifrado con su letra del alfabeto que se cifro.(6%5= ) Toma la letra del texto cifrado y la pone en las posiciones donde deben estar para poderlas separar como las pide babage, se calcula el indice para saber donde se acomoda"""
freqList[index]+=cipherText[i] #Aca se acomodan letras de acuerdo al indice calculado anteriormente
return freqList # parte dividida del texto
def calcProb(freqList):
cNums = map(chr, range(97, 123)) #Se crea un array con ciertos tipos de datos, array de caracteres del rango 97 al 123 que son las letras minusculas en ASCII
prob = [] #Vector de vectores vacio
f = range(len(cNums)) #Lista que contiene las letras en ascii
p = [.082,.015,.028,.043,.127,.022,.020,.061,.070,.002,.008,.040,.024,.067,.075,.019,.001,.060,.063,.091,.028,.010,.023,.001,.020,.001]; #Probabilidad de ocurrencia del letras en el idioma ingles
for i in range(len(cNums)):
f[i] = freqList.count(cNums[i]) # la cantidad de letras que hay en cada posicion de freq list, parte divididda del texto count( letra que se repite) Para cada subcadena del ciphertext
prob.append(0) #
print (f)
#parte clave
for g in range(len(cNums)): #Para recorrer el vector de probabilidad (prob)
#print g
for i in range(len(cNums)): #Para moverse en el vector de probabilidades del idioma ingles (p)
#print i
fig = f[(i+g)% len(f)] #COmo una sumatoria de la prob del acento ingles por (OPERACION CLAVE)
prob[g] += ((p[i]*fig) / float(len(freqList))) #(OPERACION CLAVE)
#print "pos",(i+g)% len(f)
#print "prob",prob
#print(p[i])
return prob; #retorna el vector de probabilidad
def sugKey(prob): # EL metodo que encuentra la llave, da una llave dependiendo de las probabilidades mas altas que hayan en cada vector de vectores
""" Suggestes a key, given the maximum likelihood """
cNums = map(chr, range(97, 123))
i = max(prob) # encuentra el numero mas grande en un array
return cNums[prob.index(i)] #devuelve la letra en la posicion i
def criptoAnalisis(string,n):
y = separarString(string,n) # se separa en partes de acuerdo al tamaño de la llave
probTotal = [] #Probabilidades finales
for i in range(n):
m = calcProb(y[i]) #Llama al metodo para calcular las probabilidades
probTotal.append(m)
print (probTotal)
# suggest the key
key = "";
for i in range(n):
key += sugKey(probTotal[i]) #COrrespondencia de la letra que tiene mayor probabilidad en el texto cifrado con la de mayor probabilidad del ifioma ingles
return key; #Retorna la anhelada llave
def decrypt(ciphermessage, key):
pairs = zip(ciphermessage, cycle(key))
cNums = map(chr, range(97, 123)) # Crea una lista con los elementos que uno le mande en los argumentos
result = ''
for i in pairs:
total = reduce(lambda x, y: cNums.index(x) - cNums.index(y), i) # DOnde se evalua la operacion rara con el mod
result += cNums[total % 26] # operacion de desencriptado de vigenere
return result
v=longitudLlave(string,gramLength)
print (v)
x=separarString(string,v)
a=criptoAnalisis(string,v)
print ("llave:",a)
dec=decrypt(string,a)
print (dec)
Nota* (Este script fue realizado con python 2)