-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimageCrypt.py
315 lines (224 loc) · 10.5 KB
/
imageCrypt.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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# Image encryption module
"""
Padlock Encryption Software
Copyright 2019
Created by: Suraj Kothari
For A-level Computer Science
at Woodhouse College.
"""
from PIL import Image
import itertools
import time
def getEncryptedPixel(input_pixel, shift, cipherUsed):
"""Encrypts the individual pixels with a shift value"""
# Gets the number of pixel values. JPGs have 3 and PNGs have 4.
numberOfPixelValues = len(input_pixel)
pixel = input_pixel
# Sets the R, G, B values of each pixel
R = pixel[0]
G = pixel[1]
B = pixel[2]
"""
For Medium and Strong ciphers, if the shift is too small, it is increased
to avoid small shifts in colours
"""
if cipherUsed in ("DES", "TripleDES", "AES", "RC4"):
if (shift % 256) < 20 or (256 - (shift % 256)) < 20:
shift += 50
# Shifts each colour of the pixel by the shift value to get the new pixel values
colourRed = (R + shift) % 256
colourGreen = (G + shift) % 256
colourBlue = (B + shift) % 256
# Checks if the image type is PNG
if numberOfPixelValues == 4:
# PNG images have an alpha channel
A = pixel[3]
alpha = (A + shift) % 256
return (colourRed, colourGreen, colourBlue, alpha)
else:
return (colourRed, colourGreen, colourBlue)
def getDecryptedPixel(input_pixel, shift, cipherUsed):
"""Encrypts the individual pixels with a shift value"""
# Gets the number of pixel values. JPGs have 3 and PNGs have 4.
numberOfPixelValues = len(input_pixel)
pixel = input_pixel
# Sets the R,G,B values of each pixel
R = pixel[0]
G = pixel[1]
B = pixel[2]
"""
For Medium and Strong ciphers, if the shift is too small, it is increased
to avoid small shifts in colours
"""
if cipherUsed in ("DES", "TripleDES", "AES", "RC4"):
if (shift % 256) < 20 or (256 - (shift % 256)) < 20:
shift += 50
# Shifts each colour of the pixel by the shift to get the new pixel values
colourRed = (R - shift) % 256
colourGreen = (G - shift) % 256
colourBlue = (B - shift) % 256
"""
Checks if the number of pixel values is 4, as that means the original
image was a PNG and we need to decrypt its alpha channel as well.
"""
if numberOfPixelValues == 4:
# PNG images have an alpha channel
A = pixel[3]
alpha = (A - shift) % 256
return (colourRed, colourGreen, colourBlue, alpha)
else:
return (colourRed, colourGreen, colourBlue)
def getPixelData(width, height, shifts, cipherUsed):
"""Creates a generator function to get pixel and key tuples"""
"""
In AES, the pixels are extracted vertically.
The image is iterated column wise instead of horizontally.
"""
if cipherUsed in ("AES", "RC4"):
"""
Swaps the inner loops of
itertools.product to iterate column-wise.
"""
verticalGenerator = ((x, y) for y in height for x in width)
for pixelValue, key in zip(verticalGenerator, itertools.cycle(shifts)):
# Returns a tuple: (pixelX, pixelY, key)
yield (*pixelValue, key)
else:
"""
Iterates through the pixel values of the width and height combined from itertools.product()
then iterates through the shifts in a cycle using itertools.cycle()
"""
for pixelValue, key in zip(itertools.product(width, height), itertools.cycle(shifts)):
# Returns a tuple: (pixelX, pixelY, key)
yield (*pixelValue, key)
def encryptPixels(width, height, shifts, originalImagePixelData, copyImagePixelData, cipherUsed, isTripleDES=None):
# In Triple DES, the shifts come in a pair
if isTripleDES is True:
shifts_list = shifts[0]
second_shifts = shifts[1]
third_shifts = shifts[2]
else:
shifts_list = shifts
for pixelTuple in getPixelData(width=width, height=height, shifts=shifts_list, cipherUsed=cipherUsed):
# Sets the pixel's X and Y values; and the key value, from the tuple given by the generator function
pixelX, pixelY, shift = pixelTuple[0], pixelTuple[1], pixelTuple[2]
# Gets each pixel value from the original image
pixel = originalImagePixelData[pixelX, pixelY]
if isTripleDES is True:
E_pixel_temp = getEncryptedPixel(input_pixel=pixel, shift=shift, cipherUsed=cipherUsed)
shift2 = second_shifts[shifts_list.index(shift)]
D_pixel = getDecryptedPixel(input_pixel=E_pixel_temp, shift=shift2, cipherUsed=cipherUsed)
shift3 = third_shifts[shifts_list.index(shift)]
E_pixel = getEncryptedPixel(input_pixel=D_pixel, shift=shift3, cipherUsed=cipherUsed)
else:
E_pixel = getEncryptedPixel(input_pixel=pixel, shift=shift, cipherUsed=cipherUsed)
# Stores the changes onto the copied image’s pixel map
copyImagePixelData[pixelX, pixelY] = E_pixel
def decryptPixels(width, height, shifts, encryptedImagePixelData, copyImagePixelData, cipherUsed, isTripleDES=None):
# In Triple DES, the shifts come in a pair
if isTripleDES is True:
shifts_list = shifts[0]
second_shifts = shifts[1]
third_shifts = shifts[2]
else:
shifts_list = shifts
for pixelTuple in getPixelData(width=width, height=height, shifts=shifts_list, cipherUsed=cipherUsed):
# Sets the pixel's X and Y values; and the key value, from the tuple given by the generator function
pixelX, pixelY, shift = pixelTuple[0], pixelTuple[1], pixelTuple[2]
# Gets each pixel value from the original image
pixel = encryptedImagePixelData[pixelX, pixelY]
if isTripleDES is True:
shift3 = third_shifts[shifts_list.index(shift)]
D_pixel_temp = getDecryptedPixel(input_pixel=pixel, shift=shift3, cipherUsed=cipherUsed)
shift2 = second_shifts[shifts_list.index(shift)]
E_pixel = getEncryptedPixel(input_pixel=D_pixel_temp, shift=shift2, cipherUsed=cipherUsed)
D_pixel = getDecryptedPixel(input_pixel=E_pixel, shift=shift, cipherUsed=cipherUsed)
else:
D_pixel = getDecryptedPixel(input_pixel=pixel, shift=shift, cipherUsed=cipherUsed)
# Stores the changes onto the copied image’s pixel map
copyImagePixelData[pixelX, pixelY] = D_pixel
def loadEncryption(filename, filepath, originalImage, imageFormat, shifts, cipherUsed):
"""Gets the image pixel data, manipulates the image, then saves it"""
"""
Gets a pixel access object for the original image
The pixel access object will behave like a 2D list
which will allow the program to read and modify individual pixels.
"""
originalImagePixelData = originalImage.load()
# Makes a copy of the input image and loads the copied image's pixel map
copyImage = Image.new(originalImage.mode, originalImage.size)
copyImagePixelData = copyImage.load()
# Gets the width and height of the copied image
width = range(copyImage.size[0])
height = range(copyImage.size[1])
# Encrypts the image pixels
if cipherUsed == "TripleDES":
encryptPixels(width=width, height=height, shifts=shifts, originalImagePixelData=originalImagePixelData,
copyImagePixelData=copyImagePixelData, cipherUsed=cipherUsed, isTripleDES=True)
else:
encryptPixels(width=width, height=height, shifts=shifts, originalImagePixelData=originalImagePixelData,
copyImagePixelData=copyImagePixelData, cipherUsed=cipherUsed)
# Closes the original image
originalImage.close()
"""
All the filenames are saved as .png, as JPG files perform
lossy compression. This alters the encrypted pixels and is
not beneficial when decrypting.
"""
newFilename = "{}/{}_{}_ENC.png".format(filepath, filename[:-4], cipherUsed)
# Saves the encrypted image and then close it
copyImage.save(newFilename)
copyImage.close()
return newFilename
def loadDecryption(filename, filepath, shifts, cipherUsed):
"""Gets the image pixel data, manipulates the image, then saves it"""
full_filename = filepath + "/" + filename
inputImage = Image.open(full_filename)
"""
Gets a pixel access object for the input image
The pixel access object will behave like a 2D list
which will allow the program to read and modify individual pixels.
"""
encryptedImagePixelData = inputImage.load()
# Makes a copy of the input image and loads the copied image's pixel map
copyImage = Image.new(inputImage.mode, inputImage.size)
copyPixelMap = copyImage.load()
# Gets the width and height of the copied image
width = range(copyImage.size[0])
height = range(copyImage.size[1])
# Decrypts the image pixels
if cipherUsed == "TripleDES":
decryptPixels(width=width, height=height, shifts=shifts, encryptedImagePixelData=encryptedImagePixelData,
copyImagePixelData=copyPixelMap, cipherUsed=cipherUsed, isTripleDES=True)
else:
decryptPixels(width=width, height=height, shifts=shifts, encryptedImagePixelData=encryptedImagePixelData,
copyImagePixelData=copyPixelMap, cipherUsed=cipherUsed)
# Closes the input image
inputImage.close()
if "ENC" in filename:
newFilename = "{}/{}".format(filepath, filename.replace("ENC", "DEC"))
else:
newFilename = "{}/{}_{}_DEC.png".format(filepath, filename[:-4], cipherUsed)
# Saves the encrypted image
copyImage.save(newFilename)
copyImage.close()
return newFilename
def encryptionImageHandler(filename, filepath, shifts, cipherUsed):
"""Checks if the original image needs to be converted to RGBA format"""
full_filename = filepath + "/" + filename
originalImage = Image.open(full_filename)
# Gets the extension of the image
extension = filename.split(".")[-1]
# Checks if the image type is PNG and if Triple DES encryption is not used
if extension == "png":
# PNG images need to be converted to RGBA format
originalImage = originalImage.convert("RGBA")
encryptedData = loadEncryption(filename=filename, filepath=filepath, originalImage=originalImage,
imageFormat=extension, shifts=shifts, cipherUsed=cipherUsed)
return encryptedData
def encrypt(filename, filepath, shifts, cipherUsed):
encryptedData = encryptionImageHandler(filename=filename, filepath=filepath, shifts=shifts, cipherUsed=cipherUsed)
return encryptedData
def decrypt(filename, filepath, shifts, cipherUsed):
decryptedData = loadDecryption(filename=filename, filepath=filepath, shifts=shifts, cipherUsed=cipherUsed)
return decryptedData