Ir al contenido principal

Criptografía (CCXLII): Cifrado de Polibio en python

Continúo poniendo scripts de programación en python para automatizar tareas que tengan relación con la criptografía.

En este post incluyo y comento un script en python para el cifrado y descifrado utilizando el cifrado de Polibio.

Antes de poner el script, comentar cómo se cifraba y descifraba manualmente.

El cifrado de Polibio se basa en una tabla de 5 x 5 (5 filas y 5 columnas) cuyas celdas o casillas se completan, desde la primera a la última fila, y en cada una de ellas desde la primera a la última columna, con las letras del alfabeto (la "I" y la "J" comparten celda, y "Ñ" excluida), de la siguiente forma:

En el script las funciones de cifrado y descifrado se implementan de la siguiente manera:

Cifrar:

Para cifrar se sustituía cada carácter del texto en claro por el dígito de la fila y el dígito de la columna en cuya intersección se encontraba éste. Por ejemplo: la letra "E" se sustituía por 15.

En el script el cifrado se implementa de la siguiente manera:

str((alfabeto.find(caracter)//5)+1) + str((alfabeto.find(caracter)%5)+1)

alfabeto.find(caracter): el método find() encuentra la primera ocurrencia del valor especificado en una cadena de caracteres y devuelve la posición que ésta ocupa. La primera posición es 0 y si no se encuentra el valor devuelve -1.

En el script encuentra la ocurrencia del carácter del texto en claro en el alfabeto y devuelve su posición en el mismo (de 0 a 24).

Por ejemplo, si el carácter fuese la "E" devolvería 4.

- //: el operador // obtiene el cociente de dividir el operando 1 entre el operando 2.

%: el operador % obtiene el resto de dividir el operando 1 entre el operando 2.

(alfabeto.find(caracter)//5)+1): obtiene el primer dígito correspondiente al cifrado del carácter, que es el correspondiente a la fila en la que se halla éste.

Por ejemplo, si el carácter fuese la "E" devolvería el cociente de 4/5 más 1, es decir, 0 + 1 = 1.

(alfabeto.find(caracter)%5)+1): obtiene el segundo dígito correspondiente al cifrado del carácter, que es el correspondiente a la columna en la que se halla éste.

Por ejemplo, si el carácter fuese la "E" devolvería el resto de 4/5 más 1, es decir, 4 + 1 = 5.

La concatenación de estos dos último dígitos da como resultado el par de dígitos que le corresponden en el criptograma al carácter a cifrar. si el carácter fuese la "E" el par de dígitos sería 15.

Descifrar:

De forma recíproca, para descifrar se sustituía cada par de dígitos del criptograma por la letra que se encontraba en la intersección de la fila (primer dígito) y columna (segundo dígito). Por ejemplo: el par de dígitos 15 se sustituía por la "E".

En el script el descifrado se implementa de la siguiente manera:

str(alfabeto[((int(digito1)-1)*5) + (int(digito2)-1)])

alfabeto[((int(digito1)-1)*5) + (int(digito2)-1)]obtiene el carácter descifrado buscando el que se encuentra en el alfabeto en la posición que resulta de la suma del primer dígito que le correspondió en el cifrado menos 1 y multiplicado por 5 (número de columnas) más el segundo dígito que le correspondió en el cifrado menos 1.

Por ejemplo, si el par de dígitos de un carácter a descifrar es 15, ((1 - 1) * 5) + (5 - 1) = 0 + 4 = 4, que es la posición en la que se encuentra la letra "E" en el alfabeto.

Script python del cifrado de Polibio:

El script es el siguiente: 

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# CIFRADO DE POLIBIO
#
# Cifra y descifra textos en claro y criptogramas, respectivamente,
# utilizando el cifrado de Polibio.
#
# http://mikelgarcialarragan.blogspot.com/

import re
from unicodedata import normalize

# ALFABETO:
alfabeto = "ABCDEFGHIKLMNOPQRSTUVWXYZ"

# FUNCIÓN DE CIFRADO:
def cifrar(texto_claro):
    criptograma = ''
    for caracter in texto_claro:
        criptograma = criptograma + str((alfabeto.find(caracter)//5)+1) + str((alfabeto.find(caracter)%5)+1)
    return criptograma

# FUNCIÓN DE DESCIFRADO:
def descifrar(criptograma):
    texto_claro = ''
    for i in range(0,len(criptograma)-1,2):
        digito1 = criptograma[i]
        digito2 = criptograma[i+1]
        texto_claro = texto_claro + str(alfabeto[((int(digito1)-1)*5) + (int(digito2)-1)])
    return texto_claro

# 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 introduce el texto en claro. Se convierten los caracteres a mayúsculas,
            # el carácter "J" se sustituye por "I" y la letra "Ñ" por "N", y se eliminan los espacios,
            # las tildes, diéresis, etc.
            texto_claro = "*"
            while not texto_claro.isalpha():
                texto_claro = input('Texto en claro a cifrar: ').upper()
                texto_claro = texto_claro.replace('J','I')
                texto_claro = texto_claro.replace('Ñ','N')
                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)
                    criptograma = cifrar(texto_claro)
                    print ("[+] Criptograma:", criptograma)
                else:
                    print ("*** ERROR: El texto en claro a cifrar sólo debe contener caracteres alfabéticos.")
        elif opcion == "2":
            print ("")
            print ("--- DESCIFRAR:")
            # Descifrar: Se introduce el criptograma.
            criptograma = "*"
            while not criptograma.isnumeric() or len(criptograma)%2 != 0:
                criptograma = input('Criptograma a descifrar: ')
                if criptograma.isnumeric():
                    for digito in criptograma:
                        if int(digito) < 1 or int(digito) > 5:
                            criptograma = "*"
                            break
                    if criptograma.isnumeric():
                        if len(criptograma)%2 == 0:
                            print ("[+] Criptograma a descifrar:", criptograma)
                            texto_claro = descifrar(criptograma)
                            print ("[+] Texto en claro:", texto_claro)
                        else:
                            print ("*** ERROR: El criptograma debe tener un número par de dígitos.")
                    else:
                        print ("*** ERROR: El criptograma a descifrar sólo debe contener dígitos del 1 al 5.")
                else:
                    print ("*** ERROR: El criptograma a descifrar sólo debe contener dígitos.")
        elif opcion == "3":
            print ("*** FIN ******************************************")
            salir = True
        else:
            print ("*** ERROR: Opción no válida.")
	
if __name__ == '__main__':
    main()

Lo ejecuto:

Cifrar:

Nótese que el script ha sustituido el carácter "j" que se ha introducido en el texto en claro a cifrar por "I", ya que "I" y "J" comparten la misma celda en el cuadrado de Polibio. Además, si se hubiera introducido la letra "Ñ" el script la habría sustituido por "N". 

Descifrar:

Comentarios

Entradas populares de este blog

Criptografía (I): cifrado Vigenère y criptoanálisis Kasiski

Hace unos días mi amigo Iñaki Regidor ( @Inaki_Regidor ), a quien dedico esta entrada :), compartió en las redes sociales un post titulado "Criptografía: el arte de esconder mensajes"  publicado en uno de los blogs de EiTB . En ese post se explican ciertos métodos clásicos para cifrar mensajes , entre ellos el cifrado de Vigenère , y , al final del mismo, se propone un reto consistente en descifrar un mensaje , lo que me ha animado a escribir este post sobre el método Kasiski  para atacar un cifrado polialfabético ( conociendo la clave descifrar el mensaje es muy fácil, pero lo que contaré en este post es la forma de hacerlo sin saberla ). El mensaje a descifrar es el siguiente: LNUDVMUYRMUDVLLPXAFZUEFAIOVWVMUOVMUEVMUEZCUDVSYWCIVCFGUCUNYCGALLGRCYTIJTRNNPJQOPJEMZITYLIAYYKRYEFDUDCAMAVRMZEAMBLEXPJCCQIEHPJTYXVNMLAEZTIMUOFRUFC Como ya he dicho el método de Vigenère es un sistema de sustitución polialfabético , lo que significa que, al contrario que en un sistema de

Criptografía (XXIII): cifrado de Hill (I)

En este post me propongo explicar de forma comprensible lo que he entendido sobre el cifrado de Hill , propuesto por el matemático Lester S. Hill , en 1929, y que se basa en emplear una matriz como clave  para cifrar un texto en claro y su inversa para descifrar el criptograma correspondiente . Hay tres cosas que me gustan de la criptografía clásica, además de que considero que ésta es muy didáctica a la hora de comprender los sistemas criptográficos modernos: la primera de ellas es que me "obliga" a repasar conceptos de matemáticas aprendidos hace mucho tiempo y, desgraciadamente, olvidados también hace demasiado tiempo, y, por consiguiente, que, como dice  Dani , amigo y coautor de este blog, me "obliga" a hacer "gimnasia mental"; la segunda es que, en la mayoría de las ocasiones, pueden cifrarse y descifrase los mensajes, e incluso realizarse el criptoanálisis de los criptogramas, sin más que un simple lápiz y papel, es decir, para mi es como un pasat

¿Qué significa el emblema de la profesión informática? (I)

Todas o muchas profesiones tienen un emblema que las representa simbólicamente y en el caso de la  informática: " es el establecido en la resolución de 11 de noviembre de 1977  para las titulaciones universitarias superiores de informática, y  está constituido por una figura representando en su parte central  un  núcleo toroidal de ferrita , atravesado por  hilos de lectura,  escritura e inhibición . El núcleo está rodeado por  dos ramas : una  de  laurel , como símbolo de recompensa, y la otra, de  olivo , como  símbolo de sabiduría. La  corona  será la  de la casa real  española,  y bajo el escudo se inscribirá el acrónimo de la organización. ". Veamos los diferentes elementos tomando como ejemplo el emblema del COIIE/EIIEO (Colegio Oficial de Ingenieros en Informática del País Vasco/ Euskadiko Informatikako Ingeniarien Elkargo Ofiziala ) . Pero no sólo el COIIE/EIIEO adopta el emblema establecido en dicha resolución, sino que éste se adopta también como im