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.
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
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
Pingback: Criptografia: cifras de transposição e cifras de substituição | siriarah