Fixed minor UI bugs
This commit is contained in:
parent
12fb31921d
commit
cda615d8e4
120
src/Picocrypt.py
120
src/Picocrypt.py
|
@ -67,9 +67,11 @@ working = False
|
||||||
adString = "File metadata (used to store some text along with the file):"
|
adString = "File metadata (used to store some text along with the file):"
|
||||||
passwordNotice = "Error. The provided password is incorrect."
|
passwordNotice = "Error. The provided password is incorrect."
|
||||||
corruptedNotice = "Error. The input file is corrupted."
|
corruptedNotice = "Error. The input file is corrupted."
|
||||||
|
veryCorruptedNotice = "Error. The input file and header keys are badly corrupted."
|
||||||
modifiedNotice = "Error. The input file has been intentionally modified."
|
modifiedNotice = "Error. The input file has been intentionally modified."
|
||||||
kCorruptedNotice = "The input file is corrupted, but the output has been kept."
|
kCorruptedNotice = "The input file is corrupted, but the output has been kept."
|
||||||
kModifiedNotice = "The input file has been intentionally modified, but the output has been kept."
|
kModifiedNotice = "The input file has been intentionally modified, but the output has been kept."
|
||||||
|
kVeryCorruptedNotice = "The input file is badly corrupted, but the output has been kept."
|
||||||
derivingNotice = "Deriving key (takes a few seconds)..."
|
derivingNotice = "Deriving key (takes a few seconds)..."
|
||||||
keepNotice = "Keep decrypted output even if it's corrupted or modified"
|
keepNotice = "Keep decrypted output even if it's corrupted or modified"
|
||||||
eraseNotice = "Securely erase and delete original file"
|
eraseNotice = "Securely erase and delete original file"
|
||||||
|
@ -251,13 +253,14 @@ def start():
|
||||||
# Create Reed-Solomon object
|
# Create Reed-Solomon object
|
||||||
if reedsolo:
|
if reedsolo:
|
||||||
statusString.set(rscNotice)
|
statusString.set(rscNotice)
|
||||||
# 8 bytes per 128 bytes, 6.25% larger output file
|
# 13 bytes per 128 bytes, ~10% larger output file
|
||||||
rsc = RSCodec(8)
|
rsc = RSCodec(13)
|
||||||
reedsoloFixedCount = 0
|
|
||||||
reedsoloErrorCount = 0
|
|
||||||
|
|
||||||
# Set and get some variables
|
# Set and get some variables
|
||||||
working = True
|
working = True
|
||||||
|
headerBroken = False
|
||||||
|
reedsoloFixedCount = 0
|
||||||
|
reedsoloErrorCount = 0
|
||||||
dummy.focus()
|
dummy.focus()
|
||||||
password = passwordInput.get().encode("utf-8")
|
password = passwordInput.get().encode("utf-8")
|
||||||
ad = adArea.get("1.0",tkinter.END).encode("utf-8")
|
ad = adArea.get("1.0",tkinter.END).encode("utf-8")
|
||||||
|
@ -291,10 +294,10 @@ def start():
|
||||||
fout.write(ad) # Metadata (associated data)
|
fout.write(ad) # Metadata (associated data)
|
||||||
|
|
||||||
# Write zeros as placeholder, come back to write over it later
|
# Write zeros as placeholder, come back to write over it later
|
||||||
# Note that 8 additional bytes are added if Reed-Solomon is enabled
|
# Note that 13 additional bytes are added if Reed-Solomon is enabled
|
||||||
fout.write(b"0"*(64+(8 if reedsolo else 0))) # SHA3-512 of encryption key
|
fout.write(b"0"*(64+(13 if reedsolo else 0))) # SHA3-512 of encryption key
|
||||||
fout.write(b"0"*(64+(8 if reedsolo else 0))) # CRC of file
|
fout.write(b"0"*(64+(13 if reedsolo else 0))) # CRC of file
|
||||||
fout.write(b"0"*(16+(8 if reedsolo else 0))) # Poly1305 tag
|
fout.write(b"0"*(16+(13 if reedsolo else 0))) # Poly1305 tag
|
||||||
# If Reed-Solomon is enabled, encode the salt and nonce, otherwise write them raw
|
# If Reed-Solomon is enabled, encode the salt and nonce, otherwise write them raw
|
||||||
fout.write(bytes(rsc.encode(salt)) if reedsolo else salt) # Argon2 salt
|
fout.write(bytes(rsc.encode(salt)) if reedsolo else salt) # Argon2 salt
|
||||||
fout.write(bytes(rsc.encode(nonce)) if reedsolo else nonce) # ChaCha20 nonce
|
fout.write(bytes(rsc.encode(nonce)) if reedsolo else nonce) # ChaCha20 nonce
|
||||||
|
@ -310,19 +313,59 @@ def start():
|
||||||
break
|
break
|
||||||
fin.read(int(adlen.decode("utf-8")))
|
fin.read(int(adlen.decode("utf-8")))
|
||||||
# Read the salt, nonce, etc.
|
# Read the salt, nonce, etc.
|
||||||
# Read 8 extra bytes if Reed-Solomon is enabled
|
# Read 13 extra bytes if Reed-Solomon is enabled
|
||||||
cs = fin.read(72 if reedsolo else 64)
|
cs = fin.read(77 if reedsolo else 64)
|
||||||
crccs = fin.read(72 if reedsolo else 64)
|
crccs = fin.read(77 if reedsolo else 64)
|
||||||
digest = fin.read(24 if reedsolo else 16)
|
digest = fin.read(29 if reedsolo else 16)
|
||||||
salt = fin.read(24 if reedsolo else 16)
|
salt = fin.read(29 if reedsolo else 16)
|
||||||
nonce = fin.read(32 if reedsolo else 24)
|
nonce = fin.read(37 if reedsolo else 24)
|
||||||
# If Reed-Solomon is enabled, decode each value
|
# If Reed-Solomon is enabled, decode each value
|
||||||
if reedsolo:
|
if reedsolo:
|
||||||
|
try:
|
||||||
cs = bytes(rsc.decode(cs)[0])
|
cs = bytes(rsc.decode(cs)[0])
|
||||||
|
except:
|
||||||
|
headerBroken = True
|
||||||
|
cs = cs[:64]
|
||||||
|
try:
|
||||||
crccs = bytes(rsc.decode(crccs)[0])
|
crccs = bytes(rsc.decode(crccs)[0])
|
||||||
|
except:
|
||||||
|
headerBroken = True
|
||||||
|
crccs = crccs[:64]
|
||||||
|
try:
|
||||||
digest = bytes(rsc.decode(digest)[0])
|
digest = bytes(rsc.decode(digest)[0])
|
||||||
|
except:
|
||||||
|
headerBroken = True
|
||||||
|
digest = digest[:16]
|
||||||
|
try:
|
||||||
salt = bytes(rsc.decode(salt)[0])
|
salt = bytes(rsc.decode(salt)[0])
|
||||||
|
except:
|
||||||
|
headerBroken = True
|
||||||
|
salt = salt[:16]
|
||||||
|
try:
|
||||||
nonce = bytes(rsc.decode(nonce)[0])
|
nonce = bytes(rsc.decode(nonce)[0])
|
||||||
|
except:
|
||||||
|
headerBroken = True
|
||||||
|
nonce = nonce[:24]
|
||||||
|
|
||||||
|
if headerBroken:
|
||||||
|
if keep.get()!=1:
|
||||||
|
statusString.set(veryCorruptedNotice)
|
||||||
|
fin.close()
|
||||||
|
fout.close()
|
||||||
|
remove(outputFile)
|
||||||
|
# Reset UI
|
||||||
|
selectFileInput["state"] = "normal"
|
||||||
|
passwordInput["state"] = "normal"
|
||||||
|
adArea["state"] = "normal"
|
||||||
|
startBtn["state"] = "normal"
|
||||||
|
keepBtn["state"] = "normal"
|
||||||
|
working = False
|
||||||
|
progress.stop()
|
||||||
|
progress.config(mode="determinate")
|
||||||
|
progress["value"] = 100
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
kept = "badlyCorrupted"
|
||||||
|
|
||||||
# Show notice about key derivation
|
# Show notice about key derivation
|
||||||
statusString.set(derivingNotice)
|
statusString.set(derivingNotice)
|
||||||
|
@ -332,7 +375,7 @@ def start():
|
||||||
password,
|
password,
|
||||||
salt,
|
salt,
|
||||||
time_cost=8, # 8 iterations
|
time_cost=8, # 8 iterations
|
||||||
memory_cost=2**20, # 2^20 Kibibytes (1GiB)
|
memory_cost=2**10, # 2^20 Kibibytes (1GiB)
|
||||||
parallelism=8, # 8 parallel threads
|
parallelism=8, # 8 parallel threads
|
||||||
hash_len=32,
|
hash_len=32,
|
||||||
type=Type.ID
|
type=Type.ID
|
||||||
|
@ -350,6 +393,7 @@ def start():
|
||||||
if mode=="decrypt":
|
if mode=="decrypt":
|
||||||
# If key is incorrect...
|
# If key is incorrect...
|
||||||
if not compare_digest(check,cs):
|
if not compare_digest(check,cs):
|
||||||
|
if not headerBroken:
|
||||||
statusString.set(passwordNotice)
|
statusString.set(passwordNotice)
|
||||||
fin.close()
|
fin.close()
|
||||||
fout.close()
|
fout.close()
|
||||||
|
@ -361,6 +405,7 @@ def start():
|
||||||
startBtn["state"] = "normal"
|
startBtn["state"] = "normal"
|
||||||
keepBtn["state"] = "normal"
|
keepBtn["state"] = "normal"
|
||||||
working = False
|
working = False
|
||||||
|
progress["value"] = 100
|
||||||
del key
|
del key
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -383,7 +428,7 @@ def start():
|
||||||
while True:
|
while True:
|
||||||
if mode=="decrypt" and reedsolo:
|
if mode=="decrypt" and reedsolo:
|
||||||
# Read a chunk plus Reed-Solomon recovery bytes
|
# Read a chunk plus Reed-Solomon recovery bytes
|
||||||
piece = fin.read(1082544)
|
piece = fin.read(1104905)
|
||||||
else:
|
else:
|
||||||
piece = fin.read(chunkSize)
|
piece = fin.read(chunkSize)
|
||||||
if wipe:
|
if wipe:
|
||||||
|
@ -413,7 +458,7 @@ def start():
|
||||||
fout.write(crc.digest())
|
fout.write(crc.digest())
|
||||||
fout.write(digest)
|
fout.write(digest)
|
||||||
else:
|
else:
|
||||||
# If decrypting, verify MAC tag
|
# If decrypting, verify CRC
|
||||||
crcdg = crc.digest()
|
crcdg = crc.digest()
|
||||||
if not compare_digest(crccs,crcdg):
|
if not compare_digest(crccs,crcdg):
|
||||||
# File is corrupted
|
# File is corrupted
|
||||||
|
@ -430,17 +475,18 @@ def start():
|
||||||
adArea["state"] = "normal"
|
adArea["state"] = "normal"
|
||||||
startBtn["state"] = "normal"
|
startBtn["state"] = "normal"
|
||||||
keepBtn["state"] = "normal"
|
keepBtn["state"] = "normal"
|
||||||
rsBtn["state"] = "normal"
|
|
||||||
working = False
|
working = False
|
||||||
del fin,fout,cipher,key
|
del fin,fout,cipher,key
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
if not kept:
|
||||||
kept = "corrupted"
|
kept = "corrupted"
|
||||||
|
# Next, verify MAC tag (Poly1305)
|
||||||
try:
|
try:
|
||||||
# Throws ValueError if incorrect Poly1305
|
# Throws ValueError if incorrect Poly1305
|
||||||
cipher.verify(digest)
|
cipher.verify(digest)
|
||||||
except:
|
except:
|
||||||
if not reedsoloErrorCount:
|
if not reedsoloErrorCount and not headerBroken:
|
||||||
# File is modified
|
# File is modified
|
||||||
statusString.set(modifiedNotice)
|
statusString.set(modifiedNotice)
|
||||||
progress["value"] = 100
|
progress["value"] = 100
|
||||||
|
@ -455,11 +501,11 @@ def start():
|
||||||
adArea["state"] = "normal"
|
adArea["state"] = "normal"
|
||||||
startBtn["state"] = "normal"
|
startBtn["state"] = "normal"
|
||||||
keepBtn["state"] = "normal"
|
keepBtn["state"] = "normal"
|
||||||
rsBtn["state"] = "normal"
|
|
||||||
working = False
|
working = False
|
||||||
del fin,fout,cipher,key
|
del fin,fout,cipher,key
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
if not kept:
|
||||||
kept = "modified"
|
kept = "modified"
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -480,7 +526,8 @@ def start():
|
||||||
except ReedSolomonError:
|
except ReedSolomonError:
|
||||||
# File is really corrupted
|
# File is really corrupted
|
||||||
if not reedsoloErrorCount:
|
if not reedsoloErrorCount:
|
||||||
statusString.set(corruptedNotice)
|
if keep.get()!=1:
|
||||||
|
statusString.set(veryCorruptedNotice)
|
||||||
progress["value"] = 100
|
progress["value"] = 100
|
||||||
# If keep file not checked...
|
# If keep file not checked...
|
||||||
if keep.get()!=1:
|
if keep.get()!=1:
|
||||||
|
@ -493,13 +540,24 @@ def start():
|
||||||
adArea["state"] = "normal"
|
adArea["state"] = "normal"
|
||||||
startBtn["state"] = "normal"
|
startBtn["state"] = "normal"
|
||||||
keepBtn["state"] = "normal"
|
keepBtn["state"] = "normal"
|
||||||
rsBtn["state"] = "normal"
|
|
||||||
working = False
|
working = False
|
||||||
|
progress["value"] = 100
|
||||||
del fin,fout,cipher,key
|
del fin,fout,cipher,key
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
kept = "corrupted"
|
kept = "badlyCorrupted"
|
||||||
data = piece[:2**20]
|
# 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()
|
fixed = bytearray()
|
||||||
reedsoloErrorCount += 1
|
reedsoloErrorCount += 1
|
||||||
data = bytes(data)
|
data = bytes(data)
|
||||||
|
@ -536,13 +594,14 @@ def start():
|
||||||
# Update status
|
# Update status
|
||||||
info = f"{rPercent}% at {rSpeed} MB/s (ETA: {eta}s)"
|
info = f"{rPercent}% at {rSpeed} MB/s (ETA: {eta}s)"
|
||||||
if reedsolo and mode=="decrypt" and reedsoloFixedCount:
|
if reedsolo and mode=="decrypt" and reedsoloFixedCount:
|
||||||
info += f", fixed {reedsoloFixedCount} corrupted bytes"
|
eng = "s" if reedsoloFixedCount!=1 else ""
|
||||||
|
info += f", fixed {reedsoloFixedCount} corrupted byte{eng}"
|
||||||
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 += chunkSize
|
done += chunkSize if not reedsolo else 1104905
|
||||||
fout.write(data)
|
fout.write(data)
|
||||||
|
|
||||||
# Show appropriate notice if file corrupted or modified
|
# Show appropriate notice if file corrupted or modified
|
||||||
|
@ -559,8 +618,10 @@ def start():
|
||||||
else:
|
else:
|
||||||
if kept=="modified":
|
if kept=="modified":
|
||||||
statusString.set(kModifiedNotice)
|
statusString.set(kModifiedNotice)
|
||||||
else:
|
elif kept=="corrupted":
|
||||||
statusString.set(kCorruptedNotice)
|
statusString.set(kCorruptedNotice)
|
||||||
|
else:
|
||||||
|
statusString.set(kVeryCorruptedNotice)
|
||||||
|
|
||||||
# Reset variables and UI states
|
# Reset variables and UI states
|
||||||
selectFileInput["state"] = "normal"
|
selectFileInput["state"] = "normal"
|
||||||
|
@ -607,7 +668,8 @@ def wrapper():
|
||||||
# Try start() and handle errors
|
# Try start() and handle errors
|
||||||
try:
|
try:
|
||||||
start()
|
start()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
progress["value"] = 100
|
progress["value"] = 100
|
||||||
selectFileInput["state"] = "normal"
|
selectFileInput["state"] = "normal"
|
||||||
passwordInput["state"] = "normal"
|
passwordInput["state"] = "normal"
|
||||||
|
@ -750,7 +812,7 @@ credits.bind("<Button-1>",lambda e:webbrowser.open(source))
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
versionString = tkinter.StringVar(tk)
|
versionString = tkinter.StringVar(tk)
|
||||||
versionString.set("v1.8")
|
versionString.set("v1.9")
|
||||||
version = tkinter.ttk.Label(
|
version = tkinter.ttk.Label(
|
||||||
tk,
|
tk,
|
||||||
textvariable=versionString
|
textvariable=versionString
|
||||||
|
|
Loading…
Reference in New Issue