Make e-mail text more secure (PyQT/PySide)

vegaseat 2 Tallied Votes 526 Views Share

Using the PySide GUI toolkit will make selecting, copying and pasting encrypted and decrypted text somewhat easier. Not ideal yet, but it will be a good start for those of you who are not very familiar with GUI programming.

''' ps_TextEdit_crypt_xor1.py
use PySide's QTextEdit to write, edit and ecrypt/decrypt text

click right mouse button in edit area for popup menu
for undo, cut, copy. paste, select all, etc.

xor crypt the text in one editor into printable text in another
these texts can be selected then copied and pasted to emails etc.

PySide is the official LGPL-licensed version of PyQT
PySide is available for Windows and UNIX machines
I downloaded the free Windows self-extracting installer
for Python27: PySide-1.2.0.win32-py2.7.exe
for Python33: PySide-1.2.0.win32-py3.3.exe
from:
http://developer.qt.nokia.com/wiki/PySide_Binaries_Windows

tested with Python27/33 and PySide12  by  vegaseat  5aug2013
'''

from PySide.QtCore import *
from PySide.QtGui import *
import operator
import base64

class MyFrame(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        # setGeometry(x_pos, y_pos, width, height)
        self.setGeometry(100, 50, 540, 450)
        s = "Encrypt/Decrypt text (can select/paste to and from email)"
        self.setWindowTitle(s)

        s = "Backspace default, type your password and press Enter: "
        self.label1 = QLabel(s)
        self.edit_pw = QLineEdit(self)
        # default password
        self.edit_pw.insert('Nixon')
        self.get_password()
        # echo mode set to Password
        self.edit_pw.setEchoMode(QLineEdit.Password)
        #print(self.edit_pw.echoMode())  # test
        self.edit_encr = QTextEdit(self)
        self.edit_decr = QTextEdit(self)
        self.button2 = QPushButton(self)
        self.button2.setText('encrypt text')
        self.button3 = QPushButton(self)
        self.button3.setText('decrypt text')
        self.info_label = QLabel(self)
        s1 = "click right mouse button in edit area for popup menu"
        s2 = "for undo, cut, copy. paste, select all, etc."
        self.info_label.setText(s1+s2)
        self.info_label2 = QLabel(self)
        if self.edit_pw.text() == 'Nixon':
            s3 = "Needs your own password, please enter it!"
            s4 = " (Default is 'Nixon')"
            html_code = "<font color=red>{} {}</font>".format(s3, s4)
            self.info_label2.setText(html_code)

        # use grid layout manager
        grid = QGridLayout(self)
        # addWidget(QWidget, row, column, rowSpan, columnSpan)
        grid.addWidget(self.info_label2, 0, 0, 1, 1)
        grid.addWidget(self.label1, 0, 1, 1, 1)
        grid.addWidget(self.edit_pw, 0, 2, 1, 1)
        grid.addWidget(self.button2, 1, 3, 1, 1)
        grid.addWidget(self.edit_encr,2, 0, 1, 4)
        grid.addWidget(self.button3, 3, 3, 1, 1)
        grid.addWidget(self.edit_decr, 4, 0, 1, 4)
        grid.addWidget(self.info_label, 5, 0, 1, 1)
        self.setLayout(grid)

        # bind the button clicked to action
        # newer connect style used with version 4.5 or higher
        self.button2.clicked.connect(self.encrypt_text)
        self.button3.clicked.connect(self.decrypt_text)
        self.edit_pw.returnPressed.connect(self.get_password)

    def get_password(self):
        pw = self.edit_pw.text()
        self.password = self.password_expand(pw)
        if pw != 'Nixon':
            self.info_label2.setText("Using your password now!")
        #print("{} --> {}".format(pw, self.password))  # test

    def encrypt_text(self):
        """
        encrypt text in editor2 to editor3
        """
        text = self.edit_encr.toPlainText()
        if text:
            self.encrypted = self.encrypt109(text, self.password)
        else:
            self.encrypted = ""
        self.edit_decr.setPlainText(self.encrypted)

    def decrypt_text(self):
        """
        decrypt text in editor3 to editor2
        """
        text = self.edit_decr.toPlainText()
        if text:
            self.decrypted = self.decrypt109(text, self.password)
        else:
            self.decrypted = ""
        self.edit_encr.setPlainText(self.decrypted)

    def encrypt109(self, text, password):
        '''
        use base64 encoding
        modified to work with Python2 and Python3
        '''
        x_text = self.xor_crypt2(text, password)
        try:
            # Python27
            return base64.encodestring(x_text)
        except TypeError:
            # Python3
            return base64.encodestring(x_text.encode("utf8")).decode("utf8")

    def decrypt109(self, e_text, password):
        '''
        use base64 decoding
        modified to work with Python2 and Python3
        '''
        try:
            # Python27
            b64_text = base64.decodestring(e_text)
        except TypeError:
            # Python3
            b64_text = base64.decodestring(e_text.encode("utf8")).decode("utf8")
        return self.xor_crypt2(b64_text, password)

    def xor_crypt2(self, text, password):
        '''
        xor crypt using list container
        '''
        xlist = []
        n = 0
        for c in text:
            # loop through password start to end and repeat
            if n >= len(password) - 1:
                n = 0
            pw = ord(password[n])
            n += 1
            bt = ord(c)
            # xor byte with password byte
            xbt = operator.xor(bt, pw)
            # convert to character and append to xlist
            xlist.append(chr(xbt))
            # convert xlist to string and return
        return ''.join(xlist)

    def password_expand(self, pw):
        '''
        make the password longer and more secure
        '''
        s1 = pw
        s2 = "".join(chr(ord(c) + 3) for c in pw)
        s3 = "".join(chr(ord(c) - 5) for c in pw)
        return s1 + s2 + s3

app = QApplication([])
frame = MyFrame()
frame.show()
app.exec_()