-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
265 lines (187 loc) · 7.77 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# -*- coding: utf-8 -*-
"""
* Program name: cryptopics/main.py
* Version 1.0.0
* Date: 21/07/2017
* @references
[1] https://github.com/cahlen/IMEncrypt
[2] https://gist.github.com/SpotlightKid/53e1eb408267315de620#file-aescrypt-py
* @author Jonas Stein- 16136306; Alan O’Neill- 16134427; Mark Hanlon- 16135571; Aislinn Dolan- 16135245
"""
"""
This work was initially based on the following Python image/file encryption programs:
[1] https://github.com/cahlen/IMEncrypt
[2] https://gist.github.com/SpotlightKid/53e1eb408267315de620#file-aescrypt-py
After we studied them we knew what Python libraries to use and what we would be porting into our code.
The UI idea came from [1], which we found it uses the Tkinter library, documenation can be found here:
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html
The Crypto and PBKDF2 libraries are used in [2], which we then found all we needed in the documentation here:
https://www.dlitz.net/software/pycrypto/api/2.6/
More references are used in the code.
"""
import time
import ttk
from PIL import ImageTk
from Tkinter import *
from tkFileDialog import *
import PIL.Image
import tkMessageBox
import os
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Hash import SHA512
from pbkdf2 import PBKDF2
# Global variables
KEY_SIZE = 32
ITERATIONS = 40000
HASH = SHA512
BLOCK_SIZE = AES.block_size
def print_no_pwd_msg():
tkMessageBox.showinfo("Encryption key required", "Please enter encryption key before selecting a file.")
def open_image():
# check whether a password has been entered
if ( passwd.get() ):
# open window to choose the desired file
filename = askopenfilename()
# extract the file name and its extension
infile, file_extension = os.path.splitext(filename)
# it will decrypt the file if its extension is .cryptopics
if( file_extension == ".cryptopics" ):
decrypt( filename, passwd.get() )
# otherwise it will encrypt
else:
encrypt( filename, passwd.get() )
# if no pwd, then show pop-up message
else:
print_no_pwd_msg()
def encrypt(filename, password):
# append ".cryptopics" to the encrypted file name that will be created
outfile = filename + ".cryptopics"
outfilename = outfile
# create a 32 byte long random salt to be used in the KDF function
salt = Random.new().read( KEY_SIZE )
"""
kdf = key derivation function
- this function will derive a key from the password
- additional security so that when decrypting the image it's necessary
to know the size of the salt hash, the number of iterations, the
hash used, the block size and the password itself
- using KDF, attackers will be unable to crack the password of the file
using the Rainbow Table attack referenced below
- References
https://www.dlitz.net/software/pycrypto/api/2.6/Crypto.Protocol.KDF-module.html
https://en.wikipedia.org/wiki/Rainbow_table
"""
kdf = PBKDF2(password, salt, ITERATIONS, HASH)
# the key created by the kdf will be used to encrypt the data
key = kdf.read( KEY_SIZE )
"""
- create a cypher that we will use to encrypt the data
- this cypher is created with a key, a block cipher mode and an IV(initialization vector)
- The IV is a data block that is used for encryption and decryption
- References
https://www.dlitz.net/software/pycrypto/api/2.6/Crypto.Cipher.AES-module.html
https://www.dlitz.net/software/pycrypto/api/2.6/Crypto.Cipher.blockalgo-module.html#MODE_CBC
"""
iv = Random.new().read( BLOCK_SIZE )
cipher = AES.new(key, AES.MODE_CBC, iv)
# open a new file for writing in binary mode
outfile = open(outfile, 'wb')
# write the salt and the IV into the file that will hold all the encrypted data
# these two pieces of information is needed to decrypt the image
outfile.write( salt + iv )
# open file-to-be-encrypted for reading in binary mode
infile = open(filename, 'rb')
# this loop will go through the file(picture) until the end.
# it will also pad encoded zero's if a chunk of data is not multiple of 16
while True:
# reads data in chunks of 16KB
data = infile.read( 1024 * BLOCK_SIZE )
# check whether the length of data is not multiple of 16
if len(data) % BLOCK_SIZE != 0:
# find the number of bytes that will need to be filled with zero's
data_padding = BLOCK_SIZE - len(data) % BLOCK_SIZE
# append the encoded zero's ('\x00') to the data to complete a block of 16 bytes
data += (data_padding * chr(0)).encode()
# get out of loop when there is no more data to write in to the file
if data == '':
break
"""
- AES(Advanced Encryption Standard) has a fixed data block size of 16 bytes
- The data passed in to the encrypt() function must be multiple of 16
- References
https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
"""
outfile.write(cipher.encrypt(data))
# opens a pop-up message with the location of the decrypted file
tkMessageBox.showinfo("Success", "Image encrypted to file: " + outfilename)
# clears the password in the GUI
passwd.delete(0,END)
def decrypt(filename, password):
# extract the file name and its extension
outfile, file_extension = os.path.splitext(filename)
# since we only decrypt ".cryptopics" extension, it needs to extract the file extension twice
outfile, file_extension = os.path.splitext(outfile)
# appends "_decrypted" to the name of the original file
outfile = outfile + "_decrypted" + file_extension
outfilename = outfile
# open the encrypted file for reading in binary mode
infile = open(filename, 'rb')
# reads the salt from the encrypted file
salt = infile.read( KEY_SIZE )
# derive the key from the password
# the result has to be the same as the key generated when encrypting the file
# otherwise the decryption will fail
kdf = PBKDF2(password, salt, ITERATIONS, HASH)
key = kdf.read( KEY_SIZE )
# reads the IV that was put in the file when encrypting it
iv = infile.read( BLOCK_SIZE )
# creates the AES symmetric cipher to decrypt the data
cipher = AES.new(key, AES.MODE_CBC, iv)
# "b''" represents a binary file which is necessary to initialize the file
data = b''
# open a new file to write the decrypted data for writing in binary mode
outfile = open(outfile, 'wb')
# loop to go through the encrypted data in the file
while data is not None:
# write encrypted data in to the new file
outfile.write(data)
# grab chunks of 16KB of decrypted data
data = cipher.decrypt(infile.read(1024 * BLOCK_SIZE))
# exit the loop when there is no more data to read from the encrypted file
if data == '':
data = None
# opens a pop-up message with the location of the decrypted file
tkMessageBox.showinfo("Success", "Image decrypted to file: " + outfilename)
# clears the password in the GUI
passwd.delete(0,END)
"""
The GUI is based on the Tkinter library.
- Reference
https://www.tutorialspoint.com/python/python_gui_programming.htm
https://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html
"""
# initialize the GUI
root = Tk()
# set the title of the window
root.title("CryptoPics - Python Image Encrypter")
# set the size of the window
window = Frame(root, width=720, height=228, background='white')
window.pack_propagate(0)
window.pack()
img = ImageTk.PhotoImage(file='res/logo.png')
label = Label(window, image=img, borderwidth=0, relief="groove")
label.pack()
passwdLabel = Label(window, text="Enter encryption key to encrypt/decrypt image:", bg='white')
passwdLabel.pack()
# password input field
passwd = Entry(window, show="*", width=30)
passwd.pack()
# select image button
selectImg = Button(window, text="Select Image", command=open_image)
selectImg.pack(pady=4)
# quit button
quit = Button(window, text="Quit", command=window.quit)
quit.pack({"side": "bottom"}, pady=10)
# instantiate the application
root.mainloop()