Update v2.py

This commit is contained in:
Evan Su 2021-03-28 17:34:11 -04:00 committed by GitHub
parent 80a0572d87
commit 5ba6a01b87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 92 additions and 113 deletions

View File

@ -2,7 +2,7 @@
""" """
Picocrypt v2.0 Picocrypt v1.12
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
@ -16,13 +16,13 @@ from threading import Thread
from datetime import datetime from datetime import datetime
from argon2.low_level import hash_secret_raw from argon2.low_level import hash_secret_raw
from argon2.low_level import Type as argonType from argon2.low_level import Type as argonType
from monocypher import lock,unlock,wipe,Blake2b from Crypto.Cipher import ChaCha20_Poly1305
from Crypto.Hash import SHA3_512
from hmac import compare_digest from hmac import compare_digest
from reedsolo import RSCodec,ReedSolomonError from reedsolo import RSCodec,ReedSolomonError
from os import urandom,fsync,remove,system from os import urandom,fsync,remove,system
from os.path import getsize,expanduser,isdir,exists,dirname,abspath,realpath from os.path import getsize,expanduser,isdir,exists,dirname,abspath,realpath
from os.path import join as pathJoin,split as pathSplit from os.path import join as pathJoin,split as pathSplit
#from os.path import split as pathSplit
from pathlib import Path from pathlib import Path
from zipfile import ZipFile from zipfile import ZipFile
from tkinterdnd2 import TkinterDnD,DND_FILES from tkinterdnd2 import TkinterDnD,DND_FILES
@ -41,7 +41,6 @@ working = False
mode = False mode = False
inputFile = False inputFile = False
outputFile = False outputFile = False
kept = False
rs128 = False rs128 = False
rs13 = False rs13 = False
allFiles = False allFiles = False
@ -66,7 +65,7 @@ strings = [
"Output file already exists. Would you like to overwrite it?", "Output file already exists. Would you like to overwrite it?",
"Exiting now will lead to broken output. Are you sure?", "Exiting now will lead to broken output. Are you sure?",
"Prevent corruption using Reed-Solomon", "Prevent corruption using Reed-Solomon",
"", "Error. Folder(s) and/or file(s) are empty.",
"Unknown error occured. Please try again.", "Unknown error occured. Please try again.",
"Drag and drop file(s) and folder(s) into this window.", "Drag and drop file(s) and folder(s) into this window.",
"File metadata (read-only):", "File metadata (read-only):",
@ -97,21 +96,21 @@ def Get_HWND_DPI(window_handle):
windll.shcore.GetDpiForMonitor( windll.shcore.GetDpiForMonitor(
monitorhandle,DPI_type,pointer(X),pointer(Y) monitorhandle,DPI_type,pointer(X),pointer(Y)
) )
return X.value,Y.value,(X.value+Y.value)/(2*DPI100pc) return X.value*2,Y.value*2,(X.value+Y.value)/(2*DPI100pc)
except Exception: except Exception:
return 96,96,1 return 96,96,1
def TkGeometryScale(s,cvtfunc): def TkGeometryScale(s,cvtfunc):
patt = r"(?P<W>\d+)x(?P<H>\d+)\+(?P<X>\d+)\+(?P<Y>\d+)" patt = r"(?P<W>\d+)x(?P<H>\d+)\+(?P<X>\d+)\+(?P<Y>\d+)"
R = re.compile(patt).search(s) R = re.compile(patt).search(s)
G = str(cvtfunc(R.group("W")))+"x" G = str(cvtfunc(R.group("W")))+"x"
G += str(cvtfunc(R.group("H")))+"+" G += str(cvtfunc(R.group("H")))+"+"
G += str(cvtfunc(R.group("X")))+"+" G += str(cvtfunc(R.group("X")))+"+"
G += str(cvtfunc(R.group("Y"))) G += str(cvtfunc(R.group("Y")))
return G return G
def MakeTkDPIAware(TKGUI): def MakeTkDPIAware(TKGUI):
TKGUI.DPI_X,TKGUI.DPI_Y,TKGUI.DPI_scaling = Get_HWND_DPI(TKGUI.winfo_id()) TKGUI.DPI_X,TKGUI.DPI_Y,TKGUI.DPI_scaling = Get_HWND_DPI(TKGUI.winfo_id())
TKGUI.TkScale = lambda v:int(float(v)* TKGUI.DPI_scaling) TKGUI.TkScale = lambda v:int(float(v)*TKGUI.DPI_scaling)
TKGUI.TkGeometryScale = lambda s:TkGeometryScale(s,TKGUI.TkScale) TKGUI.TkGeometryScale = lambda s:TkGeometryScale(s,TKGUI.TkScale)
if platform.system()=="Windows": if platform.system()=="Windows":
MakeTkDPIAware(tk) MakeTkDPIAware(tk)
@ -259,33 +258,25 @@ metadataLabel["state"] = "disabled"
# Frame so metadata box can fill width # Frame so metadata box can fill width
metadataFrame = tkinter.Frame( metadataFrame = tkinter.Frame(
tk, tk,
width=444, width=440,
height=99 height=99
) )
metadataFrame.place(x=20,y=220) metadataFrame.place(x=20,y=220)
metadataFrame.columnconfigure(0,weight=10) metadataFrame.columnconfigure(0,weight=10)
metadataFrame.rowconfigure(0,weight=10) metadataFrame.rowconfigure(0,weight=10)
metadataFrame.grid_propagate(False) metadataFrame.grid_propagate(False)
metadataFrame.config(bg="#dfe4ee") metadataFrame.config(bg="#e5eaf0")
# Metadata scrollbar
metadataScrollbar = tkinter.ttk.Scrollbar(
metadataFrame,
cursor="hand2"
)
metadataScrollbar.grid(row=0,column=1,sticky="nesw")
# Metadata text box # Metadata text box
metadataInput = tkinter.Text( metadataInput = tkinter.scrolledtext.ScrolledText(
metadataFrame, metadataFrame,
exportselection=0, exportselection=0,
height=5, height=5
yscrollcommand=metadataScrollbar.set
) )
metadataInput.config(font=("Consolas",12)) metadataInput.config(font=("Consolas",12))
metadataInput.grid(row=0,column=0,sticky="nesw",padx=1,pady=1) metadataInput.grid(row=0,column=0,sticky="nesw",padx=1,pady=1)
metadataInput.config(borderwidth=0) metadataInput.config(borderwidth=0)
metadataScrollbar.config(command=metadataInput.yview) metadataInput.config(bg="#fbfcfc")
metadataInput["state"] = "disabled" metadataInput["state"] = "disabled"
# Check box for keeping corrupted or modified output # Check box for keeping corrupted or modified output
@ -295,7 +286,8 @@ keepBtn = tkinter.ttk.Checkbutton(
text=strings[10], text=strings[10],
variable=keep, variable=keep,
onvalue=1, onvalue=1,
offvalue=0 offvalue=0,
command=lambda:dummy.focus()
) )
keepBtn.place(x=18,y=329) keepBtn.place(x=18,y=329)
keepBtn["state"] = "disabled" keepBtn["state"] = "disabled"
@ -308,7 +300,8 @@ eraseBtn = tkinter.ttk.Checkbutton(
text=strings[11], text=strings[11],
variable=erase, variable=erase,
onvalue=1, onvalue=1,
offvalue=0 offvalue=0,
command=lambda:dummy.focus()
) )
eraseBtn.place(x=18,y=349) eraseBtn.place(x=18,y=349)
eraseBtn["state"] = "disabled" eraseBtn["state"] = "disabled"
@ -320,7 +313,8 @@ rsBtn = tkinter.ttk.Checkbutton(
text=strings[15], text=strings[15],
variable=rs, variable=rs,
onvalue=1, onvalue=1,
offvalue=0 offvalue=0,
command=lambda:dummy.focus()
) )
rsBtn.place(x=18,y=369) rsBtn.place(x=18,y=369)
rsBtn["state"] = "disabled" rsBtn["state"] = "disabled"
@ -468,11 +462,19 @@ def filesDragged(draggedFiles):
suffix = " (will decrypt)" suffix = " (will decrypt)"
# Read file metadata # Read file metadata
fin = open(inputFile,"rb")
fin.read(129)
metadataLength = fin.read(138)
metadataLength = bytes(rs128.decode(metadataLength)[0])
metadataLength = metadataLength.replace(b"+",b"")
metadata = fin.read(int(metadataLength.decode("utf-8")))
metadata = bytes(rs128.decode(metadata)[0]).decode("utf-8")
metadataString.set("File metadata (read only):") metadataString.set("File metadata (read only):")
metadataInput["state"] = "normal" metadataInput["state"] = "normal"
metadataInput.delete("1.0",tkinter.END) metadataInput.delete("1.0",tkinter.END)
metadataInput.insert("1.0","hi") metadataInput.insert("1.0",metadata)
metadataInput["state"] = "disabled" metadataInput["state"] = "disabled"
fin.close()
# Insert filename into output box # Insert filename into output box
outputFrame.config(width=440) outputFrame.config(width=440)
@ -522,9 +524,9 @@ def filesDragged(draggedFiles):
statusString.set(strings[20]) statusString.set(strings[20])
progress["value"] = 100 progress["value"] = 100
# Nothing happened, reset UI # Nothing happened
except: except:
resetUI() pass
# Bind drag and drop to window # Bind drag and drop to window
def onDrop(e): def onDrop(e):
@ -540,10 +542,16 @@ def work():
global inputFile,outputFile,working,mode,rs13,rs128,onlyFiles,onlyFolders,allFiles global inputFile,outputFile,working,mode,rs13,rs128,onlyFiles,onlyFolders,allFiles
disableAllInputs() disableAllInputs()
dummy.focus() dummy.focus()
# Set and get some variables # Set and get some variables
kept = False
shouldKeep = keep.get()==1
shouldErase = erase.get()==1
reedsolo = rs.get()==1
working = True working = True
headerBroken = False headerBroken = False
reedsoloFixed = 0
reedsoloErrors = 0
password = passwordInput.get().encode("utf-8") password = passwordInput.get().encode("utf-8")
metadata = metadataInput.get("1.0",tkinter.END).encode("utf-8") metadata = metadataInput.get("1.0",tkinter.END).encode("utf-8")
@ -589,18 +597,16 @@ def work():
fin = open(inputFile,"rb") fin = open(inputFile,"rb")
except: except:
setEncryptionUI() setEncryptionUI()
statusString.set("Error. Folder is empty.") statusString.set(strings[16])
return return
if mode=="encrypt": if mode=="encrypt":
salt = urandom(16) salt = urandom(16)
nonce = urandom(24)
fout = open(outputFile,"wb+") fout = open(outputFile,"wb+")
#print(rs128.encode(b"-")) if reedsolo:
if rs.get()==1:
reedsolo = True
fout.write(rs128.encode(b"+")) fout.write(rs128.encode(b"+"))
else: else:
reedsolo = False
fout.write(rs128.encode(b"-")) fout.write(rs128.encode(b"-"))
metadata = rs128.encode(metadata) metadata = rs128.encode(metadata)
@ -608,36 +614,33 @@ def work():
tmp = f"{tmp:+<10}" tmp = f"{tmp:+<10}"
tmp = rs128.encode(tmp.encode("utf-8")) tmp = rs128.encode(tmp.encode("utf-8"))
#print(tmp)
#print(metadata)
#print(rs128.encode(salt))
fout.write(tmp) fout.write(tmp)
fout.write(metadata) fout.write(metadata)
fout.write(rs128.encode(salt)) # Argon2 salt fout.write(rs128.encode(salt)) # Argon2 salt
fout.write(b"0"*192) # BLAKE2 of key fout.write(rs128.encode(nonce)) # ChaCha20 nonce
fout.write(b"0"*192) # BLAKE2 MAC fout.write(b"0"*192) # Hash of key
fout.write(b"0"*192) # BLAKE2 CRC fout.write(b"0"*144) # Poly1305 MAC
fout.write(b"0"*192) # CRC
else: else:
tmp = fin.read(129) tmp = fin.read(129)
#print(tmp)
if bytes(rs128.decode(tmp)[0])==b"+": if bytes(rs128.decode(tmp)[0])==b"+":
reedsolo = True reedsolo = True
else: else:
reedsolo = False reedsolo = False
metadataLength = fin.read(138) metadataLength = fin.read(138)
#print(metadataLength)
metadataLength = bytes(rs128.decode(metadataLength)[0]) metadataLength = bytes(rs128.decode(metadataLength)[0])
metadataLength = metadataLength.replace(b"+",b"") metadataLength = metadataLength.replace(b"+",b"")
#print(metadataLength)
fin.read(int(metadataLength.decode("utf-8"))) fin.read(int(metadataLength.decode("utf-8")))
salt = fin.read(144) salt = fin.read(144)
#print(salt) nonce = fin.read(152)
keycs = fin.read(192) keycs = fin.read(192)
#print(keycs) maccs = fin.read(144)
maccs = fin.read(192)
crccs = fin.read(192) crccs = fin.read(192)
salt = bytes(rs128.decode(salt)[0]) salt = bytes(rs128.decode(salt)[0])
nonce = bytes(rs128.decode(nonce)[0])
keycs = bytes(rs128.decode(keycs)[0]) keycs = bytes(rs128.decode(keycs)[0])
maccs = bytes(rs128.decode(maccs)[0]) maccs = bytes(rs128.decode(maccs)[0])
crccs = bytes(rs128.decode(crccs)[0]) crccs = bytes(rs128.decode(crccs)[0])
@ -648,7 +651,7 @@ def work():
password, password,
salt, salt,
time_cost=8, time_cost=8,
memory_cost=2**20, memory_cost=2**10,
parallelism=8, parallelism=8,
hash_len=32, hash_len=32,
type=argonType.D type=argonType.D
@ -658,9 +661,7 @@ def work():
progress.config(mode="determinate") progress.config(mode="determinate")
progress["value"] = 0 progress["value"] = 0
check = Blake2b(hash_size=64) check = SHA3_512.new(data=key).digest()
check.update(key)
check = check.finalize()
if mode=="decrypt": if mode=="decrypt":
if not compare_digest(check,keycs): if not compare_digest(check,keycs):
@ -671,67 +672,36 @@ def work():
return return
fout = open(outputFile,"wb+") fout = open(outputFile,"wb+")
crc = Blake2b(hash_size=64) crc = SHA3_512.new()
subKey = Blake2b(hash_size=32) cipher = ChaCha20_Poly1305.new(key=key,nonce=nonce)
subKey.update(key)
subKey = subKey.finalize()
mac = Blake2b(key=subKey,hash_size=64)
done = 0 done = 0
total = getsize(inputFile) total = getsize(inputFile)
startTime = datetime.now() startTime = datetime.now()
previousTime = datetime.now() previousTime = datetime.now()
counter = 0
while True: while True:
if mode=="encrypt": if mode=="encrypt":
piece = fin.read(2**25) piece = fin.read(2**20)
if not piece:
break
tag = f"{counter:+>32}".encode("utf-8")
piece += tag
else: else:
piece = fin.read(2**25+200) piece = fin.read(2**20)
if not piece:
break if not piece:
break
if mode=="encrypt": if mode=="encrypt":
nonce = urandom(24) data = cipher.encrypt(piece)
digest,data = lock(key,nonce,piece)
crc.update(data)
mac.update(data)
fout.write(nonce)
fout.write(digest)
fout.write(data) fout.write(data)
#print(nonce)
#print("=====")
#print(digest)
#print("=======")
#print(data)
else: else:
nonce = piece[:24] data = cipher.decrypt(piece)
digest = piece[24:40] fout.write(data)
piece = piece[40:]
#print(nonce)
#print("=====")
#print(digest)
#print("=======")
#print(piece)
crc.update(piece)
mac.update(piece)
data = unlock(key,nonce,digest,piece)
if int(data[-32:].replace(b"+",b"").decode("utf-8"))!=counter:
print("Counter failed")
fout.write(data[:-32])
counter += 1
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()
percent = done*100/total percent = done*100/total
progress["value"] = percent progress["value"] = percent
done += 2**20
speed = (done/sinceStart)/10**6 or 0.0001 speed = (done/sinceStart)/10**6 or 0.0001
eta = round((total-done)/(speed*10**6)) eta = round((total-done)/(speed*10**6))
@ -745,19 +715,18 @@ def work():
statusString.set(info) statusString.set(info)
# Increase done and write to output
done += 2**25
if mode=="encrypt": if mode=="encrypt":
fout.flush() fout.flush()
fout.close() fout.close()
fout = open(outputFile,"r+b") fout = open(outputFile,"r+b")
fout.seek(129+138+len(metadata)+144) fout.seek(129+138+len(metadata)+144+152)
fout.write(rs128.encode(check)) fout.write(rs128.encode(check))
fout.write(rs128.encode(mac.finalize())) fout.write(rs128.encode(cipher.digest()))
fout.write(rs128.encode(crc.finalize())) fout.write(rs128.encode(crc.digest()))
else: else:
if not compare_digest(crccs,crc.finalize()): if not compare_digest(crccs,crc.digest()):
statusString.set(strings[3]) statusString.set(strings[3])
progress["value"] = 100 progress["value"] = 100
fin.close() fin.close()
@ -771,7 +740,9 @@ def work():
if not kept: if not kept:
kept = "corrupted" kept = "corrupted"
if not compare_digest(maccs,mac.finalize()): try:
cipher.verify(maccs)
except:
#if not reedsoloErrorCount and not headerBroken: #if not reedsoloErrorCount and not headerBroken:
if True: if True:
# File is modified # File is modified
@ -799,7 +770,7 @@ def work():
print("DONEDONEDONEDONEDONEDONEDONEDONEDONEDONE") print("DONEDONEDONEDONEDONEDONEDONEDONEDONEDONE")
# Securely wipe files as necessary # Securely wipe files as necessary
if wipe: if shouldErase:
if onlyFolders: if onlyFolders:
for i in onlyFolders: for i in onlyFolders:
secureWipe(i) secureWipe(i)
@ -854,12 +825,9 @@ def work():
resetUI() resetUI()
inputFile = "" inputFile = ""
outputFile = "" outputFile = ""
password = ""
metadata = ""
kept = False
working = False working = False
allFiles = False allFiles = []
onlyFolders = False onlyFolders = []
onlyFiles = [] onlyFiles = []
# Wipe keys for safety # Wipe keys for safety
@ -909,6 +877,8 @@ def resetUI():
cPasswordInput["state"] = "normal" cPasswordInput["state"] = "normal"
cPasswordInput.delete(0,"end") cPasswordInput.delete(0,"end")
cPasswordInput["state"] = "disabled" cPasswordInput["state"] = "disabled"
metadataFrame.config(bg="#e5eaf0")
metadataInput.config(bg="#fbfcfc")
metadataString.set(strings[0]) metadataString.set(strings[0])
metadataLabel["state"] = "disabled" metadataLabel["state"] = "disabled"
metadataInput["state"] = "normal" metadataInput["state"] = "normal"
@ -935,7 +905,10 @@ def setEncryptionUI():
passwordLabel["state"] = "normal" passwordLabel["state"] = "normal"
passwordInput["state"] = "normal" passwordInput["state"] = "normal"
cPasswordLabel["state"] = "normal" cPasswordLabel["state"] = "normal"
cPasswordString.set("Confirm password:")
cPasswordInput["state"] = "normal" cPasswordInput["state"] = "normal"
metadataFrame.config(bg="#dfe4ee")
metadataInput.config(background="#ffffff")
metadataLabel["state"] = "normal" metadataLabel["state"] = "normal"
metadataInput["state"] = "normal" metadataInput["state"] = "normal"
eraseBtn["state"] = "normal" eraseBtn["state"] = "normal"
@ -952,6 +925,9 @@ def setDecryptionUI():
outputFrame.config(width=440) outputFrame.config(width=440)
passwordLabel["state"] = "normal" passwordLabel["state"] = "normal"
passwordInput["state"] = "normal" passwordInput["state"] = "normal"
cPasswordString.set("Confirm password (N/A):")
metadataFrame.config(bg="#959aa0")
metadataInput.config(background="#fbfcfc")
metadataString.set(strings[19]) metadataString.set(strings[19])
metadataInput["state"] = "disabled" metadataInput["state"] = "disabled"
keepBtn["state"] = "normal" keepBtn["state"] = "normal"
@ -966,6 +942,9 @@ def disableAllInputs():
outputInput["state"] = "disabled" outputInput["state"] = "disabled"
passwordInput["state"] = "disabled" passwordInput["state"] = "disabled"
cPasswordInput["state"] = "disabled" cPasswordInput["state"] = "disabled"
cPasswordString.set("Confirm password:")
metadataFrame.config(bg="#e5eaf0")
metadataInput.config(background="#fbfcfc")
metadataInput["state"] = "disabled" metadataInput["state"] = "disabled"
startBtn["state"] = "disabled" startBtn["state"] = "disabled"
eraseBtn["state"] = "disabled" eraseBtn["state"] = "disabled"