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