Criptografia: cifra de Vigenère em Python

A cifra de Vigenère (atribuída equivocadamente a Blaise de Vigenère) foi descrita primeiramente pelo italiano Giovan Battista Bellaso, em 1553, em sua obra La cifra del. Sig. Giovan Batista Bellaso e por muito tempo foi considerada como le chiffre indéchiffrable (a cifra indecifrável) quando, em meados do século XIX,  Charles Babbage e Friedrich Kasiki encontraram um método de resolvê-la.

É uma cifra polialfabética e seu funcionamento vai lembrar um pouco a cifra de Trithemius, só que a cifra de Vigenère utiliza uma chave para cifrar uma mensagem, e um pouco da cifra de Della Porta, só que para cada letra da chave corresponde um alfabeto diferente.

    A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
--------------------------------------------------------
A - A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
B - B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A
C - C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B
D - D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C
E - E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D
F - F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E
G - G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F
H - H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G
I - I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H
J - J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I
K - K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J
L - L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K
M - M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L
N - N|O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M
O - O|P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N
P - P|Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O
Q - Q|R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P
R - R|S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q
S - S|T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R
T - T|U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S
U - U|V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T
V - V|W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U
W - W|X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V
X - X|Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W
Y - Y|Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X
Z - Z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y

O processo de cifragem utiliza uma chave que se não tiver o mesmo tamanho do texto a ser cifrado deve ser repetida até que o texto plano e a chave tenham o mesmo tamanho. Por exemplo, tomemos como texto plano há um espião entre nós e a chave hoplita:

texto plano - haumespiaoentrenos
      chave - hoplita

Veja que o texto plano tem 18 letras e a chave tem 7 letras, assim, vamos repetir a chave hoplita até chegarmos ao tamanho de 18 letras. Ficando assim:

texto plano - haumespiaoentrenos
      chave - hoplitahoplitahopl

Agora com a chave de tamanho igual ao texto plano começamos o processo de cifragem. Utilizando a primeira letra do texto plano (h) como coluna e a primeira letra da chave (h) como linha, encontramos na interseção a letra O, que é a letra cifrada de h com o alfabeto h.

  texto plano - haumespiaoentrenos
        chave - hoplitahoplitahopl
texto cifrado - O

A segunda letra do texto plano (a) utilizará o alfabeto da segunda letra da cifra (o), resultando na letra O novamente.

  texto plano - haumespiaoentrenos
        chave - hoplitahoplitahopl
texto cifrado - OO

O processo se repetirá para todas as letras do texto plano.

  texto plano - haumespiaoentrenos
        chave - hoplitahoplitahopl
texto cifrado - OOJXMLPPODPVMRLBDD

Neste nosso exemplo o texto plano há um espião entre nós será cifrado como OOJXMLPPODPVMRLBDD.

CÓDIGOS

Alterei a classe base Cipher (criada no post da cifra de Della Porta) e a salvei no arquivo cipher.py.

# -*- coding: utf-8 -*-
class Cipher(object):
    """ Classe base para as cifras classicas """
    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]

A classe Vigenere é a responsável pela cifragem e decifragem e está salva no arquivo vigenere.py.

# -*- coding: utf-8 -*-
from cipher import Cipher

class Vigenere(Cipher):
    """ Cifra de Vigenere """
    def __init__(self):
        self.plain = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

    def repeat_password(self, password, text):
        '''
        Repete a password ate o tamanho de text
        '''
        if len(password) < len(text):
            new_pass = password * (len(text)/len(password))
            if len(new_pass):
                new_pass += password[:len(new_pass)]
            return new_pass.upper()
        return password.upper()

    def encrypt(self, plaintext, password, decrypt=False):
        '''
        Cifra plaintext com a cifra de Vigenere
        Decifra se decrypt for True
        '''
        password = self.repeat_password(password, plaintext)
        plaintext = self.format_str(plaintext)
        textout = ''
        for idx, char in enumerate(plaintext.upper()):
            # indice da letra da cifra
            idx_key = self.plain.find(password[idx])
            # gera alfabeto cifrado
            c_alphabet = self.shift_alphabet(self.plain, idx_key)

            if decrypt:
                idx_p = c_alphabet.find(char)
                textout += self.plain[idx_p]
            else:
                idx_p = self.plain.find(char)
                textout += c_alphabet[idx_p]

        return textout

    def decrypt(self, ciphertext, password):
        '''
        Decifra ciphertext
        '''
        return self.encrypt(ciphertext, password, True)

TESTES

Para teste criamos o arquivo teste_vigenere.py que chama a classe Vigenere.

# -*- coding: utf-8 -*-
from vigenere import Vigenere
import sys

versao = sys.version_info[0]

if versao == 2:
	leitura = raw_input
elif versao == 3:
	leitura = input

txt_in = leitura('Texto a ser cifrado: ')
password = leitura('Senha: ')

cifra = Vigenere()
txt_cifrado = cifra.encrypt(txt_in, password)
print
print('Texto cifrado: {0}'.format(txt_cifrado))
print('  Texto plano: {0}'.format(cifra.decrypt(txt_cifrado, password)))

Vamos testar a cifra com a frase no dia dez o ataque sera pelo norte e senha armagedom.

Texto a ser cifrado: no dia dez o ataque sera pelo norte
Senha: armagedom

Texto cifrado: NFPIGHHNAAKMQAIVSDAGQLURRFFE
  Texto plano: NODIADEZOATAQUESERAPELONORTE

FONTES

http://pt.wikipedia.org/wiki/Cifra_de_Vigenère
http://en.wikipedia.org/wiki/Vigenère_cipher
http://www.numaboa.com.br/criptografia/cifras/substituicoes/polialfabeticas/506-vigenere

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.

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