Change space indent to tabs, added comments

Also removed some redundant code
This commit is contained in:
Evan Su 2021-03-13 12:11:06 -05:00 committed by GitHub
parent a8064641a5
commit 7ba80dd8a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,16 +3,20 @@
# Dependencies: argon2-cffi, pycryptodome # Dependencies: argon2-cffi, pycryptodome
# Copyright (c) Evan Su (https://evansu.cc) # Copyright (c) Evan Su (https://evansu.cc)
# Released under a GNU GPL v3 license # Released under a GNU GPL v3 license
# Source: https://github.com/HACKERALERT/Picocrypt # https://github.com/HACKERALERT/Picocrypt
# Test if libraries are installed
try: try:
from argon2.low_level import hash_secret_raw from argon2.low_level import hash_secret_raw
from Crypto.Cipher import ChaCha20_Poly1305 from Crypto.Cipher import ChaCha20_Poly1305
except: except:
from os import system # Libraries missing, install them
system("python3 -m pip install argon2-cffi --user") from os import system
system("python3 -m pip install pycryptodome --user") system("sudo apt-get install python3-tk")
system("python3 -m pip install argon2-cffi")
system("python3 -m pip install pycryptodome")
# Imports
from tkinter import filedialog,messagebox from tkinter import filedialog,messagebox
from threading import Thread from threading import Thread
from datetime import datetime from datetime import datetime
@ -28,10 +32,13 @@ import tkinter.ttk
import tkinter.scrolledtext import tkinter.scrolledtext
import webbrowser import webbrowser
# Global variables and notices
inputFile = "" inputFile = ""
outputFile = "" outputFile = ""
password = "" password = ""
ad = "" ad = ""
kept = False
working = False
adString = "File metadata (used to store some text along with the file):" adString = "File metadata (used to store some text along with the file):"
passwordNotice = "Error. The provided password is incorrect." passwordNotice = "Error. The provided password is incorrect."
corruptedNotice = "Error. The input file is corrupted." corruptedNotice = "Error. The input file is corrupted."
@ -40,468 +47,531 @@ kCorruptedNotice = "The input file is corrupted, but the output has been kept."
kModifiedNotice = "The input file has been intentionally modified, but the output has been kept." kModifiedNotice = "The input file has been intentionally modified, but the output has been kept."
derivingNotice = "Deriving key (takes a few seconds)..." derivingNotice = "Deriving key (takes a few seconds)..."
keepNotice = "Keep decrypted output even if it's corrupted or modified" keepNotice = "Keep decrypted output even if it's corrupted or modified"
kept = False
eraseNotice = "Securely erase and delete original file" eraseNotice = "Securely erase and delete original file"
working = False
overwriteNotice = "Output file already exists. Would you like to overwrite it?" overwriteNotice = "Output file already exists. Would you like to overwrite it?"
unknownErrorNotice = "Unknown error occured. Please try again." unknownErrorNotice = "Unknown error occured. Please try again."
# Create root Tk
tk = tkinter.Tk() tk = tkinter.Tk()
#tk.tk.call('tk', 'scaling', 2.0)
tk.geometry("480x420") tk.geometry("480x420")
tk.title("Picocrypt") tk.title("Picocrypt")
tk.configure(background="#f5f6f7") tk.configure(background="#f5f6f7")
tk.resizable(0,0) tk.resizable(0,0)
favicon = tkinter.PhotoImage(file="./key.png") # Try setting image if included with Picocrypt
tk.iconphoto(False,favicon) try:
favicon = tkinter.PhotoImage(file="./key.png")
tk.iconphoto(False,favicon)
except:
pass
# Some styling
s = tkinter.ttk.Style() s = tkinter.ttk.Style()
s.configure("TCheckbutton",background="#f5f6f7") s.configure("TCheckbutton",background="#f5f6f7")
# Event when user selects an input file
def inputSelected(): def inputSelected():
global inputFile,working global inputFile,working
dummy.focus() dummy.focus()
try: # Try to handle when select file is cancelled
suffix = "" try:
tmp = filedialog.askopenfilename( # Ask for input file
initialdir=expanduser("~") suffix = ""
) tmp = filedialog.askopenfilename(
if len(tmp)==0: initialdir=expanduser("~")
raise Exception("No file selected.") )
inputFile = tmp if len(tmp)==0:
if ".pcf" in inputFile.split("/")[-1]: # Exception will be caught by except below
suffix = " (will be decrypted)" raise Exception("No file selected.")
fin = open(inputFile,"rb+") inputFile = tmp
adlen = b"" # Decide if encrypting or decrypting
while True: if ".pcf" in inputFile.split("/")[-1]:
letter = fin.read(1) suffix = " (will be decrypted)"
adlen += letter fin = open(inputFile,"rb+")
if letter==b"|": # Read file metadata
adlen = adlen[:-1] adlen = b""
break while True:
ad = fin.read(int(adlen.decode("utf-8"))) letter = fin.read(1)
fin.close() adlen += letter
adArea["state"] = "normal" if letter==b"|":
adArea.delete("1.0",tkinter.END) adlen = adlen[:-1]
adArea.insert("1.0",ad.decode("utf-8")) break
adArea["state"] = "disabled" ad = fin.read(int(adlen.decode("utf-8")))
adLabelString.set("File metadata (read only):") fin.close()
keepBtn["state"] = "normal" # Insert the metadata into its text box
eraseBtn["state"] = "disabled" adArea["state"] = "normal"
else: adArea.delete("1.0",tkinter.END)
eraseBtn["state"] = "normal" adArea.insert("1.0",ad.decode("utf-8"))
keepBtn["state"] = "disabled" adArea["state"] = "disabled"
adArea["state"] = "normal" adLabelString.set("File metadata (read only):")
adArea.delete("1.0",tkinter.END) keepBtn["state"] = "normal"
suffix = " (will be encrypted)" eraseBtn["state"] = "disabled"
adLabelString.set(adString) else:
inputString.set(inputFile.split("/")[-1]+suffix) # Update the UI
passwordInput["state"] = "normal" eraseBtn["state"] = "normal"
passwordInput.delete(0,"end") keepBtn["state"] = "disabled"
startBtn["state"] = "normal" adArea["state"] = "normal"
statusString.set("Ready.") adArea.delete("1.0",tkinter.END)
progress["value"] = 0 suffix = " (will be encrypted)"
except UnicodeDecodeError: adLabelString.set(adString)
passwordInput["state"] = "normal" # Enable password box, etc.
passwordInput.delete(0,"end") inputString.set(inputFile.split("/")[-1]+suffix)
statusString.set(corruptedNotice) passwordInput["state"] = "normal"
except: passwordInput.delete(0,"end")
pass startBtn["state"] = "normal"
finally: statusString.set("Ready.")
dummy.focus() progress["value"] = 0
working = False # File decode error
except UnicodeDecodeError:
passwordInput["state"] = "normal"
passwordInput.delete(0,"end")
statusString.set(corruptedNotice)
# No file selected, do nothing
except:
pass
# Focus the dummy button to remove ugly borders
finally:
dummy.focus()
working = False
# Button to select input file
selectFileInput = tkinter.ttk.Button( selectFileInput = tkinter.ttk.Button(
tk, tk,
text="Select file", text="Select file",
command=inputSelected, command=inputSelected,
) )
selectFileInput.place(x=19,y=20) selectFileInput.place(x=19,y=20)
# Label that displays selected input file
inputString = tkinter.StringVar(tk) inputString = tkinter.StringVar(tk)
inputString.set("Please select a file.") inputString.set("Please select a file.")
selectedInput = tkinter.ttk.Label( selectedInput = tkinter.ttk.Label(
tk, tk,
textvariable=inputString textvariable=inputString
) )
selectedInput.config(background="#f5f6f7") selectedInput.config(background="#f5f6f7")
selectedInput.place(x=104,y=23) selectedInput.place(x=104,y=23)
# Label that prompts user to enter a password
passwordString = tkinter.StringVar(tk) passwordString = tkinter.StringVar(tk)
passwordString.set("Password:") passwordString.set("Password:")
passwordLabel = tkinter.ttk.Label( passwordLabel = tkinter.ttk.Label(
tk, tk,
textvariable=passwordString textvariable=passwordString
) )
passwordLabel.place(x=17,y=56) passwordLabel.place(x=17,y=56)
passwordLabel.config(background="#f5f6f7") passwordLabel.config(background="#f5f6f7")
# A frame to make password input fill width
passwordFrame = tkinter.Frame( passwordFrame = tkinter.Frame(
tk, tk,
width=440, width=440,
height=22 height=22
) )
passwordFrame.place(x=20,y=76) passwordFrame.place(x=20,y=76)
passwordFrame.columnconfigure(0,weight=10) passwordFrame.columnconfigure(0,weight=10)
passwordFrame.grid_propagate(False) passwordFrame.grid_propagate(False)
# Password input box
passwordInput = tkinter.ttk.Entry( passwordInput = tkinter.ttk.Entry(
passwordFrame passwordFrame
) )
passwordInput.grid(sticky="nesw") passwordInput.grid(sticky="nesw")
passwordInput["state"] = "disabled" passwordInput["state"] = "disabled"
# Start the encryption/decryption process
def start(): def start():
global inputFile,outputFile,password,ad,kept,working global inputFile,outputFile,password,ad,kept,working
if ".pcf" not in inputFile: # Decide if encrypting or decrypting
mode = "encrypt" if ".pcf" not in inputFile:
outputFile = inputFile+".pcf" mode = "encrypt"
else: outputFile = inputFile+".pcf"
mode = "decrypt" else:
outputFile = inputFile[:-4] mode = "decrypt"
try: outputFile = inputFile[:-4]
getsize(outputFile)
force = messagebox.askyesno("Warning",overwriteNotice)
dummy.focus()
if force!=1:
return
except:
pass
working = True # Check if file already exists
dummy.focus() try:
password = passwordInput.get().encode("utf-8") getsize(outputFile)
ad = adArea.get("1.0",tkinter.END).encode("utf-8") force = messagebox.askyesno("Warning",overwriteNotice)
wipe = erase.get()==1 dummy.focus()
if force!=1:
return
except:
pass
selectFileInput["state"] = "disabled" # Set and get some variables
eraseBtn["state"] = "disabled" working = True
passwordInput["state"] = "disabled" dummy.focus()
adArea["state"] = "disabled" password = passwordInput.get().encode("utf-8")
startBtn["state"] = "disabled" ad = adArea.get("1.0",tkinter.END).encode("utf-8")
keepBtn["state"] = "disabled" wipe = erase.get()==1
fin = open(inputFile,"rb+") selectFileInput["state"] = "disabled"
fout = open(outputFile,"wb+") passwordInput["state"] = "disabled"
adArea["state"] = "disabled"
startBtn["state"] = "disabled"
keepBtn["state"] = "disabled"
if mode=="encrypt": fin = open(inputFile,"rb+")
salt = urandom(16) fout = open(outputFile,"wb+")
nonce = urandom(24)
fout.write(str(len(ad)).encode("utf-8"))
fout.write(b"|")
fout.write(ad)
fout.write(b"0"*64)
fout.write(b"0"*64)
fout.write(b"0"*16)
fout.write(salt)
fout.write(nonce)
else:
adlen = b""
while True:
letter = fin.read(1)
adlen += letter
if letter==b"|":
adlen = adlen[:-1]
break
fin.read(int(adlen.decode("utf-8")))
cs = fin.read(64)
crccs = fin.read(64)
digest = fin.read(16)
salt = fin.read(16)
nonce = fin.read(24)
statusString.set(derivingNotice) # Generate values for encryption if encrypting
if mode=="encrypt":
salt = urandom(16)
nonce = urandom(24)
fout.write(str(len(ad)).encode("utf-8"))
fout.write(b"|")
fout.write(ad)
fout.write(b"0"*64)
fout.write(b"0"*64)
fout.write(b"0"*16)
fout.write(salt)
fout.write(nonce)
# If decrypting, read values from file
else:
# Read past metadata into actual data
while True:
letter = fin.read(1)
if letter==b"|":
break
fin.read(int(adlen.decode("utf-8")))
cs = fin.read(64)
crccs = fin.read(64)
digest = fin.read(16)
salt = fin.read(16)
nonce = fin.read(24)
progress.config(mode="indeterminate") # Show notice, set progress bar indeterminate
progress.start(15) statusString.set(derivingNotice)
progress.config(mode="indeterminate")
progress.start(15)
key = hash_secret_raw( # Derive argon2id key
password, key = hash_secret_raw(
salt, password,
time_cost=8, salt,
memory_cost=1048576, time_cost=8, # 8 iterations
parallelism=8, memory_cost=2**20, # 2^20 Kilobytes (1GB)
hash_len=32, parallelism=8, # 8 parallel threads
type=Type.ID hash_len=32,
) type=Type.ID
)
progress.stop() # Key deriving done, set progress bar determinate
progress.config(mode="determinate") progress.stop()
progress["value"] = 0 progress.config(mode="determinate")
progress["value"] = 0
check = sha3_512(key).digest() # Compute hash of derived key
check = sha3_512(key).digest()
if mode=="decrypt": # If decrypting, check if key is correct
if not compare_digest(check,cs): if mode=="decrypt":
statusString.set(passwordNotice) # If key is incorrect...
fin.close() if not compare_digest(check,cs):
fout.close() statusString.set(passwordNotice)
remove(outputFile) fin.close()
selectFileInput["state"] = "normal" fout.close()
passwordInput["state"] = "normal" remove(outputFile)
adArea["state"] = "normal" selectFileInput["state"] = "normal"
startBtn["state"] = "normal" passwordInput["state"] = "normal"
keepBtn["state"] = "normal" adArea["state"] = "normal"
working = False startBtn["state"] = "normal"
del key keepBtn["state"] = "normal"
return working = False
del key
return
cipher = ChaCha20_Poly1305.new(key=key,nonce=nonce) # Create XChaCha20-Poly1305 object
crc = sha3_512() cipher = ChaCha20_Poly1305.new(key=key,nonce=nonce)
# Cyclic redundancy check for file corruption
crc = sha3_512()
done = 0 done = 0
total = getsize(inputFile) total = getsize(inputFile)
chunkSize = 2**20 chunkSize = 2**20
startTime = datetime.now() startTime = datetime.now()
if wipe: # If secure wipe enabled, create a wiper object
wiper = open(inputFile,"r+b") if wipe:
wiper.seek(0) wiper = open(inputFile,"r+b")
wiper.seek(0)
while True: # Continously read file in chunks of 1MB
piece = fin.read(chunkSize) while True:
if wipe: piece = fin.read(chunkSize)
trash = urandom(len(piece)) if wipe:
wiper.write(trash) # If securely wipe, write random trash
if not piece: # to original file after reading it
if mode=="encrypt": trash = urandom(len(piece))
digest = cipher.digest() wiper.write(trash)
fout.flush() # If EOF
fout.close() if not piece:
fout = open(outputFile,"r+b") if mode=="encrypt":
fout.seek(len(str(len(ad)))+1+len(ad)) # Get the cipher MAC tag, write to file
fout.write(check) digest = cipher.digest()
fout.write(crc.digest()) fout.flush()
fout.write(digest) fout.close()
else: fout = open(outputFile,"r+b")
crcdg = crc.digest() fout.seek(len(str(len(ad)))+1+len(ad))
if not compare_digest(crccs,crcdg): fout.write(check)
statusString.set(corruptedNotice) fout.write(crc.digest())
progress["value"] = 100 fout.write(digest)
fin.close() else:
fout.close() # If decrypting, verify MAC tag
if keep.get()!=1: crcdg = crc.digest()
remove(outputFile) if not compare_digest(crccs,crcdg):
selectFileInput["state"] = "normal" # File is corrupted
passwordInput["state"] = "normal" statusString.set(corruptedNotice)
adArea["state"] = "normal" progress["value"] = 100
startBtn["state"] = "normal" fin.close()
keepBtn["state"] = "normal" fout.close()
working = False # If keep file checked...
del fin,fout,cipher,key if keep.get()!=1:
return remove(outputFile)
else: selectFileInput["state"] = "normal"
kept = "corrupted" passwordInput["state"] = "normal"
try: adArea["state"] = "normal"
cipher.verify(digest) startBtn["state"] = "normal"
except: keepBtn["state"] = "normal"
statusString.set(modifiedNotice) working = False
progress["value"] = 100 del fin,fout,cipher,key
fin.close() return
fout.close() else:
if keep.get()!=1: kept = "corrupted"
remove(outputFile) try:
selectFileInput["state"] = "normal" # Throws ValueError if incorrect
passwordInput["state"] = "normal" cipher.verify(digest)
adArea["state"] = "normal" except:
startBtn["state"] = "normal" # File is modified
keepBtn["state"] = "normal" statusString.set(modifiedNotice)
working = False progress["value"] = 100
del fin,fout,cipher,key fin.close()
return fout.close()
else: # If keep file checked...
kept = "modified" if keep.get()!=1:
break remove(outputFile)
selectFileInput["state"] = "normal"
if mode=="encrypt": passwordInput["state"] = "normal"
data = cipher.encrypt(piece) adArea["state"] = "normal"
crc.update(data) startBtn["state"] = "normal"
else: keepBtn["state"] = "normal"
crc.update(piece) working = False
data = cipher.decrypt(piece) del fin,fout,cipher,key
return
else:
kept = "modified"
break
# Encrypt/decrypt chunk and update CRC
if mode=="encrypt":
data = cipher.encrypt(piece)
crc.update(data)
else:
crc.update(piece)
data = cipher.decrypt(piece)
first = False # Calculate speed, ETA, etc.
elapsed = (datetime.now()-startTime).total_seconds() first = False
if elapsed==0: elapsed = (datetime.now()-startTime).total_seconds()
elapsed = 0.1**6 if elapsed==0:
percent = done*100/total elapsed = 0.1**6
progress["value"] = percent percent = done*100/total
rPercent = round(percent) progress["value"] = percent
speed = (done/elapsed)/10**6 rPercent = round(percent)
if speed==0: speed = (done/elapsed)/10**6
first = True if speed==0:
speed = 0.1**6 first = True
rSpeed = round(speed) speed = 0.1**6
eta = round((total-done)/(speed*10**6)) rSpeed = round(speed)
if first: eta = round((total-done)/(speed*10**6))
statusString.set("...% at ... MB/s (ETA: ...s)") if first:
else: statusString.set("...% at ... MB/s (ETA: ...s)")
info = f"{rPercent}% at {rSpeed} MB/s (ETA: {eta}s)" else:
statusString.set(info) info = f"{rPercent}% at {rSpeed} MB/s (ETA: {eta}s)"
statusString.set(info)
done += chunkSize
fout.write(data) done += chunkSize
fout.write(data)
if not kept: # Show appropriate notice if file corrupted or modified
if mode=="encrypt": if not kept:
output = inputFile.split("/")[-1]+".pcf" if mode=="encrypt":
else: output = inputFile.split("/")[-1]+".pcf"
output = inputFile.split("/")[-1].replace(".pcf","") else:
statusString.set(f"Completed. (Output: {output})") output = inputFile.split("/")[-1].replace(".pcf","")
else: statusString.set(f"Completed. (Output: {output})")
if kept=="modified": else:
statusString.set(kModifiedNotice) if kept=="modified":
else: statusString.set(kModifiedNotice)
statusString.set(kCorruptedNotice) else:
selectFileInput["state"] = "normal" statusString.set(kCorruptedNotice)
adArea["state"] = "normal"
adArea.delete("1.0",tkinter.END) # Reset variables and UI states
adArea["state"] = "disabled" selectFileInput["state"] = "normal"
startBtn["state"] = "disabled" adArea["state"] = "normal"
passwordInput["state"] = "normal" adArea.delete("1.0",tkinter.END)
passwordInput.delete(0,"end") adArea["state"] = "disabled"
passwordInput["state"] = "disabled" startBtn["state"] = "disabled"
progress["value"] = 0 passwordInput["state"] = "normal"
inputString.set("Please select a file.") passwordInput.delete(0,"end")
keepBtn["state"] = "normal" passwordInput["state"] = "disabled"
keep.set(0) progress["value"] = 0
keepBtn["state"] = "disabled" inputString.set("Please select a file.")
eraseBtn["state"] = "normal" keepBtn["state"] = "normal"
erase.set(0) keep.set(0)
eraseBtn["state"] = "disabled" keepBtn["state"] = "disabled"
if not kept: eraseBtn["state"] = "normal"
fout.flush() erase.set(0)
fsync(fout.fileno()) eraseBtn["state"] = "disabled"
fout.close() if not kept:
fin.close() fout.flush()
if wipe: fsync(fout.fileno())
wiper.flush() fout.close()
fsync(wiper.fileno()) fin.close()
wiper.close() if wipe:
remove(inputFile) # Make sure to flush file
inputFile = "" wiper.flush()
outputFile = "" fsync(wiper.fileno())
password = "" wiper.close()
ad = "" remove(inputFile)
kept = False inputFile = ""
working = False outputFile = ""
del fin,fout,cipher,key password = ""
ad = ""
kept = False
working = False
# Wipe keys for safety
del fin,fout,cipher,key
# Wraps the start() function with error handling
def wrapper(): def wrapper():
global working global working
try: # Try start() and handle errors
start() try:
except: start()
selectFileInput["state"] = "normal" except:
passwordInput["state"] = "normal" selectFileInput["state"] = "normal"
adArea["state"] = "normal" passwordInput["state"] = "normal"
startBtn["state"] = "normal" adArea["state"] = "normal"
keepBtn["state"] = "normal" startBtn["state"] = "normal"
statusString.set(unknownErrorNotice) keepBtn["state"] = "normal"
dummy.focus() statusString.set(unknownErrorNotice)
working = False dummy.focus()
finally: working = False
sys.exit(0) finally:
sys.exit(0)
def startWorker():
thread = Thread(target=wrapper,daemon=True)
thread.start()
# Encryption/decrypt is done is a separate thread
# so the UI isn't blocked. This is a wrapper
# to spawn a thread and start it.
def startWorker():
thread = Thread(target=wrapper,daemon=True)
thread.start()
# ad stands for "associated data"/metadata
adLabelString = tkinter.StringVar(tk) adLabelString = tkinter.StringVar(tk)
adLabelString.set(adString) adLabelString.set(adString)
adLabel = tkinter.ttk.Label( adLabel = tkinter.ttk.Label(
tk, tk,
textvariable=adLabelString textvariable=adLabelString
) )
adLabel.place(x=17,y=108) adLabel.place(x=17,y=108)
adLabel.config(background="#f5f6f7") adLabel.config(background="#f5f6f7")
# Frame so metadata text box can fill width
adFrame = tkinter.Frame( adFrame = tkinter.Frame(
tk, tk,
width=440, width=440,
height=100 height=100
) )
adFrame.place(x=20,y=128) adFrame.place(x=20,y=128)
adFrame.columnconfigure(0,weight=10) adFrame.columnconfigure(0,weight=10)
adFrame.grid_propagate(False) adFrame.grid_propagate(False)
# Metadata text box
adArea = tkinter.Text( adArea = tkinter.Text(
adFrame, adFrame,
exportselection=0 exportselection=0
) )
adArea.config(font=("Consolas",12)) adArea.config(font=("Consolas",12))
adArea.grid(sticky="we") adArea.grid(sticky="we")
adArea["state"] = "disabled" adArea["state"] = "disabled"
# Check box for keeping corrupted/modified output
keep = tkinter.IntVar() keep = tkinter.IntVar()
keepBtn = tkinter.ttk.Checkbutton( keepBtn = tkinter.ttk.Checkbutton(
tk, tk,
text=keepNotice, text=keepNotice,
variable=keep, variable=keep,
onvalue=1, onvalue=1,
offvalue=0, offvalue=0,
command=lambda:dummy.focus() command=lambda:dummy.focus()
) )
keepBtn.place(x=18,y=240) keepBtn.place(x=18,y=240)
keepBtn["state"] = "disabled" keepBtn["state"] = "disabled"
# Check box for securely erasing original file
erase = tkinter.IntVar() erase = tkinter.IntVar()
eraseBtn = tkinter.ttk.Checkbutton( eraseBtn = tkinter.ttk.Checkbutton(
tk, tk,
text=eraseNotice, text=eraseNotice,
variable=erase, variable=erase,
onvalue=1, onvalue=1,
offvalue=0, offvalue=0,
command=lambda:dummy.focus() command=lambda:dummy.focus()
) )
eraseBtn.place(x=18,y=260) eraseBtn.place(x=18,y=260)
eraseBtn["state"] = "disabled" eraseBtn["state"] = "disabled"
# Frame so start button can fill width
startFrame = tkinter.Frame( startFrame = tkinter.Frame(
tk, tk,
width=442, width=442,
height=25 height=25
) )
startFrame.place(x=19,y=290) startFrame.place(x=19,y=290)
startFrame.columnconfigure(0,weight=10) startFrame.columnconfigure(0,weight=10)
startFrame.grid_propagate(False) startFrame.grid_propagate(False)
# Start button
startBtn = tkinter.ttk.Button( startBtn = tkinter.ttk.Button(
startFrame, startFrame,
text="Start", text="Start",
command=startWorker command=startWorker
) )
startBtn.grid(sticky="nesw") startBtn.grid(sticky="nesw")
startBtn["state"] = "disabled" startBtn["state"] = "disabled"
# Progress bar
progress = tkinter.ttk.Progressbar( progress = tkinter.ttk.Progressbar(
tk, tk,
orient=tkinter.HORIZONTAL, orient=tkinter.HORIZONTAL,
length=440, length=440,
mode="determinate" mode="determinate"
) )
progress.place(x=20,y=328) progress.place(x=20,y=328)
# Status label
statusString = tkinter.StringVar(tk) statusString = tkinter.StringVar(tk)
statusString.set("Ready.") statusString.set("Ready.")
status = tkinter.ttk.Label( status = tkinter.ttk.Label(
tk, tk,
textvariable=statusString textvariable=statusString
) )
status.place(x=17,y=356) status.place(x=17,y=356)
status.config(background="#f5f6f7") status.config(background="#f5f6f7")
# Credits :)
hint = "Created by Evan Su. Click for details and source." hint = "Created by Evan Su. Click for details and source."
creditsString = tkinter.StringVar(tk) creditsString = tkinter.StringVar(tk)
creditsString.set(hint) creditsString.set(hint)
credits = tkinter.ttk.Label( credits = tkinter.ttk.Label(
tk, tk,
textvariable=creditsString, textvariable=creditsString,
cursor="hand2" cursor="hand2"
) )
credits["state"] = "disabled" credits["state"] = "disabled"
credits.config(background="#f5f6f7") credits.config(background="#f5f6f7")
@ -509,26 +579,31 @@ credits.place(x=17,y=386)
source = "https://github.com/HACKERALERT/Picocrypt" source = "https://github.com/HACKERALERT/Picocrypt"
credits.bind("<Button-1>",lambda e:webbrowser.open(source)) credits.bind("<Button-1>",lambda e:webbrowser.open(source))
# Version
versionString = tkinter.StringVar(tk) versionString = tkinter.StringVar(tk)
versionString.set("v1.5") versionString.set("v1.4")
version = tkinter.ttk.Label( version = tkinter.ttk.Label(
tk, tk,
textvariable=versionString textvariable=versionString
) )
version["state"] = "disabled" version["state"] = "disabled"
version.config(background="#f5f6f7") version.config(background="#f5f6f7")
version.place(x=436,y=386) version.place(x=436,y=386)
# Dummy button to remove focus from other buttons
# and prevent ugly border highlighting
dummy = tkinter.ttk.Button( dummy = tkinter.ttk.Button(
tk tk
) )
dummy.place(x=480,y=0) dummy.place(x=480,y=0)
# Close window only if not encryption or decrypting
def onClose(): def onClose():
if not working: if not working:
tk.destroy() tk.destroy()
# Main tkinter loop
if __name__=="__main__": if __name__=="__main__":
tk.protocol("WM_DELETE_WINDOW",onClose) tk.protocol("WM_DELETE_WINDOW",onClose)
tk.mainloop() tk.mainloop()
sys.exit(0) sys.exit(0)