I want to gpg --sign the pixel data in a PNG image. Later, I want to be able to gpg --verify the signature on the pixel data as-found to know the image's author and that it hasn't changed since signing.
Motivation
I am developing a system which allows the integrity of PNG images to be verified and attributed (cf. non-repudiation) at a later date. I am working in Python. Ideally, I want the additional information to not hinder any user from reading the image as usual.
What is important in an image? In my application, only the pixel data is important. I need to get this data into a form which gpg --sign
accepts, which amounts to serializing a Python object. The ability to verify a signature in the future of an image which has not been tampered with relies upon the serialization (pickle.dumps, cPickle.dumps) producing the same output say 5 years from now.
Why not just sign the file with gpg --sign? Sticking the signature of the pixel data back in the same image's metadata keeps the signature 1. attached and 2. valid. Anywhere the image goes, so goes the signature (apart from any overzealous software which might remove it).
The example below is meant to be run in ipython.
Sign a PNG image.
# The PNG image to sign.
f = 'image.png'
# Read pixel data from a PNG image.
from pillow import Image
serialized = Image.open(f).tobytes(encoder_name='raw')
open('serialized.txt').write(pixel_data)
# Sign the data with gpg using the default private key.
!gpg --sign --detach --armor --output sig.asc serialized.txt
# <enter gpg passphrase as required>
# Write the signature to the PNG image's metadata using Image Magick.
import subprocess
subprocess.call(['mogrify', '-set', 'gpgsig', open('sig.asc').read()])
# Verify it got added using Image Magick.
!identify -verbose image.png
Much, much later, attempt to verify the signature
# Many, many moons go by, and you find a PNG image in the wild.
# It looks familiar, but its provenance and integrity are unknown.
# But lo! it includes a gpg signature in its metadata.
# You want to verify the signature (which may have been signed
# by yourself or anybody else whose gpg public key you have
# access to) to ensure the image is unchanged.
# Read the pixel data, same as before.
from pillow import Image
serialized = Image.open(f).tobytes(encoder_name='raw')
open('serialized.txt').write(serialized)
# Get the included signature. This may be done
# programmatically, but for the sake of simplicity,
# just use Image Magick.
!identify -verbose image.png
# Save the signature to sig.txt.
# Attempt to verify the detached signature
!gpg --verify sig.txt serialized.txt