Skip to content

Commit 31e6926

Browse files
authored
Add files via upload
1 parent 99f9cea commit 31e6926

File tree

4 files changed

+1355
-0
lines changed

4 files changed

+1355
-0
lines changed

FileEncryption.py

Lines changed: 395 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,395 @@
1+
# FileEncryption.py, Copyright(c) 2021 Martin S. Merkli
2+
#
3+
# This program is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation, either version 3 of the License, or
6+
# (at your option) any later version.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License
14+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
15+
16+
from tkinter import *
17+
from hashlib import sha512 as sha
18+
from hashlib import sha256
19+
from random import randrange
20+
import os
21+
from tkinter import filedialog
22+
from tkinter import messagebox
23+
from time import time
24+
from math import sqrt
25+
26+
debug = False
27+
fileselected = 'none'
28+
filename = 'none'
29+
30+
31+
def xor(x: bytes, y: bytes) -> bytes:
32+
return bytes([_a ^ _b for _a, _b in zip(x, y)])
33+
34+
35+
def hashpassword(password: str, minlength: int) -> bytes:
36+
sha512hashed = sha(password.encode()).digest()
37+
hashed = sha512hashed
38+
while len(hashed) < minlength:
39+
hashed += hashed
40+
printdebug('hashed password')
41+
return hashed
42+
43+
44+
def randomprime(length: int = 16) -> int:
45+
length -= 1
46+
testnumber = randrange((10 ** length) + 1, (9 * (10 ** length)) + 9)
47+
if testnumber % 2 == 0:
48+
testnumber += 1
49+
while not rabinmillerprime(testnumber):
50+
testnumber += 2
51+
printdebug('prime found')
52+
return testnumber
53+
54+
55+
def rabinmillerprime(number: int, rounds: int = 64) -> bool:
56+
small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
57+
if number < 2:
58+
return False
59+
for p in small_primes:
60+
if number < p * p:
61+
return True
62+
if number % p == 0:
63+
return False
64+
r, s = 0, number - 1
65+
while s % 2 == 0:
66+
r += 1
67+
s //= 2
68+
for _ in range(rounds):
69+
a = randrange(2, number - 1)
70+
x = pow(a, s, number)
71+
if x == 1 or x == number - 1:
72+
continue
73+
for _ in range(r - 1):
74+
x = pow(x, 2, number)
75+
if x == number - 1:
76+
break
77+
else:
78+
return False
79+
return True
80+
81+
82+
def modinverse(e: int, phin: int) -> int:
83+
try:
84+
modinv = pow(e, -1, phin)
85+
printdebug('modinverse 3.8+ used')
86+
return modinv
87+
except:
88+
printdebug('using old modinverse')
89+
def egdc(a, b):
90+
if a == 0:
91+
return b, 0, 1
92+
else:
93+
gb, yb, xb = egdc(b % a, a)
94+
return gb, xb - (b // a) * yb, yb
95+
96+
g, x, y = egdc(e, phin)
97+
if g != 1:
98+
raise Exception('modular inverse does not exist')
99+
else:
100+
return x % phin
101+
102+
103+
def generatersakeys(base10length: int = 320) -> list:
104+
p = randomprime(base10length // 2)
105+
q = randomprime(base10length // 2)
106+
while p == q:
107+
printdebug('p=q')
108+
q = randomprime(base10length // 2)
109+
n = p * q
110+
phin = (p - 1) * (q - 1)
111+
e = randomprime(base10length // 2)
112+
while n % e == 2 or phin % e == 2 or q == e or p == e:
113+
e = randomprime(base10length // 2)
114+
d = modinverse(e, phin)
115+
del p
116+
del q
117+
printdebug('generated RSA keys')
118+
return [[e, n], [d, n]]
119+
120+
121+
def rsaencrypt(file: bytes, publickeys: list) -> bytes:
122+
filekey = int(os.urandom(32).hex(), 16)
123+
key = pow(filekey, publickeys[0], publickeys[1])
124+
hashed = hashpassword(str(filekey), len(file))
125+
normalcipher = xor(file, hashed)
126+
cipher = b'\x01RSA'
127+
cipher += bytes([len(str(key).encode()) // 256])
128+
cipher += bytes([len(str(key).encode()) % 256])
129+
cipher += str(key).encode()
130+
cipher += normalcipher
131+
printdebug('encrypted content with rsa')
132+
return cipher
133+
134+
135+
def rsadecrypt(cipher: bytes, privatekeys: list) -> bytes:
136+
if cipher[0] == 1:
137+
printdebug('RSA-version: v1')
138+
return rsadecrypt1(cipher, privatekeys)
139+
else:
140+
printdebug('unknown rsa-version')
141+
askwin = Tk()
142+
askwin.title('Unknown version - FileEncryption')
143+
a1 = Label(askwin, text='The version of the rsa encrypted file is unknown.\nPlease select a version or cancel.')
144+
options = ['cancel', 'v1']
145+
clicked = StringVar()
146+
clicked.set('cancel')
147+
a2 = OptionMenu(askwin, clicked, *options)
148+
a3 = Button(askwin, text='Select', command=askwin.quit)
149+
a1.grid(row=0, column=0)
150+
a2.grid(row=1, column=0)
151+
a3.grid(row=2, column=0)
152+
askwin.mainloop()
153+
selected = clicked.get()
154+
if selected == 'cancel':
155+
printdebug('version canceled')
156+
return b'__cancel__'
157+
elif selected == 'v1':
158+
printdebug('v1 selected')
159+
return rsadecrypt1(cipher, privatekeys)
160+
161+
162+
def rsadecrypt1(cipher: bytes, privatekeys: list) -> bytes:
163+
keylength = (cipher[4] * 256) + (cipher[5]) + 6
164+
cryptkey = int(cipher[6:keylength].decode())
165+
key = pow(cryptkey, privatekeys[0], privatekeys[1])
166+
cipherfile = cipher[keylength:]
167+
hashed = hashpassword(str(key), len(cipherfile))
168+
return xor(cipherfile, hashed)
169+
170+
171+
def inttobytes(number: int) -> bytes:
172+
bstring = b''
173+
new = number
174+
while new > 0:
175+
bstring = bytes([new % 256]) + bstring
176+
new //= 256
177+
printdebug('Turned integer to bytes')
178+
return bstring
179+
180+
181+
def bytestoint(data: bytes) -> int:
182+
return int.from_bytes(data, 'big')
183+
184+
185+
def printdebug(message: str) -> None:
186+
global debug
187+
if debug:
188+
print(str(time()) + ' - ' + message)
189+
else:
190+
pass
191+
192+
193+
def getrsapublic() -> str:
194+
try:
195+
with open('rsa.txt', 'r') as keyfile:
196+
lines = keyfile.readlines()
197+
one = hex(int(lines[0]))[2:]
198+
two = hex(int(lines[2]))[2:]
199+
output = '01g' + one + 'g' + two
200+
except ValueError:
201+
printdebug('ERROR: getrsa didnt work')
202+
output = '__ERROR__'
203+
return output
204+
205+
206+
def selectfile(label: Label):
207+
global fileselected
208+
fileselectedtmp = filedialog.askopenfilename(initialdir=os.getcwd(), title='Select a file')
209+
global filename
210+
if fileselectedtmp == ():
211+
fileselected = 'none'
212+
else:
213+
fileselected = fileselectedtmp
214+
if fileselected == 'none':
215+
filename = 'none'
216+
else:
217+
filename = fileselected.split('/')[-1]
218+
label.config(text=filename)
219+
printdebug('selected file')
220+
221+
222+
def selectfilename():
223+
global fileselected
224+
global filename
225+
filename = fileselected.split('/')[-1]
226+
227+
228+
def isrsakey(potentialkey: str) -> bool:
229+
split = potentialkey.split('g')
230+
for part in split:
231+
part.replace('g', '')
232+
if len(split) == 3 and split[0] == '01':
233+
printdebug('is rsa key')
234+
return True
235+
else:
236+
printdebug('is not rsa key')
237+
return False
238+
239+
240+
def encryptfile(passwordentry: Entry, filedirectory: str):
241+
password = passwordentry.get()
242+
if password == 'password':
243+
printdebug('unsecure password')
244+
if messagebox.askyesnocancel('Warning - FileEncryption', "'password' is one of the worst passwords!"
245+
" Do you really want to continue?"):
246+
pass
247+
else:
248+
return None
249+
if filedirectory == 'none':
250+
printdebug('no file selected')
251+
messagebox.showerror('Error - FileEncryption', 'Error: No file selected.')
252+
return None
253+
with open(filedirectory, 'rb') as originalfile:
254+
with open(filedirectory + '.enc', 'wb') as encfile:
255+
originalcontent = originalfile.read()
256+
hashedpassword = hashpassword(password, len(originalcontent))
257+
encfile.write(b'\x01ENC')
258+
encfile.write(xor(originalcontent, hashedpassword))
259+
messagebox.showinfo('Success - FileEncryption', "The file was successfully encrypted with the following"
260+
" password: '" + password + "'. \nThe original file still"
261+
" exists. You can delete it.")
262+
263+
264+
def decryptfile1(cipher: bytes, password: str) -> bytes:
265+
hashed = hashpassword(password, len(cipher))
266+
content = xor(cipher[4:], hashed)
267+
return content
268+
269+
270+
def decryptfile(passwordentry: Entry, filedirectory: str) -> None:
271+
try:
272+
password = passwordentry.get()
273+
with open(filedirectory, 'rb') as encfile:
274+
with open(filedirectory[:-4], 'wb') as newfile:
275+
cipher = encfile.read()
276+
if cipher[0] == 1:
277+
content = decryptfile1(cipher, password)
278+
printdebug('decrypted with v1')
279+
else:
280+
printdebug('unknown encryption version')
281+
askwin = Tk()
282+
askwin.title('Unknown version - FileEncryption')
283+
a1 = Label(askwin,
284+
text='The version of the encrypted file is unknown.\nPlease select a version or cancel.')
285+
options = ['cancel', 'v1']
286+
clicked = StringVar()
287+
clicked.set('cancel')
288+
a2 = OptionMenu(askwin, clicked, *options)
289+
a3 = Button(askwin, text='Select', command=askwin.quit)
290+
a1.grid(row=0, column=0)
291+
a2.grid(row=1, column=0)
292+
a3.grid(row=2, column=0)
293+
askwin.mainloop()
294+
selected = clicked.get()
295+
if selected == 'cancel':
296+
printdebug('version canceled')
297+
return None
298+
elif selected == 'v1':
299+
printdebug('v1 selected')
300+
content = decryptfile1(cipher, password)
301+
newfile.write(content)
302+
messagebox.showinfo('Success - FileEncryption', 'The file was decrypted, '
303+
'but the password could be wrong.')
304+
except:
305+
messagebox.showerror('Error - FileEncryption', 'An unexpected error accrued.')
306+
307+
308+
def receiversa(passwordentry: Entry, filedirectory: str):
309+
with open('rsa.txt', 'r') as encryptedkeysfile:
310+
with open(filedirectory, 'rb') as originalfile:
311+
with open(filedirectory[:-4], 'wb') as newfile:
312+
passwordint = int(sha256(passwordentry.get().encode()).hexdigest(), 16)
313+
try:
314+
lines = encryptedkeysfile.readlines()
315+
if int(lines[1]) % passwordint == 0:
316+
cipher = originalfile.read()
317+
content = rsadecrypt(cipher, [int(lines[1]) // passwordint, int(lines[2])])
318+
if content != b'__ERROR__' and b'__cancel__':
319+
newfile.write(content)
320+
messagebox.showinfo('Success - FileEncryption', 'The file was successfully decrypted.')
321+
else:
322+
messagebox.showerror('Error - FileEncryption', 'An unexpected error accrued.')
323+
return None
324+
else:
325+
messagebox.showerror('Wrong password - FileEncryption', 'The entered password is incorrect. '
326+
'Please try again.')
327+
except:
328+
messagebox.showerror('Error - FileEncryption', 'An unexpected error accrued.\nThe file with your '
329+
'RSA keys is probably corrupted')
330+
331+
332+
def sendrsa(passwordentry: Entry, filedirectory: str):
333+
key = passwordentry.get()
334+
if isrsakey(key):
335+
with open(filedirectory, 'rb') as originalfile:
336+
with open(filedirectory + '.rsa', 'wb') as rsafile:
337+
splited = key.split('g')
338+
keyone = int(splited[1].replace('g', ''), 16)
339+
keytwo = int(splited[2].replace('g', ''), 16)
340+
publickeys = [keyone, keytwo]
341+
rsafile.write(rsaencrypt(originalfile.read(), publickeys))
342+
messagebox.showinfo('Success - FileEncryption', "The file was successfully encrypted.\nThe original"
343+
" file still exists. You can delete it.")
344+
else:
345+
messagebox.showerror('Error - FileEncryption', 'Error: input is not a valid RSA-key.')
346+
347+
348+
def copypublic(window: Tk) -> None:
349+
key = getrsapublic()
350+
if key != '__ERROR__':
351+
window.clipboard_clear()
352+
window.clipboard_append(getrsapublic())
353+
window.update()
354+
messagebox.showinfo('Success - FileEncryption', 'Your private key was copied to the clipboard.'
355+
' Paste it before closing this window')
356+
else:
357+
messagebox.showerror('Error - FileEncryption', "Error: couldn't get your public key.\nThe file with your "
358+
'RSA keys is probably corrupted')
359+
360+
361+
def openabout():
362+
messagebox.showinfo('About - FileEncryption', 'Copyright(c) 2021 Martin S. Merkli\nThis program is free and'
363+
' open-source software and is licensed under the GNU GPL3.'
364+
' Visit https://www.gnu.org/licenses/ for more information.'
365+
'\nYou can read more about this project in the documentation.')
366+
367+
368+
def startgui():
369+
global fileselected
370+
fileselected = 'none'
371+
root = Tk()
372+
root.title('FileEncryption')
373+
a1 = Button(root, text='Select file', command=lambda: selectfile(a2), width=16)
374+
a2 = Label(root, text=filename)
375+
b = Entry(root, width=38, show='*')
376+
c1 = Button(root, text='encrypt', command=lambda: encryptfile(b, fileselected), width=16)
377+
c2 = Button(root, text='decrypt', command=lambda: decryptfile(b, fileselected), width=16)
378+
d1 = Button(root, text='send', command=lambda: sendrsa(b, fileselected), width=16)
379+
d2 = Button(root, text='receive', command=lambda: receiversa(b, fileselected), width=16)
380+
e1 = Button(root, text='copy public key', command=lambda: copypublic(root), width=16)
381+
e2 = Button(root, text='About', command=openabout, width=16)
382+
a1.grid(row=0, column=0)
383+
a2.grid(row=0, column=1)
384+
b.grid(row=1, column=0, columnspan=2)
385+
c1.grid(row=2, column=0)
386+
c2.grid(row=2, column=1)
387+
d1.grid(row=3, column=0)
388+
d2.grid(row=3, column=1)
389+
e1.grid(row=4, column=0)
390+
e2.grid(row=4, column=1)
391+
root.mainloop()
392+
393+
394+
if __name__ == '__main__':
395+
startgui()

0 commit comments

Comments
 (0)