Criptografia: Cifra de Políbio ou Quadrado de Políbio em Python

A cifra de Políbio ou quadrado de Políbio, é um cifra de substituição relatada pelo historiador grego Políbio em seu livro Histórias, que trata da ascensão do Império Romano.

Seu princípio de funcionamento consiste numa tabela quadrada (mesmo número de linhas e colunas🙂 ), onde os caracteres são disponibilizados um em cada célula desta tabela. Políbio utilizava um quadrado de 5×5, ou seja, 25 células e utilizava o alfabeto grego de 24 caracteres. A última célula era preenchida com um sinal para sincronização.

Trazendo para nossos dias, utilizamos o mesmo quadrado de 5×5 e o alfabeto latino de 26 letras. Como há uma letra a mais, nos utilizaremos de um artifício em unir duas letras numa mesma célula. Em alguns sites, com conteúdo em inglês, costuma-se unir as letras i e j numa mesma célula, ficando o quadrado como demonstrado abaixo:

    1   2   3    4    5
1 | a | b | c |  d  | e |
2 | f | g | h | i/j | k |
3 | l | m | n |  o  | p |
4 | q | r | s |  t  | u |
5 | v | w | x |  y  | z |

O processo de cifragem se dá pela troca da letra do alfabeto normal pelo número da linha seguido pelo número da coluna, por exemplo, a letra a ficará 11, a b, 12 e assim por diante. Assim, a palavra guerra, cifrada, ficará: 22 45 15 42 42 11 ou 224515424211. O processo de decifragem é o inverso: pega-se o número da linha e da coluna e substitui-se pela letra correspondente na célula.

Por exemplo, o código 24 34 22 34 43 14 15 22 45 15 42 42 11, decifrado, fica iogosdeguerra (note que a palavra iogos deveria ser jogos, mas, como comprimimos as letras i e j na mesma célula, na decifragem utilizamos somente uma delas, neste caso, a letra i. E como as partes envolvidas na comunicação sabem deste detalhe, fica fácil para alternar entre as duas letras).

Uma outra forma de utilizar o quadrado de Políbio é expandi-lo para a utilização das letras do alfabeto e os numerais de 0 a 9. Assim, nosso quadrado para a ser de 6×6, ficando como mostrado abaixo:

    1   2   3   4   5   6
1 | a | b | c | d | e | f |
2 | g | h | i | j | k | l |
3 | m | n | o | p | q | r |
4 | s | t | u | v | w | x |
5 | y | z | 0 | 1 | 2 | 3 |
6 | 4 | 5 | 6 | 7 | 8 | 9 |

Desta forma, não precisamos ter dois caracteres numa mesma célula. Utilizando este quadrado a frase A senha de acesso ao sistema é 1945, cifrada, fica (lembrando que desconsiderei os espaços em branco e caracteres especiais): 11 41 15 32 22 11 14 15 11 13 15 41 41 33 11 33 41 23 41 42 15 31 11 15 54 66 61 62.

Para dificultar um pouco mais o trabalho de quem tenta decifrar nossa mensagem ultra-secreta🙂 , há a possibilidade de se utilizar uma palavra-chave com a cifra de Políbio. Se utilizarmos a palavra-chave cavalaria, nosso quadrado ficará como mostrado abaixo:

    1   2   3   4   5   6
1 | c | a | v | l | r | i |
2 | j | k | m | n | o | p |
3 | q | s | t | u | w | x |
4 | y | z | 0 | 1 | 2 | 3 |
5 | 4 | 5 | 6 | 7 | 8 | 9 |
6 | b | d | e | f | g | h |

Utilizamos o mesmo conceito aplicado na cifra de substituição simples. As letras repetidas da palavra chave são removidas, cavalaria fica cavlri, e preenchemos as demais células com as letras restantes do alfabeto, seguidas dos numerais de 0 a 9.

E, a mesma frase, A senha de acesso ao sistema é 1945, cifrada, fica: 12 32 63 24 66 12 62 63 12 11 63 32 32 25 12 25 32 16 32 33 63 23 12 63 44 56 51 52.

CÓDIGO

O código foi salvo no arquivo Polybius.py. Lembrando que caracteres especiais não são tratados e os espaços em branco são descartados.

class Polybius:
    def __init__(self):
        ## p_alphabet nao possui a letra j, que sera substituida
        ## pela letra i na cifragem e decifragem
        self.p_alphabet = 'abcdefghiklmnopqrstuvwxyz'
        self.p_alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789'

    def cipher_alphabet(self, password):
        ''' (str) -> str
        Retorna um alfabeto cifrado iniciado com
        o texto da palavra chave password
        '''
        c_alphabet = []
        p_alphabet = self.p_alphanum
        for ch in password:
            if ch not in c_alphabet:
                c_alphabet.append(ch)
                idx = p_alphabet.find(ch)
        p_alphabet = p_alphabet[idx:] + p_alphabet[:idx]
        for ch in p_alphabet:
            if ch not in c_alphabet:
                c_alphabet.append(ch)
        return ''.join(c_alphabet)

    def create_square(self, alphabet, lines):
        ''' (list, int) -> list of str
        Retorna uma tabela lista quadrada definida por lines
        com cada celula preenchida com um caracter de alphabet
        '''
        square = []
        temp = []
        count = 0
        for ch in alphabet:
            temp.append(ch)
            count += 1
            if count == lines:
                square.append(temp)
                temp = []
                count = 0
        return square

    def select_square(self, key):
        ''' (int) -> list of str
        Retorna o quadrado de Polibio referente a key utilizada.
        Se key for True, usa-se o quadrado de Polibio com letras e numerais;
        Se key for uma string, retorna o quadrado de Polibio com key como palavra chave.
        Se nao houver key, usa-se o quadrado de Polibio padrao.
        '''
        if key:
            if key == True:
                return self.create_square(self.p_alphanum, 6)
            else:
                return self.create_square(self.cipher_alphabet(str(key)), 6)
        return self.create_square(self.p_alphabet, 5)

    def encrypt(self, plaintext, key = None):
        ''' (str, str) -> str
        Retorna o plaintext cifrado com a cifra de Polibio.
        Se key = True, cifra com quadrado de Polibio com letras e numeros.
        Se key = uma string, cifra o quadrado de Polibio com key como palavra chave.
        Se nao houver key, cifra com o quadrado de polibio padrao.
        '''
        square = self.select_square(key)
        ciphertext = ''
        for ch in plaintext.lower():
            idx = 1
            if ch == 'j':
                ch = 'i'
            for linha in square:
                if ch in linha:
                    ciphertext += str(idx) + str(linha.index(ch) + 1) + ' '
                idx += 1
        return ciphertext

    def decrypt(self, ciphertext, key = None):
        ''' (str, str) -> str
        Retorna o ciphertext decifrado com a cifra de Polibio.
        Se key = True, decifra com quadrado de Polibio com letras e numeros.
        Se key = uma string, decifra o quadrado de Polibio com key como palavra chave.
        Se nao houver key, decifra com o quadrado de polibio padrao.
        '''
        square = self.select_square(key)
        plaintext = ''
        ciphertext = str(ciphertext).replace(' ', '')
        for num in range(0, len(ciphertext), 2):
            var = int(ciphertext[num:num + 2])
            i, j = var / 10, var % 10
            if i - 1 < len(square) and j - i < len(square[0]):
                plaintext += square[i - 1][j - 1]
        return plaintext.lower()

BitBin

TESTES

>>> from polybius import Polybius
>>> Polybius().encrypt('jogos de guerra')
'24 34 22 34 43 14 15 22 45 15 42 42 11 '
>>> Polybius().decrypt('24 34 22 34 43 14 15 22 45 15 42 42 11')
'iogosdeguerra'
>>> Polybius().decrypt(24342234431415224515424211)
'iogosdeguerra'
>>> Polybius().encrypt('Senha de acesso e 1986', True)
'41 15 32 22 11 14 15 11 13 15 41 41 33 15 54 66 65 63 '
>>> Polybius().decrypt('41 15 32 22 11 14 15 11 13 15 41 41 33 15 54 66 65 63', True)
'senhadeacessoe1986'
>>> Polybius().encrypt('Senha de acesso e 1986', 'cavalaria')
'32 63 24 66 12 62 63 12 11 63 32 32 25 63 44 56 55 53 '
>>> Polybius().decrypt('32 63 24 66 12 62 63 12 11 63 32 32 25 63 44 56 55 53',  'cavalaria')
'senhadeacessoe1986'

Fontes:
http://www.numaboa.com.br/criptografia/128-substituicao-tomografica/179-polibio
http://en.wikipedia.org/wiki/Polybius

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: Cifra de Políbio ou Quadrado de Políbio em Python

  1. Pingback: Criptografia | Método Científico

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s