Python Forum
Where is problem in decrypting ADFGVX cipher
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Where is problem in decrypting ADFGVX cipher
#1
Hello, when I want to encrypt a character that has 5 characters, but the key has only 4 characters (which is one character less), decryption doesn't work. If the key has the same number of characters or two characters less, then it works

import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QVBoxLayout, QWidget, QMessageBox, QTableWidget, QTableWidgetItem, QGridLayout, QPlainTextEdit
from PyQt6.uic import loadUi
from random import shuffle, choice
from itertools import product, accumulate
from numpy import floor, sqrt

class ADFGVXCipherApp(QMainWindow):
    def __init__(self):
        super().__init__()
        loadUi("gui.ui", self)  # Load the UI from the XML file


        self.encrypt_button.clicked.connect(self.encrypt_message)
        self.decrypt_button.clicked.connect(self.decrypt_message)
        self.alphabet_entry.textChanged.connect(self.update_alphabet_table)
        self.alphabet_entry.textChanged.connect(self.check_alphabet)

        self.display_adfgvx_pairs_button.clicked.connect(self.display_adfgvx_pairs)
        self.update_key_button.clicked.connect(self.update_key_table)
        self.display_decrypt_pairs_button.clicked.connect(self.display_decryption_adfgvx_pairs)

        self.decrypt_key_table_button.clicked.connect(self.decrypt_key_table)

        self.generate_alphabet_button.clicked.connect(self.generate_random_alphabet)

        self.alphabet_entry.textChanged.connect(self.validate_alphabet)

    ALLOWED_CHARACTERS = {
        'ADFGVX': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
        'ADFGX_CZ': 'ABCDEFGHIKLMNOPQRSTUVWXYZ',
        'ADFGX_EN': 'ABCDEFGHIJKLMNOPQRSTUVXYZ',
    }

    def prepare_input_message(self, input_message, mode):
        # Převeďte vstupní zprávu na velká písmena
        input_message = input_message.upper()

        # Pro režim ADFGX_CZ: Nahraďte 'J' za 'I'
        if mode == 'ADFGX_CZ':
            input_message = input_message.replace('J', 'I')

        # Pro režim ADFGX_EN: Nahraďte 'W' za 'V'
        if mode == 'ADFGX_EN':
            input_message = input_message.replace('W', 'V')

        input_message = input_message.replace(' ', 'QXQ')

        return input_message

    def validate_alphabet(self, text):
        # Get the current text from alphabet_entry
        current_text = self.alphabet_entry.text().upper()

        # Ensure that each character in the current text is allowed
        allowed_chars = self.ALLOWED_CHARACTERS[self.alphabet_mode_combobox.currentText()]
        valid_text = ''.join(char for char in current_text if char in allowed_chars)

        # Update the text in alphabet_entry with only valid characters
        self.alphabet_entry.setText(valid_text)


    def decrypt_key_table(self):
        try:
            # Získání klíče pro dešifrování z pole vstupního klíče
            decryption_key_text = self.decryption_key.text().upper()

            # Získání zašifrovaného textu z pole zašifrovaného vstupu
            ciphertext = self.ciphertext_entry2.text().upper()

            # Seřazení klíče abecedně
            sorted_decryption_key = ''.join(sorted(decryption_key_text))

            # Nastavení seřazeného klíče v prvním řádku tabulky
            decrypt_key_table = self.findChild(QTableWidget, 'decrypt_table')
            decrypt_key_table.setRowCount(1)
            decrypt_key_table.setColumnCount(len(sorted_decryption_key))
            for i, char in enumerate(sorted_decryption_key):
                item = QTableWidgetItem(char)
                decrypt_key_table.setItem(0, i, item)

            # Spočítáme počet řádků na základě délky šifrovaného textu a délky klíče
            num_rows = (len(ciphertext) + len(sorted_decryption_key) - 1) // len(sorted_decryption_key)
            decrypt_key_table.setRowCount(num_rows)

            # Naplníme tabulku podle sloupců, ne řádků, použijeme seřazený klíč
            for i, char in enumerate(ciphertext):
                row = i % num_rows
                col = i // num_rows
                item = QTableWidgetItem(char)
                decrypt_key_table.setItem(row, col, item)

            # Upravit šířku sloupců, aby se vešlo obsah
            decrypt_key_table.resizeColumnsToContents()
        except Exception as e:
            # Zachycení a zpracování výjimky (chyby)
            error_message = str(e)  # Získání textového popisu chyby
            self.show_error("Error", f"An error occurred: {error_message}")

    def display_decryption_adfgvx_pairs(self):
        encrypted_message = self.decrypted_message_entry.toPlainText().upper()
        key = self.encryption_key.text().upper()
        alphabet = self.alphabet_entry.text().upper()
        adfgvx_pairs = []

        for char in encrypted_message:
            if char in alphabet:
                encoded_char = self.encrypt_ADFGVX(char, key)
                adfgvx_pairs.append(encoded_char)

        # Reverse each character in adfgvx_pairs for the second row
        adfgvx_pairs_second_row = [pair[::-1] for pair in adfgvx_pairs]

        # Create a new QTableWidget with 2 rows and the same number of columns as the encrypted message length
        num_columns = len(adfgvx_pairs)  # Use the length of adfgvx_pairs
        self.decrypt_pairs_table.setColumnCount(num_columns)
        self.decrypt_pairs_table.setRowCount(2)

        # Set the encrypted_message in the first row
        for i, char in enumerate(encrypted_message):
            item = QTableWidgetItem(char)
            self.decrypt_pairs_table.setItem(0, i, item)

        # Set the reversed ADFGVX pairs in the second row
        for i, pair in enumerate(adfgvx_pairs_second_row):
            item = QTableWidgetItem(pair)
            self.decrypt_pairs_table.setItem(1, i, item)

        # Adjust column widths to fit the content
        self.decrypt_pairs_table.resizeColumnsToContents()

    def update_key_table(self):
        message_pairs_table = self.findChild(QTableWidget, 'message_pairs_table')
        if not message_pairs_table.item(0, 0):
            self.show_error("Error", "Please display ADFGVX pairs first.")
            return

        encryption_key_text = self.encryption_key.text().upper()
        message_pairs_table = self.findChild(QTableWidget, 'message_pairs_table')
        key_table = self.findChild(QTableWidget, 'key_table')

        num_columns = message_pairs_table.columnCount()
        encryption_key_length = len(encryption_key_text)

        # Create a dictionary to map characters to their position in the key
        char_to_position = {}
        char_count = {}  # Počet výskytů každého znaku v klíči

        # Počítání výskytů znaků v klíči
        for char in encryption_key_text:
            if char in char_count:
                char_count[char] += 1
            else:
                char_count[char] = 1

        # Calculate the number of rows needed to display the entire message
        num_rows = (num_columns + encryption_key_length - 1) // encryption_key_length

        # Create the key table with the appropriate number of rows and columns
        key_table.setRowCount(num_rows)
        key_table.setColumnCount(encryption_key_length)

        # Replace characters with their position in the key for matching characters
        for i in range(encryption_key_length):
            char = encryption_key_text[i]
            if char_count[char] > 1:
                if char in char_to_position:
                    char_to_position[char] += 1
                else:
                    char_to_position[char] = 1
                item = QTableWidgetItem(f"{char_to_position[char]}")
            else:
                item = QTableWidgetItem(char)
            key_table.setItem(0, i, item)

        # Get the text from the second row of the message_pairs_table
        adfgvx_text = [message_pairs_table.item(1, i).text() for i in range(num_columns)]

        # Split the pairs into individual characters
        individual_characters = [char for pair in adfgvx_text for char in pair]

        # Populate the key table with the individual characters from message_pairs_table
        for i, char in enumerate(individual_characters):
            row = (i // encryption_key_length) + 1
            col = i % encryption_key_length
            if char_count.get(char, 0) > 1:
                if char in char_to_position:
                    item = QTableWidgetItem(f"{char_to_position[char]}")
                else:
                    item = QTableWidgetItem(char)
            else:
                item = QTableWidgetItem(char)
            # If the row doesn't exist, create it
            while row >= key_table.rowCount():
                key_table.insertRow(key_table.rowCount())
            key_table.setItem(row, col, item)

        # Adjust column widths to fit the content
        key_table.resizeColumnsToContents()

    def display_adfgvx_pairs(self):
        input_message = self.input_message.text().upper()
        key = self.encryption_key.text().upper()
        alphabet = self.alphabet_entry.text().upper()
        adfgvx_pairs = []

        for char in input_message:
            if char in alphabet:
                encoded_char = self.encrypt_ADFGVX(char, key)
                adfgvx_pairs.append(encoded_char)  # Append only the encoded character

        # Reverse each character in adfgvx_pairs for the second row
        adfgvx_pairs_second_row = [pair[::-1] for pair in adfgvx_pairs]

        # Create a new QTableWidget with 2 rows and the same number of columns as the input message length
        num_columns = len(adfgvx_pairs)  # Use the length of adfgvx_pairs
        self.message_pairs_table.setColumnCount(num_columns)
        self.message_pairs_table.setRowCount(2)

        # Set the input_message in the first row
        for i, char in enumerate(input_message):
            item = QTableWidgetItem(char)
            self.message_pairs_table.setItem(0, i, item)

        # Set the reversed ADFGVX pairs in the second row
        for i, pair in enumerate(adfgvx_pairs_second_row):
            item = QTableWidgetItem(pair)
            self.message_pairs_table.setItem(1, i, item)

        # Adjust column widths to fit the content
        self.message_pairs_table.resizeColumnsToContents()

    def check_alphabet(self, text):
        # Získáme aktuální text z alphabet_entry
        current_text = self.alphabet_entry.text().upper()

        # Odstraníme duplicity znaků
        unique_characters = ''.join(sorted(set(current_text), key=current_text.index))

        # Aktualizujeme text v alphabet_entry
        self.alphabet_entry.setText(unique_characters)

    def update_alphabet_table(self):
        alphabet = self.alphabet_entry.text().upper()
        alphabet_table = self.findChild(QTableWidget, 'alphabet_table')

        num_rows = 0
        num_columns = 0
        custom_row_headers = []
        custom_col_headers = []
        missing_characters = set()

        # Determine the dimensions, headers, and missing characters based on the selected mode
        mode_index = self.alphabet_mode_combobox.currentIndex()
        if mode_index == 0:  # ADFGVX
            num_rows = 6
            num_columns = 6
            custom_row_headers = ['A', 'D', 'F', 'G', 'V', 'X']
            custom_col_headers = ['A', 'D', 'F', 'G', 'V', 'X']
            missing_characters = set("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - set(alphabet)
        elif mode_index == 1:  # ADFGX_CZ
            num_rows = 5
            num_columns = 5
            custom_row_headers = ['A', 'D', 'F', 'G', 'V', 'X']
            custom_col_headers = ['A', 'D', 'F', 'G', 'X']
            missing_characters = set("ABCDEFGHIKLMNOPQRSTUVWXYZ") - set(alphabet)
        elif mode_index == 2:  # ADFGX_EN
            num_rows = 5
            num_columns = 5
            custom_row_headers = ['A', 'D', 'F', 'G', 'V', 'X']
            custom_col_headers = ['A', 'D', 'F', 'G', 'X']
            missing_characters = set("ABCDEFGHIJKLMNOPQRSTUVXYZ") - set(alphabet)

        alphabet_table.setRowCount(num_rows)
        alphabet_table.setColumnCount(num_columns)

        used_characters = set()

        for row in range(num_rows):
            for col in range(num_columns):
                index = row * num_columns + col
                if index < len(alphabet):
                    char = alphabet[index]
                    item = QTableWidgetItem(char)
                    alphabet_table.setItem(row, col, item)
                    used_characters.add(char)
                else:
                    item = QTableWidgetItem('')
                    alphabet_table.setItem(row, col, item)

        missing_characters_text = ", ".join(sorted(missing_characters))
        self.missing_characters_label.setText(f"Chybějící znaky: {missing_characters_text}")

        # Set the custom row headers
        for row, header in enumerate(custom_row_headers):
            alphabet_table.setVerticalHeaderItem(row, QTableWidgetItem(header))

        # Set the custom column headers
        for col, header in enumerate(custom_col_headers):
            alphabet_table.setHorizontalHeaderItem(col, QTableWidgetItem(header))

    def generate_random_alphabet(self):
        mode_index = self.alphabet_mode_combobox.currentIndex()
        modes = ['ADFGVX', 'ADFGX_CZ', 'ADFGX_EN']  # Add more modes as needed
        selected_mode = modes[mode_index] if 0 <= mode_index < len(modes) else 'ADFGVX'

        if selected_mode == 'ADFGVX':
            alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
        elif selected_mode == 'ADFGX_CZ':
            alphabet = 'ABCDEFGHIKLMNOPQRSTUVWXYZ'
        elif selected_mode == 'ADFGX_EN':
            alphabet = 'ABCDEFGHIJKLMNOPQRSTUVXYZ'
        else:
            # Default to ADFGVX alphabet if the mode is not recognized
            alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

        custom_dictionary = [alphabet[i:i + 6] for i in range(0, len(alphabet), 6)]
        shuffle(custom_dictionary)
        self.alphabet_entry.setText(''.join(custom_dictionary))
        self.update_alphabet_table()

    # Add a function to handle mode selection
    def select_alphabet_mode(self, index):
        modes = ['ADFGVX', 'ADFGX_CZ', 'ADFGX_EN']  # Add more modes as needed
        selected_mode = modes[index] if 0 <= index < len(modes) else 'ADFGVX'
        self.generate_random_alphabet(selected_mode)

    def create_encode_dict(self):
        """ Create the ADFGVX encoding dictionary. """
        matrices = list('ADFGVX')
        pairs = [p[0] + p[1] for p in product(matrices, matrices)]
        return dict(zip(self.alphabet_entry.text().upper(), pairs))

    def encrypt_ADFGVX(self, msg, key):
        """ Encrypt with the ADFGVX cipher. """
        alphabet = list(self.alphabet_entry.text().upper())
        key = list(key.upper())
        pdim = int(floor(sqrt(len(alphabet))))
        encode = self.create_encode_dict()

        chars = list(''.join([encode[c] for c in msg.upper() if c in alphabet]))
        colvecs = [(lett, chars[i:len(chars):len(key)]) for (i, lett) in enumerate(key)]
        colvecs.sort(key=lambda x: x[0])
        return ''.join([''.join(a[1]) for a in colvecs])

    def decrypt_ADFGVX(self, cod, key):
        """ Decrypt with the ADFGVX cipher. Does not depend on spacing of encoded text """
        matrices = list('ADFGVX')
        chars = [c for c in cod if c in matrices]
        key = list(key.upper())
        sortedkey = sorted(key)
        order = [key.index(ch) for ch in sortedkey]
        originalorder = [sortedkey.index(ch) for ch in key]
        base, extra = divmod(len(chars), len(key))
        strides = [base + (1 if extra > i else 0) for i in order]
        starts = list(accumulate(strides[:-1], lambda x, y: x + y))
        starts = [0] + starts
        ends = [starts[i] + strides[i] for i in range(len(key))]
        cols = [chars[starts[i]:ends[i]] for i in originalorder]
        pairs = []
        for i in range((len(chars) - 1) // len(key) + 1):
            for j in range(len(key)):
                if i * len(key) + j < len(chars):
                    pairs.append(cols[j][i])

        decode = dict((v, k) for (k, v) in self.create_encode_dict().items())
        return ''.join([decode[pairs[i] + pairs[i + 1]] for i in range(0, len(pairs), 2)])

    def generate_alphabet(self):
        self.generate_random_alphabet()
        self.update_alphabet_table()

    def handle_alphabet_mode_change(self, index):
        self.generate_random_alphabet()

    def encrypt_message(self):
        input_message = self.input_message.text()
        key = self.encryption_key.text()
        mode = self.alphabet_mode_combobox.currentText()

        # Předzpracujte vstupní zprávu podle režimu
        processed_input_message = self.prepare_input_message(input_message, mode)

        if not processed_input_message or not key or not self.alphabet_entry.text():
            self.show_error("Error", "Please enter message, key, and alphabet")
            return

        ciphertext = self.encrypt_ADFGVX(processed_input_message, key)
        self.ciphertext_entry.setPlainText(ciphertext)

    def decrypt_message(self):
        ciphertext = self.ciphertext_entry2.text()
        key = self.decryption_key.text()

        if not ciphertext or not key or not self.alphabet_entry.text():
            self.show_error("Error", "Please enter ciphertext, key, and alphabet")
            return


        decrypted_message = self.decrypt_ADFGVX(ciphertext, key)

        decrypted_message = decrypted_message.replace('QXQ', ' ')


        self.decrypted_message_entry.setPlainText(decrypted_message)


    def show_error(self, title, message):
        msg_box = QMessageBox(self)
        msg_box.setIcon(QMessageBox.Icon.Critical)
        msg_box.setWindowTitle(title)
        msg_box.setText(message)
        msg_box.exec()

def main():
    app = QApplication(sys.argv)
    ex = ADFGVXCipherApp()
    ex.show()
    sys.exit(app.exec())

if __name__ == '__main__':
    main()
Reply
#2
I'm not sure, but the error you are getting is likely because you are trying to decrypt a message that is longer than the key. The ADFGVX cipher is a polyalphabetic substitution cipher, which means that it uses multiple alphabets to encrypt and decrypt messages. The key determines which alphabet is used for each character in the message.

If the key is shorter than the message, then the cipher will start using the same alphabets again for the later characters in the message. This means that the decryption will not be accurate.
Reply
#3
(Nov-06-2023, 08:02 AM)armamos Wrote: I'm not sure, but the error you are getting is likely because you are trying to decrypt a message that is longer than the key. The ADFGVX cipher is a polyalphabetic substitution cipher, which means that it uses multiple alphabets to encrypt and decrypt messages. The key determines which alphabet is used for each character in the message.

If the key is shorter than the message, then the cipher will start using the same alphabets again for the later characters in the message. This means that the decryption will not be accurate.
I've already fixed the code; now, the only issue is with the key. If it's one character shorter than the input text to be encrypted, decryption doesn't work. However, if it's shorter by more characters, it works
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Cesar Cipher ForsakenDusk 5 481 Apr-07-2024, 04:30 PM
Last Post: Pedroski55
Question Rsa Cipher Paragoon2 3 650 Nov-27-2023, 12:30 PM
Last Post: snippsat
  RSA Cipher with blocks Paragoon2 0 497 Nov-26-2023, 04:35 PM
Last Post: Paragoon2
  Caesar Cipher Help pbrowne 2 2,187 Jun-30-2021, 02:36 PM
Last Post: deanhystad
  Alternate solutions to cipher problem? Mark17 3 2,426 Oct-08-2019, 01:32 PM
Last Post: jefsummers
  Problem with caesar cipher lucaron 2 2,953 Feb-05-2018, 05:17 PM
Last Post: lucaron

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020