Ir al contenido principal

Criptografía (CCXLVI): Cifrado por transposición columnar doble 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 por transposición columnar doble.

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

El cifrado por transposición columnar doble consiste en realizar dos transposiciones columnares simples de forma consecutiva (ver el post anterior donde explico el cifrado y descifrado mediante transposición columnar simple e incluyo un script en python para ello), la segunda de ellas empleando o no la misma clave que en la primera.

Por tanto, sin mayores explicaciones, pongo el script correspondiente: 

Script python del cifrado por transposición columnar doble:

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

# CIFRADO POR TRANSPOSICIÓN COLUMNAR DOBLE:
#
# Cifra y descifra textos en claro y criptogramas, respectivamente,
# utilizando la transposición columnar doble.
#
# 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. Se convierten los caracteres a mayúsculas y
            # se eliminan de él los espacios, las tildes, diéresis, etc.
            texto_claro = clave1 = clave2 = "*"
            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)
                    # Se introduce la clave para la primera transposición. Se convierten los caracteres a mayúsculas y
                    # se eliminan de ella los espacios, las tildes, diéresis, etc.
                    while not clave1.isalpha():
                        clave1 = input('Clave primera transposición: ').upper()
                        clave1 = clave1.replace(' ','')
                        clave1 = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+",
                                      r"\1", normalize("NFD", clave1), 0, re.I)
                        clave1 = normalize("NFC", clave1)
                        if clave1.isalpha():
                            print ("[+] Clave primera transposición:", clave1)
                            criptograma = cifrar(texto_claro, clave1)
                            misma_clave = ""
                            while misma_clave !="S" and misma_clave !="N":
                                misma_clave = input('¿Utilizar la misma clave para la segunda transposición (S/N)?: ').upper()
                                if misma_clave == "N":
                                    # Se introduce la clave para la segunda transposición. Se convierten los caracteres a mayúsculas y
                                    # se eliminan de ella los espacios, las tildes, diéresis, etc.
                                    while not clave2.isalpha():
                                        clave2 = input('Clave segunda transposición: ').upper()
                                        clave2 = clave2.replace(' ','')
                                        clave2 = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+",
                                                      r"\1", normalize("NFD", clave2), 0, re.I)
                                        clave2 = normalize("NFC", clave2)
                                        if clave2.isalpha():
                                            print ("[+] Clave segunda transposición:", clave2)
                                        else:
                                            print ("*** ERROR: La clave para la segunda transposición sólo debe contener caracteres alfabéticos.")
                                elif misma_clave == "S":
                                    # Para la segunda transposición se utiliza la misma clave que en la primera.
                                    print ("[+] Clave segunda transposición igual que para la primera:", clave1)
                                    clave2 = clave1
                                else:
                                    misma_clave = ""                               
                            criptograma = cifrar(criptograma, clave2)
                            print ("[+] Criptograma:", criptograma)
                        else:
                            print ("*** ERROR: La clave para la primera transposición 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 introduce el criptograma. Se convierten los caracteres a mayúsculas y
            # se eliminan de él los espacios, las tildes, diéresis, etc.
            criptograma = clave1 = clave2 = "*"
            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)
                    # Se introduce la clave empleada en el cifrado para la segunda transposición. Se convierten los caracteres a mayúsculas y
                    # se eliminan de ella los espacios, las tildes, diéresis, etc.
                    while not clave2.isalpha():
                        clave2 = input('Clave empleada en el cifrado para la segunda transposición: ').upper()
                        clave2 = clave2.replace(' ','')
                        clave2 = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+",
                                      r"\1", normalize("NFD", clave2), 0, re.I)
                        clave2 = normalize("NFC", clave2)
                        if clave2.isalpha():
                            print ("[+] Clave empleada en el cifrado para la segunda transposición:", clave2)
                            texto_claro = descifrar(criptograma, clave2)
                            misma_clave = ""
                            while misma_clave !="S" and misma_clave !="N":
                                misma_clave = input('¿La clave que se utilizó en el cifrado para la segunda transposición fue la misma que para la primera (S/N)?: ').upper()
                                if misma_clave == "N":
                                    # Se introduce la clave que se empleó en el cifrado para la primera transposición. Se convierten los caracteres a mayúsculas y
                                    # se eliminan de ella los espacios, las tildes, diéresis, etc.
                                    while not clave1.isalpha():
                                        clave1 = input('Clave empleada en el cifrado para la primera transposición: ').upper()
                                        clave1 = clave1.replace(' ','')
                                        clave1 = re.sub(r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+",
                                                      r"\1", normalize("NFD", clave1), 0, re.I)
                                        clave1 = normalize("NFC", clave1)
                                        if clave1.isalpha():
                                            print ("[+] Clave empleada en el cifrado para la primera transposición:", clave1)
                                        else:
                                            print ("*** ERROR: La clave empleada en la primera transposición sólo debe contener caracteres alfabéticos.")
                                elif misma_clave == "S":
                                    # En el cifrado se utilizó la misma clave para la segunda transposición que para la primera.
                                    print ("[+] En el cifrado se utilizó la misma clave para la segunda transposición que para la primera:", clave2)
                                    clave1 = clave2
                                else:
                                    misma_clave = ""                               
                            texto_claro = descifrar(texto_claro, clave1)
                            print ("[+] Texto en claro::", texto_claro)
                        else:
                            print ("*** ERROR: La clave para la segunda transposición 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

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