2

I am trying to test create a simple cipher that xor key with a bitmap image.

import os, io, hashlib
from PIL import Image
from array import array
from itertools import cycle

key = "aaaabbbb"

def generate_keys(key):
    round_keys = hashlib.md5(key).digest()
    return bytearray(round_keys)

def readimage(path):
    with open(path, "rb") as f:
            return bytearray(f.read())

def generate_output_image(input_image, filename_out):
    output_image = Image.open(io.BytesIO(input_image))
    output_image.save(filename_out)

def xor(x,y):
    return [ a ^ b for a,b in zip(x,cycle(y))]

round_keys = generate_keys(key)
input_image = readimage("lena512.bmp")
encrypted_image = xor(input_image, round_keys)
generate_output_image(encrypted_image, "lena_encrypted.bmp")

input_image = readimage("lena_encrypted.bmp");
decrypted_image = xor(input_image, round_keys)
generate_output_image(decrypted_image, "lena_decrypted.bmp")

But I'm getting the following error when I run the script:

Traceback (most recent call last):
    File "test.py", line 26, in <module>
        generate_output_image(encrypted_image, "lena_encrypted.bmp")
    File "test.py", line 17, in generate_output_image
        output_image = Image.open(io.BytesIO(input_image))
TypeError: 'list' does not have the buffer interface

How do I convert the byte array back into bitmap image? Any help would be appreciated thanks.

  • Your `xor` function returns a list, so you can't pass `encrypted_image` and `decrypted_image` as an argument to `io.BytesIO()`. Maybe changing the `xor` function to `return bytearray([ a ^ b for a,b in zip(x,cycle(y))])` could help. – mportes Dec 07 '18 at 16:29
  • Thanks myrmica. Your suggestion helped when I'm converting the decrypted bytearray into bitmap image. However, I could not convert encrypted bytearray to bitmap image. I was hoping to get an image of white noise and will be able to decrypt the white noise image back to the original bitmap. Any idea how to do that? – python_learner Dec 07 '18 at 17:26
  • BMP files have a header with information that you must not modify with your cipher. There should be something like `return x[:54] + bytearray([a^b for a, b in zip(x[54:], cycle(y))])` in the `xor` function. The actual offset may differ though, as there are [different versions of BMP](https://en.wikipedia.org/wiki/BMP_file_format#File_structure). – mportes Dec 07 '18 at 20:46

2 Answers2

2

I looked a bit more into how to retrieve/update the image data more elegantly with the tools included in PIL/Pillow, and made this simple working example:

from PIL import BmpImagePlugin
import hashlib
from itertools import cycle

keys = hashlib.md5(b"aaaabbbb").digest()

input_image = BmpImagePlugin.BmpImageFile("img/tea.bmp")

# extract pure image data as bytes
image_data = input_image.tobytes()

# encrypt
image_data = bytes(a^b for a, b in zip(image_data, cycle(keys)))

# create new image, update with encrypted data and save
output_image = input_image.copy()
output_image.frombytes(image_data)
output_image.save("img/tea-encrypted.bmp")

This way, you don't have to worry about the BMP header size.

(links: original and encrypted image)

mportes
  • 1,589
  • 5
  • 13
1

Updated script thats convert plain image to cipher image and back to plain image.

import os, io, hashlib, math, binascii
from PIL import Image
from array import array
from itertools import cycle

user_key = "aaaabbbb"
test_image = "lena512.bmp"

def generate_keys(key):
    round_keys = hashlib.md5(key).digest()
    return bytearray(round_keys)

def readimage(path):
    with open(path, "rb") as f:
            return bytearray(f.read())

def generate_output_image(input_image):
    output_image = Image.open(io.BytesIO(input_image))
    output_image.save(test_image)

def xor(x,y):
    return x[:54] + bytearray([a^b for a, b in zip(x[54:], cycle(y))])

round_keys = generate_keys(user_key)
input_image = readimage(test_image)
encrypted_image = xor(input_image, round_keys)
generate_output_image(encrypted_image)

input_image = readimage(test_image);
decrypted_image = xor(input_image, round_keys)
generate_output_image(decrypted_image)

Thanks myrmica for your help