Criptografia: Bastão de Licurgo (scytale) em Python

O Bastão de Licurgo ou scytale (σκυτάλη, bastão) ou cítala é uma técnica de cifragem utilizada pelos soldados espartanos, seu uso foi explicado por Plutarco em sua obra Vidas Paralelas.

O processo consistia em se enrolar uma tira de tecido sobre um bastão de largura e comprimento definidos e sobre esta tira escrevia-se a mensagem no sentido do comprimento do bastão. Finda a mensagem, a tira era desenrolada e enviada como um cinto por um mensageiro.

Scytale

No destino a tira devia ser enrolada num bastão de largura e comprimento igual ao qual a mensagem foi escrita; sendo o bastão da mesma largura e comprimento a mensagem se revelava. O algoritmo da cifra, neste caso, é o enrolar da tira no bastão e a chave, sua largura e comprimento.

Uma forma de visualizar a distribuição da mensagem é transpô-la para uma tabela. Para isso, dividimos o tamanho da mensagem pelo número de linhas (o que equivale à sua largura) e a quantidade de colunas ao número de voltas no bastão. Tomemos como exemplo a mensagem: ‘o ataque será realizado amanhã‘. Desprezando-se os espaços em branco e trocando-se os caracteres especiais, teremos a seguinte mensagem a ser cifrada: ‘oataqueserarealizadoamanha‘. Adotei como chave para a cifra, a escolha do número de colunas (ou seja, ao comprimento do bastão). Sendo o tamanho do texto de 26 caracteres e o comprimento do bastão (chave) 7, e a divisão não sendo inteira, o número de linhas deve ser 4, assim:

  (preenchimento)
→ |o|a|t|a|q|u|e|
  |s|e|r|a|r|e|a|
  |l|i|z|a|d|o|a|
  |m|a|n|h|a| | |
(cifragem)
↓ |o|a|t|a|q|u|e|
  |s|e|r|a|r|e|a|
  |l|i|z|a|d|o|a|
  |m|a|n|h|a| | |

Perceba que o preenchimento da tabela se dá da esquerda para a direita e de cima para baixo, mas a sua cifragem se faz de cima para baixo e da esquerda para a direita. Assim, o texto cifrado será:

OSLMAEIATRZNAAAHQRDAUEOEAA

CÓDIGO

Abaixo os códigos da classe Cipher e Scytale em Python:

# -*- coding: utf-8 -*-
from random import shuffle
""" Classe com metodos basicos para cifras classicas """

class Cipher(object):
    """ Classe base para as cifras classicas """
    plain_alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    plain_alphanum = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

    def format_str(self, text):
        '''Retorna text sem espacos e em maiusculas'''
        return text.replace(' ', '').upper()

    def shift_alphabet(self, alphabet, shift):
        '''Retorna alphabet com deslocamento de valor shift'''
        return alphabet[shift:] + alphabet[:shift]

    def create_square(self, alphabet = [], key = '', alphanum = False, replace = ['J', 'I'], sequence = False):
        """ Retorna um alfabeto numa matriz de num x num
        Por padrao, retorna uma matriz formada pelo alfabeto ABCDEFGHIKLMNOPQRSTUVWXYZ
        Se key, retorna um square com key iniciando o square
        alphanum square com letras e numeros
        replace letras a serem trocadas, so funciona se for usado somente o alfabeto
        sequence se True continua a preencher o square a partir do ultimo caracter da key
        """
        square = []
        if alphabet:
            if alphanum:
                replace = ['', '']
            # num = 5
            alfabeto = alphabet
        elif alphanum:
            # num = 6
            alfabeto = self.plain_alphanum
            replace = ['', '']
        else:
            # num = 5
            alfabeto = self.plain_alphabet
        alfabeto = self.create_alphabet(key.upper(), alfabeto, replace, sequence)##
        num = 5 + len(alfabeto) % 5
        for idx in range(0, len(alfabeto), num):
            square.append(alfabeto[idx:idx + num])
        return square

    def create_alphabet(self, key = '', alfabeto = plain_alphabet, replace = ['', ''], sequence = False):
        """ Retorna um alfabeto com key como chave e no inicio do alfabeto """
        if key:
            key = self.key_repeated(key)
            if replace[0] in key:
                key = key.replace(replace[0], replace[1])
            if sequence:
                idx = alfabeto.index(key[-1])
                alfabeto = self.shift_alphabet(alfabeto, idx)
        cipher = alfabeto.replace(replace[0], '')
        for ch_key in key:
            if ch_key in cipher:
                cipher = cipher.replace(ch_key, '')
        return key + cipher

    def random_alphabet(self, alphanum=False):
        """ Retorna um alfabeto aleatório """
        if alphanum:
            alfabeto = list(self.create_alphabet(alfabeto=self.plain_alphanum))
        else:
            alfabeto = list(self.create_alphabet())
        shuffle(alfabeto)
        return ''.join(alfabeto)

    def key_repeated(self, key):
        ''' Remove caracteres repetidos da senha key '''
        temp = ''
        for ch in key.upper():
            if ch not in temp:
                temp += ch
        return temp
# -*- coding: utf-8 -*-
from cipher import Cipher
""" Implementacao da Cifra Scytale """

class Scytale(Cipher):
    def encrypt(self, plaintext, key):
        ''' Cifra o texto com a cifra scytale utilizando a chave key. '''
        square = []
        plaintext = self.format_str(plaintext)
        idx = 0
        lines = len(plaintext) // key
        if len(plaintext) % key:
           lines += 1
        for lin in range(lines):
            square.append([])
            for col in range(key):
                if len(plaintext) > idx:
                    square[lin].append(plaintext[idx])
                else:
                    square[lin].append('')
                idx += 1
        ciphertext = ''.join([square[lin][col] for col in range(key) for lin in range(lines)])
        return ciphertext

    def decrypt(self, ciphertext, key):
        ''' Decifra o ciphertext com a cifra Scytale usando a chave key. '''
        idx = 0
        ciphertext = self.format_str(ciphertext)
        lines = len(ciphertext) // key
        if len(ciphertext) % key:
            lines += 1
        # quantidade de colunas completas
        spaces = len(ciphertext) % key
        square = [['' for col in range(key)] for lin in range(lines)]
        for col in range(key):
            for lin in range(lines):
                if len(ciphertext) > idx:
                    if not spaces and lin >= lines - 1:
                        # executa caso a coluna ao seja completa
                        square[lin][col] = ''
                        idx -= 1
                    else:
                        square[lin][col] = ciphertext[idx]
                idx += 1
            if spaces:
                spaces -= 1
        plaintext = ''.join([square[lin][col] for lin in range(lines) for col in range(key)])
        return plaintext

cipher.py
scytale.py

TESTES


>>> from scytale import Scytale
>>> Scytale().encrypt('o ataque sera realizado amanha', 7)
'OSLMAEIATRZNAAAHQRDAUEOEAA'
>>> Scytale().decrypt('OSLMAEIATRZNAAAHQRDAUEOEAA', 7)
'OATAQUESERAREALIZADOAMANHA'

Fontes:
Scytale – Wikipedia
http://www.numaboa.com.br/criptografia/transposicoes/322-bastao-de-licurgo
http://www.civilwarsignals.org/pages/crypto/cryptotl.html
The Skytale: An Early Greek Cryptographic Device Used in Warfare

Sobre Fábio Medeiros

Meu nome é Fábio Medeiros. Cearense de nascença e com muito orgulho (daí o nome do blog, uma referência à minha terra). Sou formado em Tecnologia em Telemática, pelo CEFET-CE. Escrevi alguns artigos sobre programação JavaME e dispositivos portáteis (PDA) para a revista WebMobile.
Esse post foi publicado em Criptografia e marcado , , . Guardar link permanente.

Uma resposta para Criptografia: Bastão de Licurgo (scytale) em Python

  1. Pingback: Criptografia: cifras de transposição e cifras de substituição | siriarah

Deixe um comentário