AES-sim Cypher App



Intro:

In this project I wrapped an AES-like cypher into a fully independant execuatable program—using PyQt5 and an AES-like cypher that I wrote. I have been planning to do this for quite some time. Although, I ran into an issue with overloading the main program when the cypher function was called. It would freeze because the main program was overburdened during the cypher proccess. This was not at all efficient.

To fix this issue I applied a threaded process. Which essentially takes a one lane highway (our main thread) and converts it to a multi-lane highway. Allowing for traffic on the main thread that is bogged down by a traffic jam (too many processes or heavy processes occuring in the same thread) to use more lanes of traffic and alieviate congestion.

I will be honest, this took me quite some time and reading to figure out—it was not at all intuitive. It can be really confusing because you have several pieces to one function that go to a thread and are all defined, called, and initialized in different places—not to mention you have to split off the heavy parts of the original function causing the program to freeze.

Once you successfully apply the threading methods the program speeds up tremendously. Which allows for cyphering of large amounts of text data at a very rapid pace.

I also made a few changes to the encryption process which allows for more than just ascii characters. I added about 7 lines of code that allow the user to input UTF-8 characters as well. So, the input will now accept UTF-8, which is the most common type of encoding on computer systems generally.

I should also say here that these cyphers—while a good exersize in coding— are not secure. These are also not intended for any purpose other than practice in handling data and methods. So, please do not use them for activity for which they are not intended.


Application Binder:

Here I'm including all the file I used to create the application. I zipped them into a binder.
DOWNLOAD: ANG AES-sim Cypher Binder
I used Pyinstaller to create the fully independant .exe application that you can see in the example videos.


AES-like Cypher Source Code:

I am mentioning this here because the AES-like cypher is a dependancy of the application.

I did some modifications to the aes-like (aes_sim_0) cypher I wrote on the ANG Cyphers page of this site. I added UTF-8 as a cipherable text encoding—this is in addition to ascii.

aes_sim_v_2_1:


"""
Created on Thu Feb  3 22:40:42 2022

@author: ANG
"""
import numpy as np
import string
import random

def sim_aes_1(vals, key, enc): 
    ''' 
    vals = 'some string to encode'
    key = 'someString16charactersLong'
    
    enc = True
    Takes the key and applies a key expansion to generate 9 more unique keys 
    Takes a plain text str and converts it to a bytearray() -> converts it to a 
    3d array ->  pads with random data if it is not divisible by 16 -> substitutes 
    data depending on axes location -> mixes rows/cols -> applies a new key to 
    the array depending on round
    
    enc = False
    Does the reverse of enc. 
    ''' 
    #convert the key to an array 
    #HERE 
    key = [i for i in bytearray(key, 'ascii')]
    key = np.array(key).reshape(-1,4,4)

    def expand_key(key):
        #expand the key here 
        #roll the key words 
        key_r1 = [i for i in np.roll(key[0], 1, 1)]
        key_r1 = np.vstack(key_r1) 
        key_r1 = key_r1.ravel()
        #substitute vals HERE (arbitrary substitution)
        key_r1 = np.array([i + 16 if (i % 2 == 0) else i for i in key_r1]).reshape(-1,4,4)
        #xor rows with xor word 
        xor_w = np.array([13, 0, 0, 0])
        key_r1 = np.array([v^xor_w for v in key_r1])
        return key_r1 
    key_0 = expand_key(key) 
    key_1 = expand_key(key_0)
    key_2 = expand_key(key_1)
    key_3 = expand_key(key_2)
    key_4 = expand_key(key_3)
    key_5 = expand_key(key_4)
    key_6 = expand_key(key_5)
    key_7 = expand_key(key_6)
    key_8 = expand_key(key_7)
    key_9 = expand_key(key_8)
    keys_dict = {0:key_0, 1:key_1, 2:key_2, 
                 3:key_3, 4:key_4, 5:key_5, 
                 6:key_6, 7:key_7, 8:key_8, 
                 9:key_9}
    #substitution table parts 
    if enc == False: 
        #CYPHER THE ARRAY
        #bit encoding for string data (arg vals)
        #sequence; str -> bytearray ascii (strings) -> int -> hex
        #this try and except is used to include a string that is utf-8 encoded
        try:
            vals_ph = [i for i in bytearray(vals, 'ascii')] #STEP 1 STRING TO BYTEARRAY
            vals_arr = np.array(vals_ph) 
        except: 
            vals_ph = [i for i in bytearray(vals, 'utf-8')] #STEP 1 STRING TO BYTEARRAY
            vals_arr = np.array(vals_ph) 
        #the size of vals_arr has to be a multiple of 16 
        if vals_arr.size % 16 == 0: 
            vals_arr = vals_arr.reshape(-1, 4, 4) 
        else: 
            while vals_arr.size % 16 != 0: 
                #apply random pad at the end if the array size is not divisible by 16
                vals_arr = np.append(vals_arr, '32') 
            vals_arr = vals_arr.reshape(-1, 4, 4) 
        #convert string to int 
        vals_arr = vals_arr.astype(int)  #BYTEARRAY TO INT
        #Apply ROUNDS HERE 
        def round_nth(vals_arr): 
            for i in range(10):
                #SUB BYTES 
                for i2 in range(vals_arr.shape[0]):
                    if i2 % 2 == 1:
                        vals_arr[i2] += 2
                    else: 
                        vals_arr[i2] -= 2
                #MIX ROWS / MIX COLS 
                vals_arr = np.roll(vals_arr, 1, 0) 
                vals_arr = np.roll(vals_arr, 1, 1) #shift rows for each axis0 down 1; last row becomes first row, and last row becomes first row
                vals_arr = np.roll(vals_arr, 1, 2) #shift axes 2 up one for each item in axes0            
                #ADD Nth ROUND KEY 
                vals_arr = vals_arr^keys_dict[i]
            return vals_arr
        vals_arr = round_nth(vals_arr)
        #flatten cyphered array 
        vals_arr = vals_arr.flatten()
        output = ' '.join(map(str, vals_arr)).split()
        output = [int(i) for i in output]
        return output
    else: 
        #DECYPHER THE ARRAY
        #bit encoding for string data (arg vals)
        #sequence; str -> bytearray ascii (strings) -> int 
        vals_arr = np.array(vals) 
        vals_arr = vals_arr.reshape(-1, 4, 4) 
        #convert string to int 
        vals_arr = vals_arr.astype(int)  #BYTEARRAY TO INT
        #Apply ROUNDS HERE 
        def round_nth(vals_arr): 
            for i in range(9, -1, -1):
                #REVERSE Nth ROUND KEY 
                vals_arr = vals_arr^keys_dict[i]  
                #MIX ROWS / MIX COLS 
                vals_arr = np.roll(vals_arr, -1, 0) 
                vals_arr = np.roll(vals_arr, -1, 1) #shift rows for each axis0 down 1; last row becomes first row, and last row becomes first row
                vals_arr = np.roll(vals_arr, -1, 2) #shift axes 2 up one for each item in axes0  
                #SUB BYTES 
                for i in range(vals_arr.shape[0]):
                    if i % 2 == 1:
                        vals_arr[i] -= 2
                    else: 
                        vals_arr[i] += 2
            return vals_arr 
        
        vals_arr = round_nth(vals_arr)
        #flatten cyphered array 
        vals_arr = vals_arr.flatten()
        output = ' '.join(map(str, vals_arr)).split()
        output = [int(i) for i in output]
        #this try and except is used to include a string that is utf-8 encoded
        try:
            output = bytearray(list(output)).decode('ascii')
        except:
            output = bytearray(list(output)).decode('utf-8')
        return output
    

Threaded App:

Here's an example useage of the threaded cypher:

Threaded Cypher App Source Code:

Here's the source code for the application. I've done my best to comment heavily.


# -*- coding: utf-8 -*-
"""
Created on Fri Oct 29 19:44:44 2021

@author: ANG
"""
#IMPORTS
from PyQt5.QtWidgets import * 
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
from ang_cyphers import aes_sim_v_2_1 as advc2

class diverted_traffic_signals(QObject):
    '''
    Class allows signals to be assigned and emitted to the main thread from whatever 
    worker. To allow data to be passed from a worker thread they have to have declared 
    signals here. 
    
    Where: someSignalVar = pyqtSignal(dataTypeHere)
    '''
    result = pyqtSignal(object)

class diverted_traffic_worker_thread(QRunnable):
    '''
    This class is passed the args vals, key, and enc to its __init__, from WindowClassOne 
    on_execute(). It then uses these passed attributes in run(). Last, it emits 
    the result from run() to return_cypher(self, thread_out_result)—where thread_out_result 
    is "result" emitted from diverted_traffic_worker_thread run(). 
    ''' 
    def __init__(self, vals, key, enc, *args, **kwargs):
        super(diverted_traffic_worker_thread, self).__init__()
        #Constructor arguments (re-used for processing)
        self.vals = vals
        self.key = key
        self.enc = enc
        self.args = args
        self.kwargs = kwargs
        self.signals = diverted_traffic_signals()

    @pyqtSlot()
    def run(self):
        '''
        Process the threaded function. All heavy lifting here. 
        ''' 
        if self.enc == 0: 
            self.result = advc2.sim_aes_1(vals=self.vals, key=self.key, enc=bool(self.enc))
            self.result = ' '.join(map(str, self.result))
            self.signals.result.emit(self.result) 
        elif self.enc == 2:
            try: 
                self.vals = self.vals.split()
                self.result = advc2.sim_aes_1(vals=self.vals, key=self.key, enc=bool(self.enc))
                self.signals.result.emit(self.result) 
            except: 
                self.result = 'wrong pass'
                self.signals.result.emit(self.result) 
                
class WindowClassOne(QWidget):
    '''
    This application cyphers a string that is encoded in utf-8 or ascii. Before 
    it only cyphered and decyphered ascii characters. So, to solve this problem 
    I added to statements that did the same for ascii but for utf-8 as well. I 
    wanted to do this because most of the web is encoded with utf-8. You can also 
    apply the same encoding and decoding methods by adding more ways of encoding 
    decoding. By adding seven lines of code I have incorporated both types of 
    encoding in the function advc2.sim_aes_1(). 
    '''
    def __init__(self):
        super(WindowClassOne,self).__init__()
        #SET THE DEFAULT FONT FOR APP
        self.setFont(QFont('consolas',11))
        #SET DEFUALT MINIMUM SIZE OF WINDOW
        self.setMinimumSize(900,900)
        #INSTANTIATE AN OUTER LAYOUT (master layout)
        self.outerLayout = QVBoxLayout()
        #ADD THE MAIN WINDOWS OUTER LAYOUT (MASTER LAYOUT)
        self.setLayout(self.outerLayout)
        #INSTANTIATE LEFT AND RIGHT LAYOUTS (sub layouts)
        self.upperLayout = QGridLayout()
        self.lowerLayout = QGridLayout()
        self.lowerLayoutWidgs = QGridLayout()
        self.subformlayout_1 = QFormLayout()
        #CALL THE INNER LAYOUTS INTO THE OUTER LAYOUT (MASTER LAYOUT)
        self.outerLayout.addLayout(self.upperLayout, 2) #the integer indicates respective stretch 
        self.outerLayout.addLayout(self.lowerLayout, 2) #the integer indicates respective stretch 
        self.outerLayout.addLayout(self.lowerLayoutWidgs, 0)
        #INSTANTIATE UPPER WIDGETS (added in order) 
        self.cypher_decypher_label = QLabel('Cypher-Decypher Output:')
        self.upper_layout_textedit = QTextEdit('', readOnly=True, )
        self.export_ct_button = QPushButton('Save Output', self, maximumWidth=125)
        #INSTANTIATE LOWER WIDGETS (added in order) 
        self.lower_label = QLabel('Cypher-Decypher Input:')
        self.lower_layout_textedit = QTextEdit('', readOnly=False, acceptRichText=False)
        self.cypher_type_label = QLabel('')
        self.cypher_key_label = QLabel('Key Input')
        self.aes_sub_cypher_button = QPushButton('Execute', maximumWidth=150)
        self.aes_pass_textinput = QLineEdit('', maxLength=16, maximumWidth=150)
        self.aes_pass_textinput.setEchoMode(QLineEdit.Password)
        self.aes_decode_label = QLabel('Decypher')
        self.decode_check_box = QCheckBox()
        self.load_txt_button = QPushButton('Load Text', maximumWidth=100)
        self.load_txt_label = QLabel('*load .txt to Cypher/Decypher')
        self.progress_bar = QProgressBar(self) #I have included the progress bar here but I do not do anything with it
        #create threadpool
        self.threadpool = QThreadPool()
        #CALL THE WIDGETS TO THE LEFT AND RIGHT LAYOUTS
        #you have to declare where the widget goes in the gridlayout here
        #so syntax; SomeLayout.addWidget(SomeWidget, gridCoord_y, gridCoord_x)
        #span multi syntax; SomeLayout.addWidget(SomeWidget, gridCoord_y, gridCoord_x, gridSpanCoord_y2, gridSpanCoord_x2)
        self.upperLayout.addWidget(self.cypher_decypher_label, 0, 0, 1, 0)
        self.upperLayout.addWidget(self.upper_layout_textedit, 1, 0, 1, 0)
        self.upperLayout.addWidget(self.export_ct_button, 2, 0, 1, 0)
        self.lowerLayout.addWidget(self.lower_label, 0, 0, 1, 1) 
        self.lowerLayout.addWidget(self.progress_bar, 0, 1, 1, 1) 
        self.lowerLayout.addWidget(self.lower_layout_textedit, 1, 0, 2, 2) 
        self.lowerLayoutWidgs.addLayout(self.subformlayout_1, 2, 0, 1, 1)
        #layout.addWidget(self.progressBar)
        #INSTANTIATE THE SUB FORM LAYOUTS 
        self.subformlayout_1.addRow(self.load_txt_button, self.load_txt_label)
        self.subformlayout_1.addRow(self.cypher_key_label, self.aes_pass_textinput)
        self.subformlayout_1.addRow(self.aes_decode_label, self.decode_check_box)
        self.subformlayout_1.addRow(self.cypher_type_label, self.aes_sub_cypher_button)
        #SET STYLES  
        self.setStyleSheet("height:30px ; font: consolas ")
        self.export_ct_button.setStyleSheet("background-color : #ADC6C5")
        self.load_txt_button.setStyleSheet("background-color : #ADC6C5")
        self.aes_sub_cypher_button.setStyleSheet("background-color : #ADC6C5")
        self.progress_bar.setStyleSheet("background-color : #C6C6C3")
        
        def key_alert_diag(self): 
            '''
            Function to generate key length alert message. 
            '''
            self.load_fasta_alert = QMessageBox()
            self.load_fasta_alert.setWindowTitle('Key size!')
            self.load_fasta_alert.setText('The key must be 16 characters...')
            self.load_fasta_alert.exec_()
        
        def wrong_key_alert_diag(self): 
            '''
            Function to generate wrong key alert message.
            '''
            self.load_fasta_alert = QMessageBox()
            self.load_fasta_alert.setIconPixmap(QPixmap("systemlockscreen.ico"))
            self.load_fasta_alert.setWindowTitle('Wrong Key!')
            self.load_fasta_alert.setText('nice try... key is definitely wrong...')
            self.load_fasta_alert.exec_()
            
        def return_cypher(self, thread_out_result): 
            '''
            Function that gets passed the result of the threaded run() from diverted_traffic_worker_thread 
            as thread_out_result. When the thread emits result, it goes here as 
            arg thread_out_result. 
            '''
            if self.decode_check_box.checkState() == 0:          
                self.upper_layout_textedit.clear()
                self.upper_layout_textedit.insertPlainText(thread_out_result)
                self.lower_layout_textedit.clear()
                self.aes_pass_textinput.clear()
            elif thread_out_result != 'wrong pass': 
                    self.upper_layout_textedit.clear()
                    self.upper_layout_textedit.insertPlainText(thread_out_result)
                    self.lower_layout_textedit.clear()
                    self.aes_pass_textinput.clear()
            elif thread_out_result == 'wrong pass':
                    dlg = wrong_key_alert_diag(self)
                    self.aes_pass_textinput.clear()
                    
        def on_execute(self):
            '''
            This function is the initial driver of the thread. First it checks 
            the integrity of the key length. Then, assigns the three variables 
            needed for the encryption/decryption. Then, assigns the assn_work 
            (assn_work) to diverted_traffic_worker_thread and passes the three 
            assignments as args for diverted_traffic_worker_thread (the threaded class)—
            this applies the args as class attibutes to diverted_traffic_worker_thread. 
            Lastly, it signals diverted_traffic_worker_thread to run and connect 
            the result to return_cypher(). 
            
            In the diverted_traffic_worker_thread class, it emits "result" to the
            thread_out_result arg in return_cypher(). That is how "result" is passed 
            from diverted_traffic_worker_thread to return_cypher() — but the 
            function return_cypher() is called here in the second to last line 
            of this function. 
            
            Basic steps: 
                1. checks key
                2. assigns key, enc, vals 
                3. applies key, enc, vals as attributes of diverted_traffic_worker_thread
                4. calls a signal to run() in diverted_traffic_worker_thread and 
                    passes its emmited work to return_cypher() as return_cypher 
                    argument thread_out_result
                5. calls the thread to start 
            '''
            if len(self.aes_pass_textinput.text()) < 16: 
                dlg = key_alert_diag(self)
            else: 
                key = self.aes_pass_textinput.text()
                enc = self.decode_check_box.checkState() #0 or 1
                vals = self.lower_layout_textedit.toPlainText()
                assn_work = diverted_traffic_worker_thread(vals, key, enc) 
                assn_work.signals.result.connect(lambda checked: return_cypher(self, assn_work.result))
                self.threadpool.start(assn_work) 
        
        def on_export_ct(self):
            '''
            Function to export a file as .txt 
            ''' 
            #GET DIRECTORY EXTENTION TO SAVE (EXPORT)
            save_file_var = QFileDialog.getSaveFileName(None,'SaveTextFile','./', "Text Files (*.txt)")
            #APPLY QTEXTEDIT AS PLAIN TEXT TO SAVE
            Text = self.upper_layout_textedit.toPlainText()
            #PREVENT ERROR ON CANCEL
            if save_file_var[0]: 
                # Finally this will Save your file to the path selected.
                with open(save_file_var[0], 'w') as file:
                    file.write(Text)
            self.upper_layout_textedit.clear()
            
        def load_txt(self):
            '''
            function that loads a .txt file -- encrypted or not encrypted. 
            ''' 
            filename = QFileDialog.getOpenFileName(None, 'Open file', './', 'txt Files (*.txt)')
            #PREVENT ERROR ON CANCEL
            if filename[0]:
                txt = open(filename[0], 'r')
                with txt:
                    self.data = txt.read()
                    self.filename = filename[0].split('/') 
                    self.lower_layout_textedit.insertPlainText(self.data)
        
        #CONNECT THE BUTTONS TO RESPECTIVE FUNCTIONS
        self.aes_sub_cypher_button.clicked.connect(lambda checked: on_execute(self))
        self.export_ct_button.clicked.connect(lambda checked: on_export_ct(self))
        self.load_txt_button.clicked.connect(lambda checked: load_txt(self))
        
if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle('Fusion') 
    window = WindowClassOne()
    window.show()
    sys.exit(app.exec_()) 
    
With the above, to apply the thread it looks like the following visual—which I thought gives a good visual aid on what is going on.
thread_vis

Non-Threaded App:

Here's an example useage of the non-threaded cypher:

Non-threaded Cypher App Source code:

Here's the source code for the non-threaded application. I've done my best to comment heavily.

 
# -*- coding: utf-8 -*-
"""
Created on Fri Oct 29 19:44:44 2021

@author: ANG
"""
#IMPORTS
from PyQt5.QtWidgets import * 
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
from ang_cyphers import aes_sim_v_2_1 as advc2

class WindowClassOne(QWidget):
    '''
    This application cyphers a string that is encoded in utf-8 or ascii. Before 
    it only cyphered and decyphered ascii characters. So, to solve this problem 
    I added to statements that did the same for ascii but for utf-8 as well. I 
    wanted to do this because most of the web is encoded with utf-8. You can also 
    apply the same encoding and decoding methods by adding more ways of encoding 
    decoding. By adding seven lines of code I have incorporated both types of 
    encoding in the function advc2.sim_aes_1(). 
    '''
    def __init__(self):
        super(WindowClassOne,self).__init__()
        #SET THE DEFAULT FONT FOR APP
        self.setFont(QFont('consolas',11))
        #SET DEFUALT MINIMUM SIZE OF WINDOW
        self.setMinimumSize(900,900)
        #INSTANTIATE AN OUTER LAYOUT (master layout); 
        self.outerLayout = QVBoxLayout()
        #ADD THE MAIN WINDOWS OUTER LAYOUT (MASTER LAYOUT)
        self.setLayout(self.outerLayout)
        #INSTANTIATE LEFT AND RIGHT LAYOUTS (sub layouts)
        self.upperLayout = QGridLayout()
        self.lowerLayout = QGridLayout()
        self.lowerLayoutWidgs = QGridLayout()
        self.subformlayout_1 = QFormLayout()
        #CALL THE INNER LAYOUTS INTO THE OUTER LAYOUT (MASTER LAYOUT)
        self.outerLayout.addLayout(self.upperLayout, 2) #the integer indicates respective stretch 
        self.outerLayout.addLayout(self.lowerLayout, 2) #the integer indicates respective stretch 
        self.outerLayout.addLayout(self.lowerLayoutWidgs, 0)
        #INSTANTIATE UPPER WIDGETS (added in order) 
        self.cypher_decypher_label = QLabel('Cypher-Decypher Output:')
        self.upper_layout_textedit = QTextEdit('', readOnly=True, )
        self.export_ct_button = QPushButton('Export CT', maximumWidth=100)
        #INSTANTIATE LOWER WIDGETS (added in order) 
        self.lower_label = QLabel('Cypher-Decypher Input:')
        self.lower_layout_textedit = QTextEdit('', readOnly=False, acceptRichText=False)
        self.cypher_type_label = QLabel('')
        self.cypher_key_label = QLabel('Key Input')
        self.aes_sub_cypher_button = QPushButton('Execute', maximumWidth=150)
        self.aes_pass_textinput = QLineEdit('', maxLength=16, maximumWidth=150)
        self.aes_pass_textinput.setEchoMode(QLineEdit.Password)
        self.aes_decode_label = QLabel('Decypher')
        self.decode_check_box = QCheckBox()
        self.load_txt_button = QPushButton('Load Text', maximumWidth=100)
        self.load_txt_label = QLabel('*load txt to Cypher/Decypher')
        #CALL THE WIDGETS TO THE LEFT AND RIGHT LAYOUTS
        #you have to declare where the widget goes in the gridlayout here
        #so syntax; SomeLayout.addWidget(SomeWidget, gridCoord_y, gridCoord_x)
        #span multi syntax; SomeLayout.addWidget(SomeWidget, gridCoord_y, gridCoord_x, gridSpanCoord_y2, gridSpanCoord_x2)
        self.upperLayout.addWidget(self.cypher_decypher_label, 0, 0, 1, 0)
        self.upperLayout.addWidget(self.upper_layout_textedit, 1, 0, 1, 0)
        self.upperLayout.addWidget(self.export_ct_button, 2, 0, 1, 0)
        self.lowerLayout.addWidget(self.lower_label, 0, 0, 1, 1) 
        self.lowerLayout.addWidget(self.lower_layout_textedit, 1, 0, 1, 1) 
        self.lowerLayoutWidgs.addLayout(self.subformlayout_1, 2, 0, 1, 1)
        #INSTANTIATE THE SUB FORM LAYOUTS 
        self.subformlayout_1.addRow(self.load_txt_button, self.load_txt_label)
        self.subformlayout_1.addRow(self.cypher_key_label, self.aes_pass_textinput)
        self.subformlayout_1.addRow(self.aes_decode_label, self.decode_check_box)
        self.subformlayout_1.addRow(self.cypher_type_label, self.aes_sub_cypher_button)
        #SET STYLES  
        self.setStyleSheet("height:30px ; font: consolas ")
        self.export_ct_button.setStyleSheet("background-color : #ADC6C5")
        self.load_txt_button.setStyleSheet("background-color : #ADC6C5")
        self.aes_sub_cypher_button.setStyleSheet("background-color : #ADC6C5")
        def key_alert_diag(self): 
            self.load_fasta_alert = QMessageBox()
            self.load_fasta_alert.setWindowTitle('Key size!')
            self.load_fasta_alert.setText('The key must be 16 characters...')
            self.load_fasta_alert.exec_()
        
        def wrong_key_alert_diag(self): 
            self.load_fasta_alert = QMessageBox()
            self.load_fasta_alert.setIconPixmap(QPixmap("systemlockscreen.ico"))
            self.load_fasta_alert.setWindowTitle('Wrong Key!')
            self.load_fasta_alert.setText('nice try... key is definitely wrong...')
            self.load_fasta_alert.exec_()
            
        def on_execute(self): 
            '''
            This function checks the key length. 
            '''
            if len(self.aes_pass_textinput.text()) < 16: #checks key length 
                dlg = key_alert_diag(self)
            elif self.decode_check_box.checkState() == 0: #if cyphering the data 
                key = self.aes_pass_textinput.text() #assigns the key
                encode_state = self.decode_check_box.checkState() #assigns the cypher state
                vals = self.lower_layout_textedit.toPlainText()   #assigns the data in the input QTextEdit
                encoding = advc2.sim_aes_1(vals=vals, key=key, enc=bool(encode_state)) #applies the cypher to text
                self.upper_layout_textedit.clear()                                     #clears the upper QTextEdit
                for i in encoding:                                           #for every item in the encoded data list
                    self.upper_layout_textedit.insertPlainText(f'{str(i)} ') #inserts items into upper QTextEdit
                self.lower_layout_textedit.clear()                           #clears the lower QTextEdit
                self.aes_pass_textinput.clear()                              #clears the key input
            else: #if decypher is checked
                try: 
                    key = self.aes_pass_textinput.text()
                    encode_state = self.decode_check_box.checkState()
                    vals = self.lower_layout_textedit.toPlainText() 
                    vals = vals.split() #splits the text into by spaces into a list
                    encoding = advc2.sim_aes_1(vals=vals, key=key, enc=bool(encode_state)) #decodes the text data
                    self.upper_layout_textedit.clear() 
                    for i in encoding: 
                        self.upper_layout_textedit.insertPlainText(f'{str(i)}', )
                    self.lower_layout_textedit.clear()
                    self.aes_pass_textinput.clear()
                except:                              #if the key is wrong it returns an exception 
                    dlg = wrong_key_alert_diag(self) #calls an alert for wrong key
                    self.aes_pass_textinput.clear()  #clears the key input
                    
        def on_export_ct(self):
            '''
            Function to export a file as .txt 
            ''' 
            #GET DIRECTORY EXTENTION TO SAVE (EXPORT)
            save_file_var = QFileDialog.getSaveFileName(None,'SaveTextFile','./', "Text Files (*.txt)")
            #APPLY QTEXTEDIT AS PLAIN TEXT TO SAVE
            Text = self.upper_layout_textedit.toPlainText()
            #PREVENT ERROR ON CANCEL
            if save_file_var[0]: 
                # Finally this will Save your file to the path selected.
                with open(save_file_var[0], 'w') as file:
                    file.write(Text)
            self.upper_layout_textedit.clear()
            
        def load_txt(self):
            '''
            function that loads a .txt file -- encrypted or not encrypted. 
            ''' 
            filename = QFileDialog.getOpenFileName(None, 'Open file', './', 'txt Files (*.txt)')
            #PREVENT ERROR ON CANCEL
            if filename[0]:
                txt = open(filename[0], 'r')
                with txt:
                    self.data = txt.read()
                    self.filename = filename[0].split('/') 
                    self.lower_layout_textedit.insertPlainText(self.data)
        #CONNECT THE BUTTONS TO RESPECTIVE FUNCTIONS
        self.aes_sub_cypher_button.clicked.connect(lambda checked: on_execute(self))
        self.export_ct_button.clicked.connect(lambda checked: on_export_ct(self))
        self.load_txt_button.clicked.connect(lambda checked: load_txt(self))
if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle('Fusion') 
    window = WindowClassOne()
    window.show()
    sys.exit(app.exec_())