XOR PNG script

Description

After seeing a thread on reddit the other day I wanted to make a little script to XOR encrypt a PNG image file, and still being able to view the image. The script encrypt the pixel values o the PNG and stores the resulting image. It makes it very visually clear that choosing a good key when encrypting something is important. The pictures below shows a few different pictures encrypted with several different keys.

Pictures

Unencrypted picture of the astronaut.
Unencrypted picture of the astronaut.
Astronaut picture encrypted with the key "a".Can still clearly see the astronaut.
Astronaut picture encrypted with the key “a”. Can still clearly see the astronaut.
Astronaut picture encrypted with the key long key. Can still clearly see the astronaut.
Astronaut picture encrypted with the key long key.

Here the picture is encrypted with a key that is over 600 chars long, and you can still clearly see the astronaut in the picture. A small pattern has emerged in the picture compared to the image above it which was only encrypted with the key “a”.

The astronaut picture encrypted with a one time pad. Can no longer see any resemblance.
The astronaut picture encrypted with a one time pad.

This is the astronaut picture, and all visual resemblance with the previous pictures is gone.

Pinkie Pie unencrypted.
Pinkie Pie unencrypted.
Pinkie Pie encrypted with the key "a" and still looking as happy as always.
Pinkie Pie encrypted with the key “a” and still looking as happy as always.
Pinkie Pie encrypted with a long key, but still clearly visible, but picture has a small pattern on it.
Pinkie Pie encrypted with a long key.

Here Pinkie Pie is encrypted with the same long key (over 600 chars) as the astronaut picture. The small pattern is not as strong in this picture.

Drawing of the grumpy cat unencrypted.
Drawing of the grumpy cat unencrypted.
Drawing of the grumpy cat encrypted with the key "a". The cat is still clearly visible.
Drawing of the grumpy cat encrypted with the key “a”.

Code

"""
Idea spawned from a reddit thread: 
http://www.reddit.com/r/geek/comments/18vvdg/my_friend_asked_me_if_a_onetime_pad_was_secure/
 
It XORs the inputted image either with a one time pad, or a user specified key. 
It cleary visualizes why strong keys are needed for encryption.
 
WARNING: 
Do not actually use this to encrypt stuff for privacy. It only changes pixel values, and
not metadata, and the image header.
 
Sindre Smistad <sindre@downgoat.net>
www.DownGoat.net
"""
 
import sys
import random
import argparse
from PIL import Image
 
MODES = ["RGB", "RGBA"]
 
op = False
 
def xor(pixel, mode, key):
	#XOR all the channels with the same key.
	red = pixel[0] ^ key[0]
	green = pixel[1] ^ key[1]
	blue = pixel[2] ^ key[2]
 
	if mode == "RGBA": #Need Aplha if RGBA
		return (red, green, blue, 255)
	else:
		return (red, green, blue)
 
parser = argparse.ArgumentParser(description="XOR-viewer")
parser.add_argument("pic_name", action="store", help="Name of picture file")
parser.add_argument("out_pic", action="store", help="Name of the output file.")
parser.add_argument("-op", help="Use one time pad.", action="store_true")
parser.add_argument("-k", help="Encryption key", action="store")
 
args = parser.parse_args()
 
#Raise exception if both k and op is set, don't know which one to use then.
if args.op and args.k:
	raise ValueError("Cannot use both one time pad and a key.")
 
#If none is set go with one time pad.
if not args.op and not args.k:
	print("No key specified, will use one time pad.")
	op = True
 
if args.op:
	op = True
 
im = Image.open(args.pic_name)
 
#If the image mode is not in the list of supported modes, rase a exception.
if im.mode not in MODES:
	raise NotImplementedError("The image mode '{0}' is not supported.".format(im.mode))
 
key = []
org_len = 0
 
pix = im.load()
width, height = im.size
 
if op:
	#Number of keys needed is pixel in the image times 3 because each channel needs a key.
	for x in range(0, width*height*3): 
		key.append(random.SystemRandom().randint(0, 255))
 
	org_len = len(key)
else:
	#Key needs to be atleast 3.
	temp = ""
	if len(args.k) < 3:
		temp  = args.k + args.k
		if len(temp) < 3:
			temp += args.k
	else:
		temp = args.k
 
	for l in temp:
		key.append(ord(l)) 
 
	org_len = len(key)
 
	#Key robin hack.
	key.append(ord(temp[0]))
	key.append(ord(temp[1]))
 
key_count = 0
for x in range(0, width):
	for y in range(0, height):
		if key_count >= org_len:
			key_count = 0
 
		pix[x, y] = xor(pix[x, y], im.mode, (key[key_count], key[key_count+1], key[key_count+2]))
 
		key_count += 3
 
im.save(args.out_pic)
This entry was posted in Python and tagged , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>