En este post incluyo y comento un script en python para el cifrado y descifrado por transposición columnar simple.
Antes de poner el script, comentar cómo se cifraba y descifraba manualmente.
El cifrado por transposición columnar simple se basa en una tabla o matriz cuyo número de columnas viene determinado por el tamaño o longitud de la clave de cifrado.
Para cifrar: el texto en claro se disponía en dicha tabla o matriz, desde la fila situada más arriba hasta la de más abajo, y dentro de cada una de ellas desde la columna ubicada más a la izquierda hasta la de más a la derecha, un carácter debajo de cada letra de la clave. Posteriormente, se ordenaban las columnas conforme al orden alfabético de las letras de la clave (si hay letras repetidas el orden se establecía de izquierda a derecha: primero la columna de una misma letra situada más a la izquierda, después la siguiente de esa misma letra más a la izquierda y así sucesivamente) y, finalmente, se leía el resultado obtenido por columnas.
Pongo como ejemplo el cifrado del mensaje que veremos más adelante al ejecutar el script:
De esta forma, en el cifrado del texto en claro "EJEMPLOCIFRADO" se obtendría el criptograma: "ECDELRPFJOAMIO".
Y para descifrar un criptograma simplemente se actuaba de forma inversa:
En el script, las funciones de cifrado y descifrado se implementan de la siguiente manera:
- Cifrar:
- len(): la función len() devuelve el número de items de un objeto. Cuando el objeto es una cadena devuelve el número de caracteres de la misma.
En el script devuelve el número de caracteres del texto a cifrar y de la clave, en el ejemplo 14 y 5, respectivamente.
- %: el operador % realiza la división modular entre dos operandos, es decir, obtiene el resto de dividir el operando 1 entre el operando 2.
- //: el operador // obtiene el cociente entero de dividir el operando 1 entre el operando 2.
Calcula el número de filas de la tabla o matriz:
Si el resto de dividir la longitud del texto a cifrar entre la longitud de la clave es 0: el número de filas es el cociente de dicha división.
En caso contrario: el número de filas es el cociente entero de dicha división + 1.
En el ejemplo: como resto(longitud texto plano / longitud clave) = resto(14 / 5) = 4, entonces número de filas = cociente(longitud texto plano / longitud clave) + 1 = cociente(14 / 5) + 1 = 2 + 1 = 3.
for i in range(0,len(texto)):
texto_claro[i]=texto[i]
Inicializa con espacios una lista en la que se va a incluir el texto en claro a cifrar. Tendrá tantos elementos como número de filas * número de columnas (longitud clave), y se incluyen en ella los caracteres del texto en claro.
En el ejemplo:
['E', 'J', 'E', 'M', 'P', 'L', 'O', 'C', 'I', 'F', 'R', 'A', 'D', 'O', ' ']
- clave_alfabetica_ordenada=sorted(clave): ordena alfabéticamente los caracteres de la clave y deja el resultado en una lista.
En el ejemplo:
['A', 'C', 'E', 'L', 'V']
for i in range(0,len(clave)):
clave_numerica.append(clave_alfabetica_ordenada.index(clave[i]))
Asigna un número a cada carácter de la clave en función de la posición que ocupa cada uno de ellos en la clave ordenada alfabéticamente.
En el ejemplo:
[1, 3, 0, 4, 2]
for j in range(0,i):
if clave_numerica[i]==clave_numerica[j]:
clave_numerica[i]+=1
Asigna un valor ascendente de +1 al segundo y siguientes caracteres repetidos de una misma letra de la clave.
En el ejemplo (no hay caracteres repetidos en la clave):
[1, 3, 0, 4, 2]
- Y finalmente, el criptograma se obtiene mediante los dos siguientes bucles anidados:
for j in range(0,filas):
criptograma+=texto_claro[clave_numerica.index(i)+len(clave)*j]
En el ejemplo, el criptograma (del que se eliminan los espacios en blanco o "huecos") es:
"ECDELRPFJOAMIO".
- Descifrar:
Se implementa actuando de forma inversa a la del cifrado:
filas=len(texto)//len(clave)
columnas_completas=len(clave)
else:
filas=len(texto)//len(clave)+1
columnas_completas=len(texto)%len(clave)
columnas_incompletas=len(clave)-columnas_completas
De forma análoga que en el cifrado, se calcula el número de filas de la tabla o matriz, pero, además, se calculan también el número de columnas completas (sin espacios en su última fila o sin "huecos") que se produjeron en el proceso de cifrado del texto en claro, y, como consecuencia, el número de columnas incompletas (con espacios en su última fila o con "huecos") que se produjeron en dicho proceso, que es igual a la longitud de la clave menos el número de columnas completas.
Si el resto de dividir la longitud del criptograma a descifrar entre la longitud de la clave es 0: el número de filas es el cociente de dicha división y todas las columnas están completas, es decir, el número de columnas completas es igual que la longitud de la clave.
En caso contrario: el número de filas es el cociente entero de dicha división + 1 y el número de columnas completas es el resto de dividir la longitud del criptograma entre la longitud de la clave.
El número de columnas incompletas es igual a la longitud de la clave menos el número de columnas completas.
En el ejemplo: como resto(longitud criptograma / longitud clave) = resto(14 / 5) = 4, entonces número de filas = cociente(longitud criptograma / longitud clave) + 1 = cociente(14 / 5) + 1 = 2 + 1 = 3 y el número de columnas completas es el resto de dicha división: 4, y, como consecuencia, el número de columnas incompletas es igual a: longitud clave - número de columnas completas = 5 - 4 = 1.
- clave_alfabetica_ordenada=sorted(clave): ordena alfabéticamente los caracteres de la clave y deja el resultado en una lista.
En el ejemplo:
['A', 'C', 'E', 'L', 'V']
- clave_numerica=[]
for i in range(0,len(clave)):
clave_numerica.append(clave_alfabetica_ordenada.index(clave[i]))
Asigna un número a cada carácter de la clave en función de la posición que ocupa cada uno de ellos en la clave ordenada alfabéticamente.
En el ejemplo:
[1, 3, 0, 4, 2]
for j in range(0,i):
if clave_numerica[i]==clave_numerica[j]:
clave_numerica[i]+=1
Asigna un valor ascendente de +1 al segundo y siguientes caracteres repetidos de una misma letra de la clave.
En el ejemplo (no hay caracteres repetidos en la clave):
[1, 3, 0, 4, 2]
for i in range(-1,-columnas_incompletas-1,-1):
huecos.append((clave_numerica[i]*filas)+(filas-1))
huecos=sorted(huecos)
Calcula las posiciones del criptograma en las que se produjeron espacios o "huecos" en el proceso de cifrado y clasifica ascendentemente dichas posiciones.
En el ejemplo: en el proceso de cifrado sólo se produjo un hueco y éste se encuentra en la posición 8 del criptograma.
Incluye un espacio en cada posición con "hueco" del criptograma.
En el ejemplo:
"ECDELRPF JOAMIO"
criptograma[((i%filas)*len(clave))+(i//filas)]=texto_huecos[i]
Inicializa con espacios la lista en la que se va a incluir el criptograma a descifrar. Tendrá tantos elementos como número de filas * número de columnas (longitud clave), y se incluyen en ella los caracteres del criptograma.
En el ejemplo:
['E', 'E', 'P', 'J', 'M', 'C', 'L', 'F', 'O', 'I', 'D', 'R', ' ', 'A', 'O']
- Y finalmente, el texto en claro se obtiene mediante los dos siguientes bucles anidados:
for j in range(0,len(clave)):
texto_claro+=criptograma[clave_numerica[j]+len(clave)*i]
En el ejemplo, el texto en claro es:
"EJEMPLOCIFRADO"
- Script python del cifrado por transposición columnar simple:
El script es el siguiente:
#!/usr/bin/env python # -*- coding: utf-8 -*- # CIFRADO POR TRANSPOSICIÓN COLUMNAR SIMPLE: # # Cifra y descifra textos en claro y criptogramas, respectivamente, # utilizando la transposición columnar simple. # # http://mikelgarcialarragan.blogspot.com/ import re from unicodedata import normalize # FUNCIÓN DE CIFRADO: def cifrar(texto, clave): criptograma='' if len(texto)%len(clave)==0: filas=len(texto)//len(clave) else: filas=len(texto)//len(clave)+1 texto_claro=[" "]*filas*len(clave) for i in range(0,len(texto)): texto_claro[i]=texto[i] clave_alfabetica_ordenada=sorted(clave) clave_numerica=[] for i in range(0,len(clave)): clave_numerica.append(clave_alfabetica_ordenada.index(clave[i])) for i in range(1,len(clave)): for j in range(0,i): if clave_numerica[i]==clave_numerica[j]: clave_numerica[i]+=1 for i in range(0,len(clave)): for j in range(0,filas): criptograma+=texto_claro[clave_numerica.index(i)+len(clave)*j] return criptograma.replace(' ','') # FUNCIÓN DE DESCIFRADO: def descifrar(texto, clave): texto_claro='' if len(texto)%len(clave)==0: filas=len(texto)//len(clave) columnas_completas=len(clave) else: filas=len(texto)//len(clave)+1 columnas_completas=len(texto)%len(clave) columnas_incompletas=len(clave)-columnas_completas clave_alfabetica_ordenada=sorted(clave) clave_numerica=[] for i in range(0,len(clave)): clave_numerica.append(clave_alfabetica_ordenada.index(clave[i])) for i in range(1,len(clave)): for j in range(0,i): if clave_numerica[i]==clave_numerica[j]: clave_numerica[i]+=1 huecos=[] for i in range(-1,-columnas_incompletas-1,-1): huecos.append((clave_numerica[i]*filas)+(filas-1)) huecos=sorted(huecos) texto_huecos='' huecos_incluidos=0 for i in range(0,len(texto)): if i in huecos: texto_huecos+=' ' huecos_incluidos+=1 if columnas_incompletas-huecos_incluidos!=0: huecos[huecos_incluidos]=huecos[huecos_incluidos]-huecos_incluidos texto_huecos+=texto[i] criptograma=[" "]*filas*len(clave) for i in range(0,len(texto_huecos)): criptograma[((i%filas)*len(clave))+(i//filas)]=texto_huecos[i] for i in range(0,filas): for j in range(0,len(clave)): texto_claro+=criptograma[clave_numerica[j]+len(clave)*i] return texto_claro.replace(' ','') # MENÚ: # Se presenta el menú para que se seleccione una opción. def main(): salir = False while not salir: print ("") print ("*** MENÚ *****************************************") print ("1. Cifrar.") print ("2. Descifrar.") print ("3. Salir.") print ("") opcion = input("Por favor, seleccione una opción: ") if opcion == "1": print ("") print ("--- CIFRAR:") # Cifrar: Se introducen el texto en claro y la clave. Se convierten los caracteres a mayúsculas y # se eliminan de ambos los espacios, las tildes, diéresis, etc. texto_claro = clave = "*" while not texto_claro.isalpha(): texto_claro = input('Texto en claro a cifrar: ').upper() texto_claro = texto_claro.replace(' ','') texto_claro = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+", r"\1", normalize("NFD", texto_claro), 0, re.I) texto_claro = normalize("NFC", texto_claro) if texto_claro.isalpha(): print ("[+] Texto en claro a cifrar:", texto_claro) while not clave.isalpha(): clave = input('Clave: ').upper() clave = clave.replace(' ','') clave = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+", r"\1", normalize("NFD", clave), 0, re.I) clave = normalize("NFC", clave) if clave.isalpha(): print ("[+] Clave:", clave) criptograma = cifrar(texto_claro, clave) print ("[+] Criptograma:", criptograma) else: print ("*** ERROR: La clave sólo debe contener caracteres alfabéticos.") else: print ("*** ERROR: El texto en claro a cifrar sólo debe contener caracteres alfabéticos.") elif opcion == "2": print ("") print ("--- DESCIFRAR:") # Descifrar: Se introducen el criptograma y la clave. Se convierten los caracteres a mayúsculas y # se eliminan de ambos los espacios, las tildes, diéresis, etc. criptograma = clave = "*" while not criptograma.isalpha(): criptograma = input('Criptograma a descifrar: ').upper() criptograma = criptograma.replace(' ','') criptograma = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+", r"\1", normalize("NFD", criptograma), 0, re.I) criptograma = normalize("NFC", criptograma) if criptograma.isalpha(): print ("[+] Criptograma a descifrar:", criptograma) while not clave.isalpha(): clave = input('Clave: ').upper() clave = clave.replace(' ','') clave = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+", r"\1", normalize("NFD", clave), 0, re.I) clave = normalize("NFC", clave) if clave.isalpha(): print ("[+] Clave:", clave) texto_claro = descifrar(criptograma, clave) print ("[+] Texto en claro:", texto_claro) else: print ("*** ERROR: La clave sólo debe contener caracteres alfabéticos.") else: print ("*** ERROR: El criptograma a descifrar sólo debe contener caracteres alfabéticos.") elif opcion == "3": print ("*** FIN ******************************************") salir = True else: print ("*** ERROR: Opción no válida.") if __name__ == '__main__': main()
Lo ejecuto:
- Cifrar:
- Descifrar:
Comentarios
Publicar un comentario