Update v1.12.py

This commit is contained in:
Evan Su 2021-03-30 09:30:52 -04:00 committed by GitHub
parent 23e5912d1c
commit 0984742e64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 140 additions and 65 deletions

View File

@ -19,7 +19,7 @@ from argon2.low_level import Type as argonType
from Crypto.Cipher import ChaCha20_Poly1305 from Crypto.Cipher import ChaCha20_Poly1305
from Crypto.Hash import SHA3_512,BLAKE2b from Crypto.Hash import SHA3_512,BLAKE2b
from hmac import compare_digest from hmac import compare_digest
from reedsolo import RSCodec,ReedSolomonError from creedsolo 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
@ -50,6 +50,10 @@ onlyFiles = False
startTime = False startTime = False
previousTime = False previousTime = False
done = False done = False
stopUpdating = False
reedsolo = False
reedsoloFixed = False
reedsoloErrors = False
# Strings # Strings
strings = [ strings = [
@ -132,7 +136,7 @@ except:
dummy = tkinter.ttk.Button(tk) dummy = tkinter.ttk.Button(tk)
dummy.place(x=480,y=0) dummy.place(x=480,y=0)
# Label that shows the input files # Label that shows the input file(s)
inputString = tkinter.StringVar(tk) inputString = tkinter.StringVar(tk)
inputString.set(strings[18]) inputString.set(strings[18])
inputLabel = tkinter.ttk.Label( inputLabel = tkinter.ttk.Label(
@ -141,7 +145,7 @@ inputLabel = tkinter.ttk.Label(
) )
inputLabel.place(x=20,y=18) inputLabel.place(x=20,y=18)
# Clear input files # Clear input file(s)
clearInput = tkinter.ttk.Button( clearInput = tkinter.ttk.Button(
tk, tk,
text="Clear", text="Clear",
@ -386,7 +390,7 @@ status = tkinter.ttk.Label(
) )
status.place(x=20,y=453) status.place(x=20,y=453)
# Credits :D # 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)
@ -416,7 +420,7 @@ def filesDragged(draggedFiles):
resetUI() resetUI()
status.config(cursor="") status.config(cursor="")
status.bind("<Button-1>",lambda e:None) status.bind("<Button-1>",lambda e:None)
# Use try to catch file errors
try: try:
# Create lists to track files dragged # Create lists to track files dragged
onlyFiles = [] onlyFiles = []
@ -559,8 +563,9 @@ tk.drop_target_register(DND_FILES)
tk.dnd_bind("<<Drop>>",onDrop) tk.dnd_bind("<<Drop>>",onDrop)
def work(): def work():
global inputFile,outputFile,working,mode,rs13,rs128,done global inputFile,outputFile,working,mode,rs13,rs128,reedsolo
global startTime,previousTime,onlyFiles,onlyFolders,allFiles global done,stopUpdating,startTime,previousTime,onlyFiles
global onlyFolders,allFiles,reedsoloFixed,reedsoloErrors
disableAllInputs() disableAllInputs()
dummy.focus() dummy.focus()
@ -570,6 +575,7 @@ def work():
shouldErase = erase.get()==1 shouldErase = erase.get()==1
reedsolo = rs.get()==1 reedsolo = rs.get()==1
working = True working = True
stopUpdating = False
headerBroken = False headerBroken = False
reedsoloFixed = 0 reedsoloFixed = 0
reedsoloErrors = 0 reedsoloErrors = 0
@ -629,7 +635,7 @@ def work():
fout.write(rs128.encode(b"+")) fout.write(rs128.encode(b"+"))
else: else:
fout.write(rs128.encode(b"-")) fout.write(rs128.encode(b"-"))
metadata = rs128.encode(metadata) metadata = rs128.encode(metadata)
tmp = len(metadata) tmp = len(metadata)
tmp = f"{tmp:+<10}" tmp = f"{tmp:+<10}"
@ -641,7 +647,7 @@ def work():
fout.write(rs128.encode(nonce)) # ChaCha20 nonce fout.write(rs128.encode(nonce)) # ChaCha20 nonce
fout.write(b"0"*192) # Hash of key fout.write(b"0"*192) # Hash of key
fout.write(b"0"*144) # Poly1305 MAC fout.write(b"0"*144) # Poly1305 MAC
fout.write(b"0"*192) # CRC fout.write(b"0"*192) # BLAKE2b CRC
else: else:
tmp = fin.read(129) tmp = fin.read(129)
if bytes(rs128.decode(tmp)[0])==b"+": if bytes(rs128.decode(tmp)[0])==b"+":
@ -660,11 +666,47 @@ def work():
maccs = fin.read(144) maccs = fin.read(144)
crccs = fin.read(192) crccs = fin.read(192)
salt = bytes(rs128.decode(salt)[0]) try:
nonce = bytes(rs128.decode(nonce)[0]) salt,_,fixed = rs128.decode(salt)
keycs = bytes(rs128.decode(keycs)[0]) salt = bytes(salt)
maccs = bytes(rs128.decode(maccs)[0]) reedsoloFixed += len(fixed)
crccs = bytes(rs128.decode(crccs)[0]) except:
headerBroken = True
try:
nonce,_,fixed = rs128.decode(nonce)
nonce = bytes(nonce)
reedsoloFixed += len(fixed)
except:
headerBroken = True
try:
keycs,_,fixed = rs128.decode(keycs)
keycs = bytes(keycs)
reedsoloFixed += len(fixed)
except:
headerBroken = True
try:
maccs,_,fixed = rs128.decode(maccs)
maccs = bytes(maccs)
reedsoloFixed += len(fixed)
except:
headerBroken = True
try:
crccs,_,fixed = rs128.decode(crccs)
crccs = bytes(crccs)
reedsoloFixed += len(fixed)
except:
headerBroken = True
if headerBroken:
if not shouldKeep:
statusString.set(strings[8])
fin.close()
fout.close()
remove(outputFile)
setDecryptionUI()
return
else:
kept = "badlyCorrupted"
statusString.set(strings[9]) statusString.set(strings[9])
@ -695,29 +737,63 @@ def work():
crc = BLAKE2b.new(digest_bits=512) crc = BLAKE2b.new(digest_bits=512)
cipher = ChaCha20_Poly1305.new(key=key,nonce=nonce) cipher = ChaCha20_Poly1305.new(key=key,nonce=nonce)
done = 0 done = 0
total = getsize(inputFile) total = getsize(inputFile)
startTime = datetime.now() startTime = datetime.now()
previousTime = datetime.now() previousTime = datetime.now()
Thread(target=updateStats,daemon=True,args=(total,)).start() Thread(target=updateStats,daemon=True,args=(total,)).start()
while True: while True:
if mode=="encrypt": if mode=="decrypt" and reedsolo:
piece = fin.read(2**20) piece = fin.read(1104905)
else: else:
piece = fin.read(2**20) piece = fin.read(2**20)
if not piece: if not piece:
break break
if mode=="encrypt": if mode=="encrypt":
data = cipher.encrypt(piece) data = cipher.encrypt(piece)
#crc.update(data) if reedsolo:
data = bytes(rs13.encode(data))
crc.update(data)
else: else:
#crc.update(piece) crc.update(piece)
data = cipher.decrypt(piece) if reedsolo:
try:
data,_,fixed = rs13.decode(piece)
except ReedSolomonError:
# File is really corrupted
if not reedsoloErrors and not shouldKeep:
statusString.set(strings[8])
if not shouldKeep:
fin.close()
fout.close()
remove(outputFile)
setDecryptionUI()
return
kept = "badlyCorrupted"
# Attempt to recover badly corrupted data
data = b""
piece = piece[:-13]
counter = 0
while True:
# Basically just strip the Reed-Solomon bytes
# and return the original non-encoded data
if counter<1104905:
data += piece[counter:counter+242]
counter += 255 # 255 bytes, 242 original
else:
break
fixed = bytearray()
reedsoloErrors += 1
reedsoloFixed += len(fixed)
data = cipher.decrypt(data)
else:
data = cipher.decrypt(piece)
fout.write(data) fout.write(data)
done += 2**20 done += 2**20
@ -748,8 +824,7 @@ def work():
try: try:
cipher.verify(maccs) cipher.verify(maccs)
except: except:
#if not reedsoloErrorCount and not headerBroken: if not reedsoloErrors and not headerBroken:
if True:
# File is modified # File is modified
statusString.set(modifiedNotice) statusString.set(modifiedNotice)
progress["value"] = 100 progress["value"] = 100
@ -766,15 +841,12 @@ def work():
kept = "modified" kept = "modified"
# Flush outputs, close files # Flush outputs, close files
#if not kept: if not kept:
working = False
if True:
fout.flush() fout.flush()
fsync(fout.fileno()) fsync(fout.fileno())
fout.close() fout.close()
fin.close() fin.close()
stopUpdating = True
print("DONEDONEDONEDONEDONEDONEDONEDONEDONEDONE")
# Securely wipe files as necessary # Securely wipe files as necessary
if shouldErase: if shouldErase:
@ -783,36 +855,34 @@ def work():
secureWipe(i) secureWipe(i)
if onlyFiles: if onlyFiles:
for i in range(len(onlyFiles)): for i in range(len(onlyFiles)):
statusString.set( statusString.set(strings[12]+f" ({i}/{len(onlyFiles)}")
strings[12]+f" ({i}/{len(onlyFiles)}"
)
progress["value"] = i/len(onlyFiles) progress["value"] = i/len(onlyFiles)
secureWipe(onlyFiles[i]) secureWipe(onlyFiles[i])
secureWipe(inputFile) secureWipe(inputFile)
# Secure wipe not enabled # Secure wipe not enabled
else: else:
if allFiles or onlyFiles: if allFiles or onlyFiles:
# Remove temporary zip file if created # Remove temporary zip file if created
remove(inputFile) remove(inputFile)
print(kept,reedsoloFixed)
# Show appropriate notice if file corrupted or modified # Show appropriate notice if file corrupted or modified
#if not kept: if not kept:
if True:
statusString.set(f"Completed. (Click here to show output)") statusString.set(f"Completed. (Click here to show 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 reedsoloFixed:
statusString.set( statusString.set(
f"Completed with {reedsoloFixedCount}"+ f"Completed with {reedsoloFixed}"+
f" bytes fixed. (Output: {output})" f" bytes fixed. (Click here to show output)"
) )
else: else:
if kept=="modified": if kept=="modified":
statusString.set(kModifiedNotice) statusString.set(strings[7])
elif kept=="corrupted": elif kept=="corrupted":
statusString.set(kCorruptedNotice) statusString.set(strings[6])
else: else:
statusString.set(kVeryCorruptedNotice) statusString.set(strings[8])
status.config(cursor="hand2") status.config(cursor="hand2")
@ -828,6 +898,7 @@ def work():
status.bind("<Button-1>", status.bind("<Button-1>",
lambda e:showOutput(output) lambda e:showOutput(output)
) )
# Reset variables and UI states # Reset variables and UI states
resetUI() resetUI()
inputFile = "" inputFile = ""
@ -835,35 +906,39 @@ def work():
allFiles = [] allFiles = []
onlyFolders = [] onlyFolders = []
onlyFiles = [] onlyFiles = []
working = False
# Wipe keys for safety
#del fin,fout,cipher,key
def updateStats(total): def updateStats(total):
global startTime,previousTime,done,working global startTime,previousTime,done,stopUpdating,reedsolo,reedsoloFixed,reedsoloErrors
while True: while True:
if not working: validStatus = (
statusString.get().startswith("Working") or statusString.get().startswith("Deriving")
)
if not stopUpdating and validStatus:
elapsed = (datetime.now()-previousTime).total_seconds() or 0.0001
sinceStart = (datetime.now()-startTime).total_seconds() or 0.0001
previousTime = datetime.now()
percent = done*100/total
progress["value"] = percent
speed = (done/sinceStart)/10**6 or 0.0001
eta = round((total-done)/(speed*10**6))
info = f"Working... {min(percent,100):.0f}% at {speed:.2f} MB/s (ETA: {max(eta,0)}s)"
if reedsolo and mode=="decrypt" and reedsoloFixed:
tmp = "s" if reedsoloFixed!=1 else ""
info += f", fixed {reedsoloFixed} corrupted byte{tmp}"
if reedsolo and mode=="decrypt" and reedsoloErrors:
info += f", {reedsoloErrors} MB unrecoverable"
statusString.set(info)
sleep(0.05)
else:
sys.exit(0) sys.exit(0)
break break
elapsed = (datetime.now()-previousTime).total_seconds() or 0.0001
sinceStart = (datetime.now()-startTime).total_seconds() or 0.0001
previousTime = datetime.now()
percent = done*100/total
progress["value"] = percent
speed = (done/sinceStart)/10**6 or 0.0001
eta = round((total-done)/(speed*10**6))
info = f"{min(percent,100):.0f}% at {speed:.2f} MB/s (ETA: {max(eta,0)}s)"
'''if reedsolo and mode=="decrypt" and reedsoloFixedCount:
tmp = "s" if reedsoloFixedCount!=1 else ""
info += f", fixed {reedsoloFixedCount} corrupted byte{tmp}"
if reedsolo and mode=="decrypt" and reedsoloErrorCount:
info += f", {reedsoloErrorCount} MB unrecoverable"'''
statusString.set(info)
sleep(0.05)
def secureWipe(fin): def secureWipe(fin):
statusString.set(strings[12]) statusString.set(strings[12])
# Check platform, erase accordingly # Check platform, erase accordingly