Ir al contenido principal

Criptografía (CCXLIV): La escítala espartana 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 la escítala espartana.

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

El cifrado utilizando la escítala se basa en sendas varas de grosor igual o similar que debían estar en posesión del emisor y del receptor del mensaje secreto.

Para cifrar un mensaje el emisor enrollaba una cinta de cuero en su vara y escribía el mensaje longitudinalmente en la cinta:

Después desenrollaba la cinta y se la enviaba al receptor del mensaje.

De esta forma el mensaje secreto "ESCITALAEJEMPLODECIFRADO" escrito en una cinta de cuero en la que en cada una de las vueltas de ésta sobre la vara se mostrasen tres filas, al ser desenrollada la cinta se leería: "EEESJCCEIIMFTPRALALODADO".

Para descifrar un mensaje el receptor enrollaba la cinta de cuero que había recibido en su vara y leía longitudinalmente la letras que se mostraban en la cinta:

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

Cifrar:

Se puede pensar en la escítala como una tabla o matriz de i filas y j columnas. En el ejemplo que se muestra en la figura anterior y que veremos más adelante al ejecutar el script, de 3 filas y 8 columnas.

El número de filas sería el grosor de la escítala, lo que puede considerarse como la clave; tanto emisor como receptor tienen que tener una vara del mismo o similar grosor, es decir, deben compartir la misma clave (cifrado simétrico), para que el mensaje secreto pueda comunicarse del primero al segundo con éxito.

Para cifrar basta con transponer la tabla o matriz que resulta de escribir longitudinalmente el texto en claro en la cinta de cuero enrollada en la escítala, o lo que es lo mismo intercambiar las filas por columnas, y leer el resultado por filas. El número de columnas se calcula a partir de la longitud del texto en claro y del grosor (número de filas): si el resto de dividir la longitud del texto plano entre el grosor de la escítala es cero, el número de columnas será el cociente de dicha división; si el resto es diferente de cero, el número de columnas será el cociente de dicha división más 1.

En el ejemplo anterior: como resto(longitud texto plano / grosor escítala) = resto(24 / 3) = 0, entonces número de columnas = cociente(longitud texto plano / grosor escítala) = cociente(24 / 3) = 8.

Lo que en el script se implementa mediante el cálculo indicado en el párrafo anterior y dos bucles anidados:

    if len(texto_claro)%int(grosor)==0:
        longitud=len(texto_claro)//int(grosor)
    else:
        longitud=len(texto_claro)//int(grosor)+1
    texto_claro=texto_claro.ljust(int(grosor)*longitud)
    j=0
    for i in range(0,int(longitud)):
        for j in range(j,len(texto_claro),int(longitud)):
            criptograma+=texto_claro[j]
        j=i+1

Descifrar:

Para descifrar basta con transponer la tabla o matriz transpuesta que se obtuvo en el cifrado, es decir transponer una tabla (intercambiar filas por columnas) con un número de columnas igual que el grosor de la escítala y en la que se incluyen, de la primera fila a la última y dentro de cada fila de la primera columna a la última, todos los caracteres del criptograma, y leer el resultado por filas.

Lo que en el script se implementa mediante dos bucles anidados:

    j=0
    for i in range(0,int(grosor)):   
        for j in range(j,len(criptograma),int(grosor)):
            texto_claro+=criptograma[j]
        j=i+1

Script python del cifrado utilizando la escítala espartana:

El script es el siguiente: 

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

# LA ESCÍTALA ESPARTANA:
#
# Cifra y descifra textos en claro y criptogramas, respectivamente,
# utilizando la escítala espartana.
#
# http://mikelgarcialarragan.blogspot.com/

import re
from unicodedata import normalize

# FUNCIÓN DE CIFRADO:
def cifrar(texto_claro, grosor):
    criptograma = ''
    if len(texto_claro)%int(grosor)==0:
        longitud=len(texto_claro)//int(grosor)
    else:
        longitud=len(texto_claro)//int(grosor)+1
    texto_claro=texto_claro.ljust(int(grosor)*longitud)
    j=0
    for i in range(0,int(longitud)):
        for j in range(j,len(texto_claro),int(longitud)):
            criptograma+=texto_claro[j]
        j=i+1
    return criptograma

# FUNCIÓN DE DESCIFRADO:
def descifrar(criptograma, grosor):
    texto_claro = ''
    j=0
    for i in range(0,int(grosor)):   
        for j in range(j,len(criptograma),int(grosor)):
            texto_claro+=criptograma[j]
        j=i+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 introducen el texto en claro y el grosor de la escítala (número de filas).
            # Se convierten los caracteres del texto en claro a mayúsculas y se eliminan de él
            # los espacios, las tildes, diéresis, etc.
            texto_claro = grosor = "*"
            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 grosor.isnumeric():
                        grosor = input('Grosor de la escítala (número de filas): ')
                        if grosor.isnumeric():
                            print ("[+] Grosor de la escítala (número de filas):", grosor)
                            criptograma = cifrar(texto_claro, grosor)
                            print ("[+] Criptograma:", criptograma)
                        else:
                            print ("*** ERROR: El grosor de la escítala (número de filas) debe ser numérico.")
                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 el grosor de la escítala (número de filas).
            # Se convierten los caracteres del criptograma a mayúsculas y se eliminan de él
            # los espacios, las tildes, diéresis, etc.
            criptograma = grosor = "*"
            while not criptograma.isalpha():
                criptograma = input('Criptograma a descifrar: ').upper()
                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)
                criptograma = criptograma.replace(' ','ñ')
                if criptograma.isalpha():
                    criptograma = criptograma.replace('ñ',' ')
                    print ("[+] Criptograma a descifrar:", criptograma)
                    while not grosor.isnumeric():
                        grosor = input('Grosor de la escítala (número de filas):')
                        if grosor.isnumeric():
                            print ("[+] Grosor de la escítala (número de filas):", grosor)
                            texto_claro = descifrar(criptograma, grosor)
                            print ("[+] Texto en claro:", texto_claro)
                            criptograma = criptograma.replace(' ','')
                        else:
                            print ("*** ERROR: El grosor de la escítala (número de filas) debe ser numérico.")
                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