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



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”.

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



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.


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) |