Last active
November 30, 2024 01:49
-
-
Save danilw/5c6cc2dc9e995e0c284b9c5511efe655 to your computer and use it in GitHub Desktop.
text_edit.py Python text editor with pass encryption with UTF-8 text support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/python3 | |
# text_edit.py | |
# Python text editor (from random Tinker based code) with pass encryption, with UTF-8 text support(non ASCII) | |
# usage: | |
# pip3 install pycryptodome | |
# python3 text_edit.py | |
# P.S. password strength: | |
# for 12-length password with upper-case symbol and number and special symbol: | |
# on single GPU that can process 2000x2000 - compute-passwords per frame at 60 fps | |
# there 333061772956016240000000 password compinations (assuming one upper-case and digit and special symbol is required) | |
# https://www.omnicalculator.com/statistics/password-combination | |
# it will require for that single GPU 44-million years (44005498) | |
# to find 12-length password in 44-years - 1000000(million) GPUs with that performance | |
import tkinter as tk | |
from tkinter import ttk | |
from tkinter import filedialog | |
from tkinter import messagebox | |
from tkinter import simpledialog | |
from tkinter import scrolledtext | |
import Crypto.Random, Crypto.Protocol.KDF, Crypto.Cipher.AES | |
tittle= "Text edit" | |
def cipherAES_GCM(pwd, nonce): | |
key = Crypto.Protocol.KDF.PBKDF2(pwd, nonce, count=100000) | |
return Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_GCM, nonce=nonce, mac_len=16) | |
def new_file(e=None): | |
if not text_area.edit_modified(): | |
text_area.delete('1.0', tk.END) | |
else: | |
save_file_as() | |
text_area.delete('1.0', tk.END) | |
text_area.edit_modified(0) | |
main_window.title(tittle) | |
def open_file(root=None): | |
global pwd | |
if not text_area.edit_modified(): | |
try: | |
path = filedialog.askopenfile(filetypes = (("Text files", "*.txt"), ("All files", "*.*"))).name | |
main_window.title(tittle+' - ' + path) | |
with open(path, 'rb') as f: | |
content = f.read() | |
nonce, ciphertext, tag = content[:16], content[16:len(content)-16], content[-16:] | |
while True: | |
try: | |
if(root): | |
root.update_idletasks() | |
pwd = tk.simpledialog.askstring('Load', 'Password:', show="*").encode() | |
s = cipherAES_GCM(pwd, nonce).decrypt_and_verify(ciphertext, tag).decode() | |
text_area.delete('1.0', tk.END) | |
text_area.insert('1.0', s) | |
text_area.edit_modified(0) | |
break | |
except AttributeError: | |
exit() | |
except ValueError: | |
pass | |
except: | |
pass | |
else: | |
save_file_as() | |
text_area.edit_modified(0) | |
open_file() | |
def save(filex): | |
global pwd | |
if 'pwd' not in globals(): | |
pwd = tk.simpledialog.askstring('Pass', 'Password:', show="*").encode() | |
nonce = Crypto.Random.new().read(16) | |
ciphertext = nonce + b''.join(cipherAES_GCM(pwd, nonce).encrypt_and_digest(text_area.get("1.0", tk.END).encode())) | |
with open(filex, 'wb') as f: | |
f.write(ciphertext) | |
text_area.edit_modified(False) | |
def save_file(e=None): | |
try: | |
path = main_window.title().split('-')[1][1:] | |
except: | |
path = '' | |
if path != '': | |
save(path) | |
else: | |
save_file_as() | |
text_area.edit_modified(0) | |
def save_file_as(): | |
try: | |
path = filedialog.asksaveasfile(filetypes = (("Text files", "*.txt"), ("All files", "*.*"))).name | |
main_window.title(tittle+' - ' + path) | |
except: | |
return | |
save(path) | |
def close(e=None): | |
if not text_area.edit_modified() or tk.messagebox.askokcancel('Exit', "Some modifications have not been saved, do you really want to quit?", default=tk.messagebox.CANCEL): | |
main_window.destroy() | |
def find(e=False, findnext=False): | |
global query | |
if not findnext or 'query' not in globals(): | |
query = tk.simpledialog.askstring('Find', 'Query:') | |
start = "1.0" | |
else: | |
start = text_area.index(tk.INSERT) + "+1c" | |
pos='' | |
if(query): | |
if(len(query)>0): | |
pos = text_area.search(query, start, stopindex="end", nocase=True, regexp=True) | |
if pos != '': | |
text_area.mark_set("insert", pos) | |
text_area.see("insert") | |
text_area.focus() | |
def select_all(event): | |
text_area.tag_add(tk.SEL, "1.0", tk.END) | |
text_area.mark_set(tk.INSERT, "1.0") | |
text_area.see(tk.INSERT) | |
return 'break' | |
main_window = tk.Tk() | |
main_window.bind('<Control-s>', save_file) | |
main_window.bind('<Control-f>', find) | |
main_window.bind('<F3>', lambda e: find(e, True)) | |
main_window.title('Text edit') | |
main_window.geometry('1280x720') | |
main_window.protocol("WM_DELETE_WINDOW", close) | |
menubar = tk.Menu(main_window) | |
file_menu = tk.Menu(menubar, tearoff=0) | |
file_menu.add_command(label="New", command=new_file) | |
file_menu.add_command(label="Open", command=open_file) | |
file_menu.add_command(label="Save", command=save_file) | |
file_menu.add_command(label="Save as...", command=save_file_as) | |
file_menu.add_separator() | |
file_menu.add_command(label="Exit", command=close) | |
menubar.add_cascade(label="File", menu=file_menu) | |
text_area = tk.Text(main_window, wrap='none', bg='#272822', fg="#f8f8f2", insertbackground='#f8f8f2', font=("Consolas", 18)) | |
text_area.pack(expand = tk.YES, fill = tk.BOTH, side = tk.LEFT) | |
main_window.bind('<Control-a>', select_all) | |
scroll_bar = ttk.Scrollbar(main_window, orient=tk.VERTICAL, command=text_area.yview) | |
scroll_bar.pack(fill=tk.Y, side=tk.RIGHT) | |
text_area['yscrollcommand'] = scroll_bar.set | |
s = "Welcome! This is a new file. Basic:\n* Save with CTRL+S\n* Open CTRL+O \n* Find a pattern with CTRL+F or F3" | |
text_area.insert(tk.END, s) | |
text_area.mark_set("insert", "1.0") | |
text_area.edit_modified(False) | |
text_area.focus() | |
main_window.config(menu=menubar) | |
tk.mainloop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment