Update Picocrypt.py
This commit is contained in:
parent
d101b779f5
commit
a2ab95ec53
637
src/Picocrypt.py
637
src/Picocrypt.py
|
@ -1,34 +1,17 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Dependencies: argon2-cffi, pycryptodome, reedsolo
|
|
||||||
|
Picocrypt v1.11 (Beta)
|
||||||
|
Dependencies: argon2-cffi, pycryptodome, reedsolo, tkinterdnd2
|
||||||
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
|
||||||
https://github.com/HACKERALERT/Picocrypt
|
https://github.com/HACKERALERT/Picocrypt
|
||||||
|
|
||||||
|
~ In cryptography we trust ~
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Test if libraries are installed
|
|
||||||
try:
|
|
||||||
from argon2.low_level import hash_secret_raw
|
|
||||||
from Crypto.Cipher import ChaCha20_Poly1305
|
|
||||||
try:
|
|
||||||
from creedsolo import ReedSolomonError
|
|
||||||
except:
|
|
||||||
from reedsolo import ReedSolomonError
|
|
||||||
except:
|
|
||||||
# Libraries missing, install them
|
|
||||||
from os import system
|
|
||||||
try:
|
|
||||||
# Debian/Ubuntu based
|
|
||||||
system("sudo apt-get install python3-tk")
|
|
||||||
except:
|
|
||||||
# Fedora
|
|
||||||
system("sudo dnf install python3-tkinter")
|
|
||||||
|
|
||||||
system("python3 -m pip install argon2-cffi --no-cache-dir")
|
|
||||||
system("python3 -m pip install pycryptodome --no-cache-dir")
|
|
||||||
system("python3 -m pip install reedsolo --no-cache-dir")
|
|
||||||
|
|
||||||
# Imports
|
# Imports
|
||||||
from tkinter import filedialog,messagebox
|
from tkinter import filedialog,messagebox
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
@ -37,17 +20,22 @@ from argon2.low_level import hash_secret_raw,Type
|
||||||
from Crypto.Cipher import ChaCha20_Poly1305
|
from Crypto.Cipher import ChaCha20_Poly1305
|
||||||
from Crypto.Hash import SHA3_512 as sha3_512
|
from Crypto.Hash import SHA3_512 as sha3_512
|
||||||
from secrets import compare_digest
|
from secrets import compare_digest
|
||||||
from os import urandom,fsync,remove
|
from os import urandom,fsync,remove,system
|
||||||
from os.path import getsize,expanduser
|
from os.path import getsize,expanduser,isdir
|
||||||
|
from os.path import dirname,abspath,realpath
|
||||||
|
from os.path import join as pathJoin
|
||||||
|
from os.path import split as pathSplit
|
||||||
|
from tkinterdnd2 import TkinterDnD,DND_FILES
|
||||||
|
from zipfile import ZipFile
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import rmtree
|
||||||
import sys
|
import sys
|
||||||
import tkinter
|
import tkinter
|
||||||
import tkinter.ttk
|
import tkinter.ttk
|
||||||
import tkinter.scrolledtext
|
import tkinter.scrolledtext
|
||||||
import webbrowser
|
import webbrowser
|
||||||
try:
|
import platform
|
||||||
from creedsolo import RSCodec,ReedSolomonError
|
from creedsolo import RSCodec,ReedSolomonError
|
||||||
except:
|
|
||||||
from reedsolo import RSCodec,ReedSolomonError
|
|
||||||
|
|
||||||
# Tk/Tcl is a little barbaric, so I'm disabling
|
# Tk/Tcl is a little barbaric, so I'm disabling
|
||||||
# high DPI so it doesn't scale bad and look horrible
|
# high DPI so it doesn't scale bad and look horrible
|
||||||
|
@ -57,16 +45,22 @@ try:
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Global variables and notices
|
# Global variables and strings
|
||||||
|
rootDir = dirname(realpath(__file__))
|
||||||
inputFile = ""
|
inputFile = ""
|
||||||
outputFile = ""
|
outputFile = ""
|
||||||
|
outputPath = ""
|
||||||
password = ""
|
password = ""
|
||||||
ad = ""
|
ad = ""
|
||||||
kept = False
|
kept = False
|
||||||
working = False
|
working = False
|
||||||
gMode = None
|
gMode = None
|
||||||
headerRsc = None
|
headerRsc = False
|
||||||
|
allFiles = False
|
||||||
|
draggedFolderPaths = False
|
||||||
|
files = 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):"
|
||||||
|
compressingNotice = "Compressing files together..."
|
||||||
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."
|
||||||
veryCorruptedNotice = "Error. The input file and header keys are badly corrupted."
|
veryCorruptedNotice = "Error. The input file and header keys are badly corrupted."
|
||||||
|
@ -77,16 +71,21 @@ kVeryCorruptedNotice = "The input file is badly corrupted, but the output has be
|
||||||
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"
|
||||||
eraseNotice = "Securely erase and delete original file"
|
eraseNotice = "Securely erase and delete original file"
|
||||||
|
erasingNotice = "Securely erasing original file(s)..."
|
||||||
overwriteNotice = "Output file already exists. Would you like to overwrite it?"
|
overwriteNotice = "Output file already exists. Would you like to overwrite it?"
|
||||||
|
cancelNotice = "Exiting now will lead to broken output. Are you sure?"
|
||||||
rsNotice = "Prevent corruption using Reed-Solomon"
|
rsNotice = "Prevent corruption using Reed-Solomon"
|
||||||
rscNotice = "Creating Reed-Solomon tables..."
|
rscNotice = "Creating Reed-Solomon tables..."
|
||||||
unknownErrorNotice = "Unknown error occured. Please try again."
|
unknownErrorNotice = "Unknown error occured. Please try again."
|
||||||
|
|
||||||
# Create root Tk
|
# Create root Tk
|
||||||
tk = tkinter.Tk()
|
tk = TkinterDnD.Tk()
|
||||||
tk.geometry("480x480")
|
tk.geometry("480x470")
|
||||||
tk.title("Picocrypt")
|
tk.title("Picocrypt")
|
||||||
tk.configure(background="#f5f6f7")
|
if platform.system()=="Darwin":
|
||||||
|
tk.configure(background="#edeced")
|
||||||
|
else:
|
||||||
|
tk.configure(background="#ffffff")
|
||||||
tk.resizable(0,0)
|
tk.resizable(0,0)
|
||||||
|
|
||||||
# Try setting window icon if included with Picocrypt
|
# Try setting window icon if included with Picocrypt
|
||||||
|
@ -98,29 +97,85 @@ except:
|
||||||
|
|
||||||
# Some styling
|
# Some styling
|
||||||
s = tkinter.ttk.Style()
|
s = tkinter.ttk.Style()
|
||||||
s.configure("TCheckbutton",background="#f5f6f7")
|
s.configure("TCheckbutton",background="#ffffff")
|
||||||
|
|
||||||
# Event when user selects an input file
|
# Event when user drags file(s) and folder(s) into window
|
||||||
def inputSelected():
|
def inputSelected(draggedFile):
|
||||||
global inputFile,working,headerRsc
|
global inputFile,working,headerRsc,allFiles,draggedFolderPaths,files
|
||||||
|
resetUI()
|
||||||
dummy.focus()
|
dummy.focus()
|
||||||
|
status.config(cursor="")
|
||||||
|
status.bind("<Button-1>",lambda e:None)
|
||||||
|
|
||||||
# Try to handle when select file is cancelled
|
# Use try to handle errors
|
||||||
try:
|
try:
|
||||||
# Ask for input file
|
# Create list of input files
|
||||||
|
allFiles = []
|
||||||
|
files = []
|
||||||
|
draggedFolderPaths = []
|
||||||
suffix = ""
|
suffix = ""
|
||||||
tmp = filedialog.askopenfilename(
|
tmp = [i for i in draggedFile]
|
||||||
initialdir=expanduser("~")
|
res = []
|
||||||
)
|
within = False
|
||||||
if len(tmp)==0:
|
tmpName = ""
|
||||||
# Exception will be caught by except below
|
|
||||||
raise Exception("No file selected.")
|
"""
|
||||||
inputFile = tmp
|
The next for loop parses data return by tkinterdnd2's file drop method.
|
||||||
|
When files and folders are dragged, the output (the 'draggedFile' parameter)
|
||||||
|
will contain the dropped files/folders and will look something like this:
|
||||||
|
|
||||||
|
A single file/folder: "C:\Foo\Bar.txt"
|
||||||
|
A single file/folder with a space in path: "{C:\Foo Bar\Lorem.txt}"
|
||||||
|
Multiple files/folders: "C:\Foo\Bar1.txt C:\Foo\Ba2.txt"
|
||||||
|
Multiple files/folders with spaces in paths:
|
||||||
|
- "C:\Foo\Bar1.txt {C:\Foo Bar\Lorem.txt}"
|
||||||
|
- "{C:\Foo Bar\Lorem.txt} C:\Foo\Bar1.txt"
|
||||||
|
- "{C:\Foo Bar\Lorem1.txt} {C:\Foo Bar\Lorem2.txt}"
|
||||||
|
"""
|
||||||
|
for i in tmp:
|
||||||
|
if i=="{":
|
||||||
|
within = True
|
||||||
|
elif i=="}":
|
||||||
|
within = False
|
||||||
|
res.append(tmpName)
|
||||||
|
tmpName = ""
|
||||||
|
else:
|
||||||
|
if i==" " and not within:
|
||||||
|
if tmpName!="":
|
||||||
|
res.append(tmpName)
|
||||||
|
tmpName = ""
|
||||||
|
else:
|
||||||
|
tmpName += i
|
||||||
|
if tmpName:
|
||||||
|
res.append(tmpName)
|
||||||
|
|
||||||
|
allFiles = []
|
||||||
|
files = []
|
||||||
|
|
||||||
|
# Check each thing dragged by user
|
||||||
|
for i in res:
|
||||||
|
# If there is a directory, recursively add all files to 'allFiles'
|
||||||
|
if isdir(i):
|
||||||
|
# Record the directory for secure wipe (if necessary)
|
||||||
|
draggedFolderPaths.append(i)
|
||||||
|
tmp = Path(i).rglob("*")
|
||||||
|
for p in tmp:
|
||||||
|
allFiles.append(abspath(p))
|
||||||
|
# Just a file, add it to files
|
||||||
|
else:
|
||||||
|
files.append(i)
|
||||||
|
|
||||||
|
# If there's only one file, set it as input file
|
||||||
|
if len(files)==1 and len(allFiles)==0:
|
||||||
|
inputFile = files[0]
|
||||||
|
files = []
|
||||||
|
else:
|
||||||
|
inputFile = ""
|
||||||
|
|
||||||
# Decide if encrypting or decrypting
|
# Decide if encrypting or decrypting
|
||||||
if ".pcv" in inputFile.split("/")[-1]:
|
if inputFile.endswith(".pcv"):
|
||||||
suffix = " (will decrypt)"
|
suffix = " (will decrypt)"
|
||||||
fin = open(inputFile,"r+b")
|
fin = open(inputFile,"rb")
|
||||||
|
|
||||||
# Read file metadata (a little complex)
|
# Read file metadata (a little complex)
|
||||||
tmp = fin.read(139)
|
tmp = fin.read(139)
|
||||||
|
@ -148,6 +203,8 @@ def inputSelected():
|
||||||
adArea.delete("1.0",tkinter.END)
|
adArea.delete("1.0",tkinter.END)
|
||||||
adArea.insert("1.0",ad)
|
adArea.insert("1.0",ad)
|
||||||
adArea["state"] = "disabled"
|
adArea["state"] = "disabled"
|
||||||
|
|
||||||
|
# Update UI
|
||||||
adLabelString.set("File metadata (read only):")
|
adLabelString.set("File metadata (read only):")
|
||||||
keepBtn["state"] = "normal"
|
keepBtn["state"] = "normal"
|
||||||
eraseBtn["state"] = "disabled"
|
eraseBtn["state"] = "disabled"
|
||||||
|
@ -155,6 +212,7 @@ def inputSelected():
|
||||||
cpasswordInput["state"] = "normal"
|
cpasswordInput["state"] = "normal"
|
||||||
cpasswordInput.delete(0,"end")
|
cpasswordInput.delete(0,"end")
|
||||||
cpasswordInput["state"] = "disabled"
|
cpasswordInput["state"] = "disabled"
|
||||||
|
cpasswordString.set("Confirm password (N/A):")
|
||||||
else:
|
else:
|
||||||
# Update the UI
|
# Update the UI
|
||||||
eraseBtn["state"] = "normal"
|
eraseBtn["state"] = "normal"
|
||||||
|
@ -166,13 +224,33 @@ def inputSelected():
|
||||||
adLabelString.set(adString)
|
adLabelString.set(adString)
|
||||||
cpasswordInput["state"] = "normal"
|
cpasswordInput["state"] = "normal"
|
||||||
cpasswordInput.delete(0,"end")
|
cpasswordInput.delete(0,"end")
|
||||||
|
cpasswordString.set("Confirm password:")
|
||||||
|
cpasswordLabel["state"] = "normal"
|
||||||
|
adLabel["state"] = "normal"
|
||||||
|
|
||||||
|
nFiles = len(files)
|
||||||
|
nFolders = len(draggedFolderPaths)
|
||||||
|
|
||||||
|
# Show selected file(s) and folder(s)
|
||||||
|
if (allFiles or files) and not draggedFolderPaths:
|
||||||
|
inputString.set(f"{nFiles} files selected (will encrypt).")
|
||||||
|
elif draggedFolderPaths and not files:
|
||||||
|
inputString.set(f"{nFolders} folder{'s' if nFolders!=1 else ''} selected (will encrypt).")
|
||||||
|
elif draggedFolderPaths and (allFiles or files):
|
||||||
|
inputString.set(
|
||||||
|
f"{nFiles} file{'s' if nFiles!=1 else ''} and "+
|
||||||
|
f"{nFolders} folder{'s' if nFolders!=1 else ''} selected (will encrypt)."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
inputString.set(inputFile.split("/")[-1]+suffix)
|
||||||
|
|
||||||
# Enable password box, etc.
|
# Enable password box, etc.
|
||||||
inputString.set(inputFile.split("/")[-1]+suffix)
|
|
||||||
passwordInput["state"] = "normal"
|
passwordInput["state"] = "normal"
|
||||||
passwordInput.delete(0,"end")
|
passwordInput.delete(0,"end")
|
||||||
|
passwordLabel["state"] = "normal"
|
||||||
startBtn["state"] = "normal"
|
startBtn["state"] = "normal"
|
||||||
statusString.set("Ready.")
|
statusString.set("Ready.")
|
||||||
|
status["state"] = "enabled"
|
||||||
progress["value"] = 0
|
progress["value"] = 0
|
||||||
|
|
||||||
# File decode error
|
# File decode error
|
||||||
|
@ -180,32 +258,54 @@ def inputSelected():
|
||||||
statusString.set(corruptedNotice)
|
statusString.set(corruptedNotice)
|
||||||
progress["value"] = 100
|
progress["value"] = 100
|
||||||
|
|
||||||
# No file selected, do nothing
|
# No file(s) selected, do nothing
|
||||||
except:
|
except:
|
||||||
pass
|
inputString.set("Drag and drop file(s) and folder(s) into this window.")
|
||||||
|
resetUI()
|
||||||
|
|
||||||
# Focus the dummy button to remove ugly borders
|
# Focus the dummy button to remove ugly borders
|
||||||
finally:
|
finally:
|
||||||
dummy.focus()
|
dummy.focus()
|
||||||
working = False
|
working = False
|
||||||
|
|
||||||
# Button to select input file
|
# Clears the selected files
|
||||||
selectFileInput = tkinter.ttk.Button(
|
def clearInputs():
|
||||||
tk,
|
dummy.focus()
|
||||||
text="Select file",
|
resetUI()
|
||||||
command=inputSelected,
|
|
||||||
)
|
# Allow drag and drop
|
||||||
selectFileInput.place(x=19,y=20)
|
def onDrop(e):
|
||||||
|
global working
|
||||||
|
if not working:
|
||||||
|
inputSelected(e.data)
|
||||||
|
tk.drop_target_register(DND_FILES)
|
||||||
|
tk.dnd_bind("<<Drop>>",onDrop)
|
||||||
|
|
||||||
# Label that displays selected input file
|
# Label that displays selected input file
|
||||||
inputString = tkinter.StringVar(tk)
|
inputString = tkinter.StringVar(tk)
|
||||||
inputString.set("Please select a file.")
|
inputString.set("Drag and drop file(s) and folder(s) into this window.")
|
||||||
selectedInput = tkinter.ttk.Label(
|
selectedInput = tkinter.ttk.Label(
|
||||||
tk,
|
tk,
|
||||||
textvariable=inputString
|
textvariable=inputString
|
||||||
)
|
)
|
||||||
selectedInput.config(background="#f5f6f7")
|
selectedInput.config(background="#ffffff")
|
||||||
selectedInput.place(x=104,y=23)
|
selectedInput.place(x=17,y=16)
|
||||||
|
|
||||||
|
# Clear input files
|
||||||
|
clearInput = tkinter.ttk.Button(
|
||||||
|
tk,
|
||||||
|
text="Clear",
|
||||||
|
command=clearInputs
|
||||||
|
)
|
||||||
|
if platform.system()=="Darwin":
|
||||||
|
clearInput.place(x=398,y=14,width=64,height=24)
|
||||||
|
else:
|
||||||
|
clearInput.place(x=421,y=14,width=40,height=24)
|
||||||
|
|
||||||
|
separator = tkinter.ttk.Separator(
|
||||||
|
tk
|
||||||
|
)
|
||||||
|
separator.place(x=20,y=36,width=440)
|
||||||
|
|
||||||
# Label that prompts user to enter a password
|
# Label that prompts user to enter a password
|
||||||
passwordString = tkinter.StringVar(tk)
|
passwordString = tkinter.StringVar(tk)
|
||||||
|
@ -214,16 +314,17 @@ passwordLabel = tkinter.ttk.Label(
|
||||||
tk,
|
tk,
|
||||||
textvariable=passwordString
|
textvariable=passwordString
|
||||||
)
|
)
|
||||||
passwordLabel.place(x=17,y=56)
|
passwordLabel.place(x=17,y=46)
|
||||||
passwordLabel.config(background="#f5f6f7")
|
passwordLabel.config(background="#ffffff")
|
||||||
|
passwordLabel["state"] = "disabled"
|
||||||
|
|
||||||
# A frame to make password input fill width
|
# A frame to make password input fill width
|
||||||
passwordFrame = tkinter.Frame(
|
passwordFrame = tkinter.Frame(
|
||||||
tk,
|
tk,
|
||||||
width=440,
|
width=(445 if platform.system()=="Darwin" else 440),
|
||||||
height=22
|
height=22
|
||||||
)
|
)
|
||||||
passwordFrame.place(x=20,y=76)
|
passwordFrame.place(x=(17 if platform.system()=="Darwin" else 20),y=66)
|
||||||
passwordFrame.columnconfigure(0,weight=10)
|
passwordFrame.columnconfigure(0,weight=10)
|
||||||
passwordFrame.grid_propagate(False)
|
passwordFrame.grid_propagate(False)
|
||||||
# Password input box
|
# Password input box
|
||||||
|
@ -240,16 +341,17 @@ cpasswordLabel = tkinter.ttk.Label(
|
||||||
tk,
|
tk,
|
||||||
textvariable=cpasswordString
|
textvariable=cpasswordString
|
||||||
)
|
)
|
||||||
cpasswordLabel.place(x=17,y=106)
|
cpasswordLabel.place(x=17,y=96)
|
||||||
cpasswordLabel.config(background="#f5f6f7")
|
cpasswordLabel.config(background="#ffffff")
|
||||||
|
cpasswordLabel["state"] = "disabled"
|
||||||
|
|
||||||
# A frame to make confirm password input fill width
|
# A frame to make confirm password input fill width
|
||||||
cpasswordFrame = tkinter.Frame(
|
cpasswordFrame = tkinter.Frame(
|
||||||
tk,
|
tk,
|
||||||
width=440,
|
width=(445 if platform.system()=="Darwin" else 440),
|
||||||
height=22
|
height=22
|
||||||
)
|
)
|
||||||
cpasswordFrame.place(x=20,y=126)
|
cpasswordFrame.place(x=(17 if platform.system()=="Darwin" else 20),y=116)
|
||||||
cpasswordFrame.columnconfigure(0,weight=10)
|
cpasswordFrame.columnconfigure(0,weight=10)
|
||||||
cpasswordFrame.grid_propagate(False)
|
cpasswordFrame.grid_propagate(False)
|
||||||
# Confirm password input box
|
# Confirm password input box
|
||||||
|
@ -262,7 +364,9 @@ cpasswordInput["state"] = "disabled"
|
||||||
|
|
||||||
# Start the encryption/decryption process
|
# Start the encryption/decryption process
|
||||||
def start():
|
def start():
|
||||||
global inputFile,outputFile,password,ad,kept,working,gMode,headerRsc
|
global inputFile,outputFile,password,ad,kept
|
||||||
|
global working,gMode,headerRsc,allFiles,files
|
||||||
|
global dragFolderPath
|
||||||
dummy.focus()
|
dummy.focus()
|
||||||
reedsolo = False
|
reedsolo = False
|
||||||
chunkSize = 2**20
|
chunkSize = 2**20
|
||||||
|
@ -277,7 +381,7 @@ def start():
|
||||||
mode = "decrypt"
|
mode = "decrypt"
|
||||||
gMode = "decrypt"
|
gMode = "decrypt"
|
||||||
# Check if Reed-Solomon was enabled by checking for "+"
|
# Check if Reed-Solomon was enabled by checking for "+"
|
||||||
test = open(inputFile,"rb+")
|
test = open(inputFile,"rb")
|
||||||
decider = test.read(1).decode("utf-8")
|
decider = test.read(1).decode("utf-8")
|
||||||
test.close()
|
test.close()
|
||||||
if decider=="+":
|
if decider=="+":
|
||||||
|
@ -288,7 +392,7 @@ def start():
|
||||||
# Check if file already exists (getsize() throws error if file not found)
|
# Check if file already exists (getsize() throws error if file not found)
|
||||||
try:
|
try:
|
||||||
getsize(outputFile)
|
getsize(outputFile)
|
||||||
force = messagebox.askyesno("Warning",overwriteNotice)
|
force = messagebox.askyesno("Confirmation",overwriteNotice)
|
||||||
dummy.focus()
|
dummy.focus()
|
||||||
if force!=1:
|
if force!=1:
|
||||||
return
|
return
|
||||||
|
@ -296,26 +400,11 @@ def start():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Disable inputs and buttons while encrypting/decrypting
|
# Disable inputs and buttons while encrypting/decrypting
|
||||||
selectFileInput["state"] = "disabled"
|
disableAllInputs()
|
||||||
passwordInput["state"] = "disabled"
|
|
||||||
cpasswordInput["state"] = "disabled"
|
|
||||||
adArea["state"] = "disabled"
|
|
||||||
startBtn["state"] = "disabled"
|
|
||||||
eraseBtn["state"] = "disabled"
|
|
||||||
keepBtn["state"] = "disabled"
|
|
||||||
rsBtn["state"] = "disabled"
|
|
||||||
|
|
||||||
# Make sure passwords match
|
# Make sure passwords match
|
||||||
if passwordInput.get()!=cpasswordInput.get() and mode=="encrypt":
|
if passwordInput.get()!=cpasswordInput.get() and mode=="encrypt":
|
||||||
selectFileInput["state"] = "normal"
|
resetEncryptionUI()
|
||||||
passwordInput["state"] = "normal"
|
|
||||||
cpasswordInput["state"] = "normal"
|
|
||||||
adArea["state"] = "normal"
|
|
||||||
startBtn["state"] = "normal"
|
|
||||||
eraseBtn["state"] = "normal"
|
|
||||||
rsBtn["state"] = "normal"
|
|
||||||
working = False
|
|
||||||
progress["value"] = 100
|
|
||||||
statusString.set("Passwords don't match.")
|
statusString.set("Passwords don't match.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -329,6 +418,27 @@ def start():
|
||||||
# 13 bytes per 128 bytes, ~10% larger output file
|
# 13 bytes per 128 bytes, ~10% larger output file
|
||||||
rsc = RSCodec(13)
|
rsc = RSCodec(13)
|
||||||
|
|
||||||
|
# Compress files together if user dragged multiple files
|
||||||
|
if allFiles or files:
|
||||||
|
statusString.set(compressingNotice)
|
||||||
|
tmp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
|
if files:
|
||||||
|
zfPath = Path(files[0]).parent.absolute()
|
||||||
|
else:
|
||||||
|
zfPath = Path(dirname(allFiles[0])).parent.absolute()
|
||||||
|
zfOffset = len(str(zfPath))
|
||||||
|
zfName = pathJoin(zfPath,tmp+".zip")
|
||||||
|
zf = ZipFile(zfName,"w")
|
||||||
|
for i in allFiles:
|
||||||
|
zf.write(i,i[zfOffset:])
|
||||||
|
for i in files:
|
||||||
|
zf.write(i,pathSplit(i)[1])
|
||||||
|
|
||||||
|
zf.close()
|
||||||
|
inputFile = zfName
|
||||||
|
outputFile = zfName+".pcv"
|
||||||
|
outputPath = dirname(outputFile)
|
||||||
|
|
||||||
# Set and get some variables
|
# Set and get some variables
|
||||||
working = True
|
working = True
|
||||||
headerBroken = False
|
headerBroken = False
|
||||||
|
@ -340,7 +450,13 @@ def start():
|
||||||
wipe = erase.get()==1
|
wipe = erase.get()==1
|
||||||
|
|
||||||
# Open files
|
# Open files
|
||||||
fin = open(inputFile,"rb+")
|
try:
|
||||||
|
fin = open(inputFile,"rb")
|
||||||
|
except:
|
||||||
|
resetEncryptionUI()
|
||||||
|
statusString.set("Folder is empty.")
|
||||||
|
return
|
||||||
|
|
||||||
if reedsolo and mode=="decrypt":
|
if reedsolo and mode=="decrypt":
|
||||||
# Move pointer one forward
|
# Move pointer one forward
|
||||||
fin.read(1)
|
fin.read(1)
|
||||||
|
@ -425,15 +541,7 @@ def start():
|
||||||
fout.close()
|
fout.close()
|
||||||
remove(outputFile)
|
remove(outputFile)
|
||||||
# Reset UI
|
# Reset UI
|
||||||
selectFileInput["state"] = "normal"
|
resetDecryptionUI()
|
||||||
passwordInput["state"] = "normal"
|
|
||||||
adArea["state"] = "normal"
|
|
||||||
startBtn["state"] = "normal"
|
|
||||||
keepBtn["state"] = "normal"
|
|
||||||
working = False
|
|
||||||
progress.stop()
|
|
||||||
progress.config(mode="determinate")
|
|
||||||
progress["value"] = 100
|
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
kept = "badlyCorrupted"
|
kept = "badlyCorrupted"
|
||||||
|
@ -472,14 +580,7 @@ def start():
|
||||||
fout.close()
|
fout.close()
|
||||||
remove(outputFile)
|
remove(outputFile)
|
||||||
# Reset UI
|
# Reset UI
|
||||||
selectFileInput["state"] = "normal"
|
resetDecryptionUI()
|
||||||
passwordInput["state"] = "normal"
|
|
||||||
adArea["state"] = "normal"
|
|
||||||
startBtn["state"] = "normal"
|
|
||||||
keepBtn["state"] = "normal"
|
|
||||||
working = False
|
|
||||||
progress["value"] = 100
|
|
||||||
del key
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create XChaCha20-Poly1305 object
|
# Create XChaCha20-Poly1305 object
|
||||||
|
@ -492,9 +593,6 @@ def start():
|
||||||
total = getsize(inputFile)
|
total = getsize(inputFile)
|
||||||
|
|
||||||
# If secure wipe enabled, create a wiper object
|
# If secure wipe enabled, create a wiper object
|
||||||
if wipe:
|
|
||||||
wiper = open(inputFile,"r+b")
|
|
||||||
wiper.seek(0)
|
|
||||||
|
|
||||||
# Keep track of time because it flies...
|
# Keep track of time because it flies...
|
||||||
startTime = datetime.now()
|
startTime = datetime.now()
|
||||||
|
@ -507,11 +605,7 @@ def start():
|
||||||
piece = fin.read(1104905)
|
piece = fin.read(1104905)
|
||||||
else:
|
else:
|
||||||
piece = fin.read(chunkSize)
|
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 EOF
|
||||||
if not piece:
|
if not piece:
|
||||||
if mode=="encrypt":
|
if mode=="encrypt":
|
||||||
|
@ -540,12 +634,7 @@ def start():
|
||||||
if keep.get()!=1:
|
if keep.get()!=1:
|
||||||
remove(outputFile)
|
remove(outputFile)
|
||||||
# Reset UI
|
# Reset UI
|
||||||
selectFileInput["state"] = "normal"
|
resetDecryptionUI()
|
||||||
passwordInput["state"] = "normal"
|
|
||||||
adArea["state"] = "normal"
|
|
||||||
startBtn["state"] = "normal"
|
|
||||||
keepBtn["state"] = "normal"
|
|
||||||
working = False
|
|
||||||
del fin,fout,cipher,key
|
del fin,fout,cipher,key
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -566,12 +655,7 @@ def start():
|
||||||
if keep.get()!=1:
|
if keep.get()!=1:
|
||||||
remove(outputFile)
|
remove(outputFile)
|
||||||
# Reset UI
|
# Reset UI
|
||||||
selectFileInput["state"] = "normal"
|
resetDecryptionUI()
|
||||||
passwordInput["state"] = "normal"
|
|
||||||
adArea["state"] = "normal"
|
|
||||||
startBtn["state"] = "normal"
|
|
||||||
keepBtn["state"] = "normal"
|
|
||||||
working = False
|
|
||||||
del fin,fout,cipher,key
|
del fin,fout,cipher,key
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -605,13 +689,7 @@ def start():
|
||||||
fout.close()
|
fout.close()
|
||||||
remove(outputFile)
|
remove(outputFile)
|
||||||
# Reset UI
|
# Reset UI
|
||||||
selectFileInput["state"] = "normal"
|
resetDecryptionUI()
|
||||||
passwordInput["state"] = "normal"
|
|
||||||
adArea["state"] = "normal"
|
|
||||||
startBtn["state"] = "normal"
|
|
||||||
keepBtn["state"] = "normal"
|
|
||||||
working = False
|
|
||||||
progress["value"] = 100
|
|
||||||
del fin,fout,cipher,key
|
del fin,fout,cipher,key
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
@ -639,60 +717,76 @@ def start():
|
||||||
data = cipher.decrypt(piece)
|
data = cipher.decrypt(piece)
|
||||||
|
|
||||||
# Calculate speed, ETA, etc.
|
# Calculate speed, ETA, etc.
|
||||||
first = False
|
|
||||||
elapsed = (datetime.now()-previousTime).total_seconds() or 0.0001
|
elapsed = (datetime.now()-previousTime).total_seconds() or 0.0001
|
||||||
sinceStart = (datetime.now()-startTime).total_seconds() or 0.0001
|
sinceStart = (datetime.now()-startTime).total_seconds() or 0.0001
|
||||||
previousTime = datetime.now()
|
previousTime = datetime.now()
|
||||||
# Prevent divison by zero
|
|
||||||
if not elapsed:
|
|
||||||
elapsed = 0.1**6
|
|
||||||
percent = done*100/total
|
percent = done*100/total
|
||||||
progress["value"] = percent
|
progress["value"] = percent
|
||||||
rPercent = round(percent)
|
|
||||||
speed = (done/sinceStart)/10**6
|
speed = (done/sinceStart)/10**6 or 0.0001
|
||||||
# Prevent divison by zero
|
|
||||||
if not speed:
|
|
||||||
first = True
|
|
||||||
speed = 0.1**6
|
|
||||||
rSpeed = str(round(speed,2))
|
|
||||||
# Right-pad with zeros to large prevent layout shifts
|
|
||||||
while len(rSpeed.split(".")[1])!=2:
|
|
||||||
rSpeed += "0"
|
|
||||||
eta = round((total-done)/(speed*10**6))
|
eta = round((total-done)/(speed*10**6))
|
||||||
|
|
||||||
# Seconds to minutes if seconds more than 59
|
# Seconds to minutes if seconds more than 59
|
||||||
if eta>=60:
|
if eta>=60:
|
||||||
|
# Set blank ETA if just starting
|
||||||
|
if sinceStart<0.5:
|
||||||
|
eta = "..."
|
||||||
|
else:
|
||||||
eta = f"{eta//60}m {eta%60}"
|
eta = f"{eta//60}m {eta%60}"
|
||||||
if isinstance(eta,int) or isinstance(eta,float):
|
if isinstance(eta,int) or isinstance(eta,float):
|
||||||
if eta<0:
|
if eta<0:
|
||||||
eta = 0
|
eta = 0
|
||||||
# If it's the first round and no data/predictions yet...
|
|
||||||
if first:
|
|
||||||
statusString.set("...% at ... MB/s (ETA: ...s)")
|
|
||||||
else:
|
|
||||||
# Update status
|
# Update status
|
||||||
info = f"{rPercent}% at {rSpeed} MB/s (ETA: {eta}s)"
|
info = f"{percent:.0f}% at {speed:.2f} MB/s (ETA: {eta}s)"
|
||||||
|
|
||||||
if reedsolo and mode=="decrypt" and reedsoloFixedCount:
|
if reedsolo and mode=="decrypt" and reedsoloFixedCount:
|
||||||
eng = "s" if reedsoloFixedCount!=1 else ""
|
tmp = "s" if reedsoloFixedCount!=1 else ""
|
||||||
info += f", fixed {reedsoloFixedCount} corrupted byte{eng}"
|
info += f", fixed {reedsoloFixedCount} corrupted byte{tmp}"
|
||||||
if reedsolo and mode=="decrypt" and reedsoloErrorCount:
|
if reedsolo and mode=="decrypt" and reedsoloErrorCount:
|
||||||
info += f", {reedsoloErrorCount} MB unrecoverable"
|
info += f", {reedsoloErrorCount} MB unrecoverable"
|
||||||
|
|
||||||
statusString.set(info)
|
statusString.set(info)
|
||||||
|
|
||||||
# Increase done and write to output
|
# Increase done and write to output
|
||||||
done += 1104905 if (reedsolo and mode=="decrypt") else chunkSize
|
done += 1104905 if (reedsolo and mode=="decrypt") else chunkSize
|
||||||
fout.write(data)
|
fout.write(data)
|
||||||
|
|
||||||
|
# Flush outputs, close files
|
||||||
|
if not kept:
|
||||||
|
fout.flush()
|
||||||
|
fsync(fout.fileno())
|
||||||
|
fout.close()
|
||||||
|
fin.close()
|
||||||
|
|
||||||
|
# Securely wipe files as necessary
|
||||||
|
if wipe:
|
||||||
|
if draggedFolderPaths:
|
||||||
|
for i in draggedFolderPaths:
|
||||||
|
secureWipe(i)
|
||||||
|
if files:
|
||||||
|
for i in range(len(files)):
|
||||||
|
statusString.set(erasingNotice+f" ({i}/{len(files)}")
|
||||||
|
progress["value"] = i/len(files)
|
||||||
|
secureWipe(files[i])
|
||||||
|
secureWipe(inputFile)
|
||||||
|
# Secure wipe not enabled
|
||||||
|
else:
|
||||||
|
if allFiles:
|
||||||
|
# Remove temporary zip file if created
|
||||||
|
remove(inputFile)
|
||||||
|
|
||||||
# Show appropriate notice if file corrupted or modified
|
# Show appropriate notice if file corrupted or modified
|
||||||
if not kept:
|
if not kept:
|
||||||
if mode=="encrypt":
|
statusString.set(f"Completed. (Click here to show output)")
|
||||||
output = inputFile.split("/")[-1]+".pcv"
|
|
||||||
else:
|
|
||||||
output = inputFile.split("/")[-1].replace(".pcv","")
|
|
||||||
statusString.set(f"Completed. (Output: {output})")
|
|
||||||
# Show Reed-Solomon stats if it fixed corrupted bytes
|
# Show Reed-Solomon stats if it fixed corrupted bytes
|
||||||
if mode=="decrypt" and reedsolo and reedsoloFixedCount:
|
if mode=="decrypt" and reedsolo and reedsoloFixedCount:
|
||||||
statusString.set(f"Completed with {reedsoloFixedCount} bytes fixed."+
|
statusString.set(
|
||||||
f" (Output: {output})")
|
f"Completed with {reedsoloFixedCount}"+
|
||||||
|
f" bytes fixed. (Output: {output})"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if kept=="modified":
|
if kept=="modified":
|
||||||
statusString.set(kModifiedNotice)
|
statusString.set(kModifiedNotice)
|
||||||
|
@ -701,45 +795,32 @@ def start():
|
||||||
else:
|
else:
|
||||||
statusString.set(kVeryCorruptedNotice)
|
statusString.set(kVeryCorruptedNotice)
|
||||||
|
|
||||||
|
status.config(cursor="hand2")
|
||||||
|
|
||||||
|
# A little hack since strings are immutable
|
||||||
|
output = "".join([i for i in outputFile])
|
||||||
|
|
||||||
|
# Bind the output file
|
||||||
|
if platform.system()=="Windows":
|
||||||
|
status.bind("<Button-1>",
|
||||||
|
lambda e:showOutput(output.replace("/","\\"))
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
status.bind("<Button-1>",
|
||||||
|
lambda e:showOutput(output)
|
||||||
|
)
|
||||||
# Reset variables and UI states
|
# Reset variables and UI states
|
||||||
selectFileInput["state"] = "normal"
|
resetUI()
|
||||||
adArea["state"] = "normal"
|
status["state"] = "normal"
|
||||||
adArea.delete("1.0",tkinter.END)
|
|
||||||
adArea["state"] = "disabled"
|
|
||||||
startBtn["state"] = "disabled"
|
|
||||||
passwordInput["state"] = "normal"
|
|
||||||
passwordInput.delete(0,"end")
|
|
||||||
passwordInput["state"] = "disabled"
|
|
||||||
cpasswordInput["state"] = "normal"
|
|
||||||
cpasswordInput.delete(0,"end")
|
|
||||||
cpasswordInput["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"
|
|
||||||
rs.set(0)
|
|
||||||
rsBtn["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 = ""
|
inputFile = ""
|
||||||
outputFile = ""
|
outputFile = ""
|
||||||
password = ""
|
password = ""
|
||||||
ad = ""
|
ad = ""
|
||||||
kept = False
|
kept = False
|
||||||
working = False
|
working = False
|
||||||
|
allFiles = False
|
||||||
|
dragFolderPath = False
|
||||||
|
|
||||||
# Wipe keys for safety
|
# Wipe keys for safety
|
||||||
del fin,fout,cipher,key
|
del fin,fout,cipher,key
|
||||||
|
|
||||||
|
@ -751,34 +832,124 @@ def wrapper():
|
||||||
start()
|
start()
|
||||||
except:
|
except:
|
||||||
# Reset UI accordingly
|
# Reset UI accordingly
|
||||||
progress.stop()
|
|
||||||
progress.config(mode="determinate")
|
|
||||||
progress["value"] = 100
|
|
||||||
selectFileInput["state"] = "normal"
|
|
||||||
passwordInput["state"] = "normal"
|
|
||||||
startBtn["state"] = "normal"
|
|
||||||
|
|
||||||
if gMode=="decrypt":
|
if gMode=="decrypt":
|
||||||
keepBtn["state"] = "normal"
|
resetDecryptionUI()
|
||||||
else:
|
else:
|
||||||
adArea["state"] = "normal"
|
resetEncryptionUI()
|
||||||
cpasswordInput["state"] = "normal"
|
|
||||||
rsBtn["state"] = "normal"
|
|
||||||
eraseBtn["state"] = "normal"
|
|
||||||
|
|
||||||
statusString.set(unknownErrorNotice)
|
statusString.set(unknownErrorNotice)
|
||||||
dummy.focus()
|
dummy.focus()
|
||||||
working = False
|
|
||||||
finally:
|
finally:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Encryption/decrypt is done is a separate thread
|
# Encryption/decrypt is done is a separate thread so the UI
|
||||||
# so the UI isn't blocked. This is a wrapper
|
# isn't blocked. This is a wrapper to spawn a thread and start it.
|
||||||
# to spawn a thread and start it.
|
|
||||||
def startWorker():
|
def startWorker():
|
||||||
thread = Thread(target=wrapper,daemon=True)
|
thread = Thread(target=wrapper,daemon=True)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
# Securely wipe file
|
||||||
|
def secureWipe(fin):
|
||||||
|
statusString.set(erasingNotice)
|
||||||
|
# Check platform, erase accordingly
|
||||||
|
if platform.system()=="Windows":
|
||||||
|
if isdir(fin):
|
||||||
|
paths = []
|
||||||
|
for i in Path(fin).rglob("*"):
|
||||||
|
if dirname(i) not in paths:
|
||||||
|
paths.append(dirname(i))
|
||||||
|
for i in range(len(paths)):
|
||||||
|
statusString.set(erasingNotice+f" ({i}/{len(paths)})")
|
||||||
|
progress["value"] = 100*i/len(paths)
|
||||||
|
system(f'cd "{paths[i]}" && "{rootDir}/sdelete64.exe" * -p 4 -s -nobanner')
|
||||||
|
system(f'cd "{rootDir}"')
|
||||||
|
rmtree(fin)
|
||||||
|
else:
|
||||||
|
statusString.set(erasingNotice)
|
||||||
|
progress["value"] = 100
|
||||||
|
system(f'sdelete64.exe "{fin}" -p 4 -nobanner')
|
||||||
|
elif platform.system()=="Darwin":
|
||||||
|
system(f'rm -rfP "{fin}"')
|
||||||
|
else:
|
||||||
|
system(f'shred -uz "{fin}" -n 4')
|
||||||
|
|
||||||
|
# Disable all inputs while encrypting/decrypting
|
||||||
|
def disableAllInputs():
|
||||||
|
passwordInput["state"] = "disabled"
|
||||||
|
cpasswordInput["state"] = "disabled"
|
||||||
|
adArea["state"] = "disabled"
|
||||||
|
startBtn["state"] = "disabled"
|
||||||
|
eraseBtn["state"] = "disabled"
|
||||||
|
keepBtn["state"] = "disabled"
|
||||||
|
rsBtn["state"] = "disabled"
|
||||||
|
|
||||||
|
# Reset UI to encryption state
|
||||||
|
def resetEncryptionUI():
|
||||||
|
global working
|
||||||
|
passwordInput["state"] = "normal"
|
||||||
|
cpasswordInput["state"] = "normal"
|
||||||
|
adArea["state"] = "normal"
|
||||||
|
startBtn["state"] = "normal"
|
||||||
|
eraseBtn["state"] = "normal"
|
||||||
|
rsBtn["state"] = "normal"
|
||||||
|
working = False
|
||||||
|
progress.stop()
|
||||||
|
progress.config(mode="determinate")
|
||||||
|
progress["value"] = 100
|
||||||
|
|
||||||
|
# Reset UI to decryption state
|
||||||
|
def resetDecryptionUI():
|
||||||
|
global working
|
||||||
|
passwordInput["state"] = "normal"
|
||||||
|
adArea["state"] = "normal"
|
||||||
|
startBtn["state"] = "normal"
|
||||||
|
keepBtn["state"] = "normal"
|
||||||
|
working = False
|
||||||
|
progress.stop()
|
||||||
|
progress.config(mode="determinate")
|
||||||
|
progress["value"] = 100
|
||||||
|
|
||||||
|
# Reset UI to original state (no file selected)
|
||||||
|
def resetUI():
|
||||||
|
adArea["state"] = "normal"
|
||||||
|
adArea.delete("1.0",tkinter.END)
|
||||||
|
adArea["state"] = "disabled"
|
||||||
|
adLabel["state"] = "disabled"
|
||||||
|
startBtn["state"] = "disabled"
|
||||||
|
passwordInput["state"] = "normal"
|
||||||
|
passwordInput.delete(0,"end")
|
||||||
|
passwordInput["state"] = "disabled"
|
||||||
|
passwordLabel["state"] = "disabled"
|
||||||
|
cpasswordInput["state"] = "normal"
|
||||||
|
cpasswordInput.delete(0,"end")
|
||||||
|
cpasswordInput["state"] = "disabled"
|
||||||
|
cpasswordString.set("Confirm password:")
|
||||||
|
cpasswordLabel["state"] = "disabled"
|
||||||
|
status["state"] = "disabled"
|
||||||
|
progress["value"] = 0
|
||||||
|
inputString.set("Drag and drop file(s) and folder(s) into this window.")
|
||||||
|
keepBtn["state"] = "normal"
|
||||||
|
keep.set(0)
|
||||||
|
keepBtn["state"] = "disabled"
|
||||||
|
eraseBtn["state"] = "normal"
|
||||||
|
erase.set(0)
|
||||||
|
eraseBtn["state"] = "disabled"
|
||||||
|
rs.set(0)
|
||||||
|
rsBtn["state"] = "disabled"
|
||||||
|
progress.stop()
|
||||||
|
progress.config(mode="determinate")
|
||||||
|
progress["value"] = 0
|
||||||
|
|
||||||
|
def showOutput(file):
|
||||||
|
if platform.system()=="Windows":
|
||||||
|
system(f'explorer /select,"{file}"')
|
||||||
|
elif platform.system()=="Darwin":
|
||||||
|
system(f'cd "{dirname(file)}"; open -R {pathSplit(file)[1]}')
|
||||||
|
system(f'cd "{rootDir}"')
|
||||||
|
else:
|
||||||
|
system(f'xdg-open "{dirname(file)}"')
|
||||||
|
|
||||||
# ad stands for "associated data"/metadata
|
# ad stands for "associated data"/metadata
|
||||||
adLabelString = tkinter.StringVar(tk)
|
adLabelString = tkinter.StringVar(tk)
|
||||||
adLabelString.set(adString)
|
adLabelString.set(adString)
|
||||||
|
@ -786,8 +957,9 @@ adLabel = tkinter.ttk.Label(
|
||||||
tk,
|
tk,
|
||||||
textvariable=adLabelString
|
textvariable=adLabelString
|
||||||
)
|
)
|
||||||
adLabel.place(x=17,y=158)
|
adLabel.place(x=17,y=148)
|
||||||
adLabel.config(background="#f5f6f7")
|
adLabel.config(background="#ffffff")
|
||||||
|
adLabel["state"] = "disabled"
|
||||||
|
|
||||||
# Frame so metadata text box can fill width
|
# Frame so metadata text box can fill width
|
||||||
adFrame = tkinter.Frame(
|
adFrame = tkinter.Frame(
|
||||||
|
@ -795,7 +967,7 @@ adFrame = tkinter.Frame(
|
||||||
width=440,
|
width=440,
|
||||||
height=100
|
height=100
|
||||||
)
|
)
|
||||||
adFrame.place(x=20,y=178)
|
adFrame.place(x=20,y=168)
|
||||||
adFrame.columnconfigure(0,weight=10)
|
adFrame.columnconfigure(0,weight=10)
|
||||||
adFrame.grid_propagate(False)
|
adFrame.grid_propagate(False)
|
||||||
|
|
||||||
|
@ -818,7 +990,7 @@ keepBtn = tkinter.ttk.Checkbutton(
|
||||||
offvalue=0,
|
offvalue=0,
|
||||||
command=lambda:dummy.focus()
|
command=lambda:dummy.focus()
|
||||||
)
|
)
|
||||||
keepBtn.place(x=18,y=290)
|
keepBtn.place(x=18,y=280)
|
||||||
keepBtn["state"] = "disabled"
|
keepBtn["state"] = "disabled"
|
||||||
|
|
||||||
# Check box for securely erasing original file
|
# Check box for securely erasing original file
|
||||||
|
@ -831,7 +1003,7 @@ eraseBtn = tkinter.ttk.Checkbutton(
|
||||||
offvalue=0,
|
offvalue=0,
|
||||||
command=lambda:dummy.focus()
|
command=lambda:dummy.focus()
|
||||||
)
|
)
|
||||||
eraseBtn.place(x=18,y=310)
|
eraseBtn.place(x=18,y=300)
|
||||||
eraseBtn["state"] = "disabled"
|
eraseBtn["state"] = "disabled"
|
||||||
|
|
||||||
# Check box for Reed Solomon
|
# Check box for Reed Solomon
|
||||||
|
@ -844,16 +1016,16 @@ rsBtn = tkinter.ttk.Checkbutton(
|
||||||
offvalue=0,
|
offvalue=0,
|
||||||
command=lambda:dummy.focus()
|
command=lambda:dummy.focus()
|
||||||
)
|
)
|
||||||
rsBtn.place(x=18,y=330)
|
rsBtn.place(x=18,y=320)
|
||||||
rsBtn["state"] = "disabled"
|
rsBtn["state"] = "disabled"
|
||||||
|
|
||||||
# Frame so start button can fill width
|
# Frame so start button can fill width
|
||||||
startFrame = tkinter.Frame(
|
startFrame = tkinter.Frame(
|
||||||
tk,
|
tk,
|
||||||
width=442,
|
width=442,
|
||||||
height=25
|
height=24
|
||||||
)
|
)
|
||||||
startFrame.place(x=19,y=360)
|
startFrame.place(x=19,y=350)
|
||||||
startFrame.columnconfigure(0,weight=10)
|
startFrame.columnconfigure(0,weight=10)
|
||||||
startFrame.grid_propagate(False)
|
startFrame.grid_propagate(False)
|
||||||
# Start button
|
# Start button
|
||||||
|
@ -872,7 +1044,7 @@ progress = tkinter.ttk.Progressbar(
|
||||||
length=440,
|
length=440,
|
||||||
mode="determinate"
|
mode="determinate"
|
||||||
)
|
)
|
||||||
progress.place(x=20,y=388)
|
progress.place(x=20,y=378)
|
||||||
|
|
||||||
# Status label
|
# Status label
|
||||||
statusString = tkinter.StringVar(tk)
|
statusString = tkinter.StringVar(tk)
|
||||||
|
@ -881,8 +1053,9 @@ status = tkinter.ttk.Label(
|
||||||
tk,
|
tk,
|
||||||
textvariable=statusString
|
textvariable=statusString
|
||||||
)
|
)
|
||||||
status.place(x=17,y=416)
|
status.place(x=17,y=406)
|
||||||
status.config(background="#f5f6f7")
|
status.config(background="#ffffff")
|
||||||
|
status["state"] = "disabled"
|
||||||
|
|
||||||
# Credits :)
|
# Credits :)
|
||||||
hint = "Created by Evan Su. Click for details and source."
|
hint = "Created by Evan Su. Click for details and source."
|
||||||
|
@ -894,21 +1067,21 @@ credits = tkinter.ttk.Label(
|
||||||
cursor="hand2"
|
cursor="hand2"
|
||||||
)
|
)
|
||||||
credits["state"] = "disabled"
|
credits["state"] = "disabled"
|
||||||
credits.config(background="#f5f6f7")
|
credits.config(background="#ffffff")
|
||||||
credits.place(x=17,y=446)
|
credits.place(x=17,y=436)
|
||||||
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
|
# Version
|
||||||
versionString = tkinter.StringVar(tk)
|
versionString = tkinter.StringVar(tk)
|
||||||
versionString.set("v1.10")
|
versionString.set("v1.11")
|
||||||
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="#ffffff")
|
||||||
version.place(x=430,y=446)
|
version.place(x=(420 if platform.system()=="Darwin" else 430),y=436)
|
||||||
|
|
||||||
# Dummy button to remove focus from other buttons
|
# Dummy button to remove focus from other buttons
|
||||||
# and prevent ugly border highlighting
|
# and prevent ugly border highlighting
|
||||||
|
@ -923,16 +1096,28 @@ def createRsc():
|
||||||
headerRsc = RSCodec(128)
|
headerRsc = RSCodec(128)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
def prepare():
|
||||||
|
if platform.system()=="Windows":
|
||||||
|
system("sdelete64.exe /accepteula")
|
||||||
|
|
||||||
# Close window only if not encrypting or decrypting
|
# Close window only if not encrypting or decrypting
|
||||||
def onClose():
|
def onClose():
|
||||||
|
global outputFile
|
||||||
if not working:
|
if not working:
|
||||||
tk.destroy()
|
tk.destroy()
|
||||||
|
else:
|
||||||
|
force = messagebox.askyesno("Confirmation",cancelNotice)
|
||||||
|
if force:
|
||||||
|
tk.destroy()
|
||||||
|
|
||||||
# Main application loop
|
# Main application loop
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
# Create Reed-Solomon header codec
|
# Create Reed-Solomon header codec
|
||||||
tmp = Thread(target=createRsc,daemon=True)
|
tmp = Thread(target=createRsc,daemon=True)
|
||||||
tmp.start()
|
tmp.start()
|
||||||
|
# Prepare application
|
||||||
|
tmp = Thread(target=prepare,daemon=True)
|
||||||
|
tmp.start()
|
||||||
# Start tkinter
|
# Start tkinter
|
||||||
tk.protocol("WM_DELETE_WINDOW",onClose)
|
tk.protocol("WM_DELETE_WINDOW",onClose)
|
||||||
tk.mainloop()
|
tk.mainloop()
|
||||||
|
|
Loading…
Reference in New Issue