Created
August 24, 2013 19:42
-
-
Save joakibj/6330040 to your computer and use it in GitHub Desktop.
Made this into a gist instead. Contains an implementation of the Least Significant Bit algorithm for steganography. Hides a png image (payload) in the n lowest bits of another png image(carrier), resulting in contaminated image called the package. This program was created for the Ukens Nøtt (nut of the week) newsmail, security group, Visma Consu…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os, sys, png, argparse | |
#coding: utf-8 | |
""" | |
Contains an implementation of the Least Significant Bit algorithm for steganography. | |
Hides a png image (payload) in the n lowest bits of another png image(carrier), resulting in contaminated image called the package. | |
""" | |
class Error(Exception): | |
"""Base class for exceptions""" | |
pass | |
class PayloadLargerThanCarrierError(Error): | |
"""The payload image is larger than the carrier image in either width or height""" | |
class Image: | |
def __init__(self, width, height, pixels=None): | |
self.width = width | |
self.height = height | |
self.pixels = pixels | |
def encode(args): | |
carrier = read_png(args.carrier[0]) | |
print 'The carrier image size is %d*%d' % (carrier.width, carrier.height) | |
payload = read_png(args.payload[0]) | |
print 'The payload image size is %d*%d' % (payload.width, payload.height) | |
if (carrier.width < payload.width) or (carrier.height < payload.height): | |
raise PayloadLargerThanCarrierError() | |
package = encode_payload_into_carrier(payload, carrier, args.encode_bits) | |
write_png(package, 'package.png') | |
print 'Encoded payload(%s) into carrier(%s) in the lowest %d bits.' % (args.payload[0], args.carrier[0], args.encode_bits) | |
print 'Written package.png. Exiting... ' | |
def encode_payload_into_carrier(payload, carrier, encodeBits): | |
package = carrier | |
for row in range(len(payload.pixels)): | |
for colourComponent in range(len(payload.pixels[row])): | |
package.pixels[row][colourComponent] = encode_colour_component(carrier.pixels[row][colourComponent], payload.pixels[row][colourComponent], encodeBits) | |
return package | |
def encode_colour_component(carrierColourComponent, payloadColourComponent, encodeBits): | |
mask = 0b11111111 << encodeBits | |
payloadColourComponent = payloadColourComponent >> 8 - encodeBits | |
carrierColourComponent = carrierColourComponent & mask | |
return carrierColourComponent + payloadColourComponent | |
def decode(args): | |
suspect = read_png(args.suspect[0]) | |
print 'The suspect image is %d*%d' % (suspect.width, suspect.height) | |
payload = decode_payload_from_suspect(suspect, args.encode_bits) | |
write_png(payload, 'payload.png') | |
print 'Decoded payload from suspect(%s).' % (args.suspect[0]) | |
print 'Written payload.png. Exiting... ' | |
def decode_payload_from_suspect(suspect, encodeBits): | |
payload = suspect | |
for row in range(len(suspect.pixels)): | |
for colourComponent in range(len(suspect.pixels[row])): | |
payload.pixels[row][colourComponent] = decode_colour_component(suspect.pixels[row][colourComponent], encodeBits) | |
return payload | |
def decode_colour_component(suspectPixel, encodeBits): | |
suspectPixel = suspectPixel << 8 - encodeBits | |
return 0b11111111 & suspectPixel | |
def write_png(image, fileName): | |
fileHandle = open(fileName, 'wb') | |
writer = png.Writer(image.width, image.height, alpha=True) | |
writer.write(fileHandle, image.pixels) | |
fileHandle.close() | |
def read_png(fileName): | |
fileHandle = open(fileName, 'rb') | |
pngImage = png.Reader(fileHandle).asRGBA() | |
pixels = list(pngImage[2]) | |
fileHandle.close() | |
return Image(pngImage[0], pngImage[1], pixels) | |
def init_parser(): | |
parent_parser = argparse.ArgumentParser(add_help=False) | |
parent_parser.add_argument('-eb', '--encode-bits', type=int, default=3) | |
parser = argparse.ArgumentParser(parents=[parent_parser], description='Encodes and decodes a png inside a png. A practice in image steganography.') | |
subparsers = parser.add_subparsers(help='subcommand help') | |
encode_parser = subparsers.add_parser('encode', help='encode help', description='Encode a payload in the lower encode_bits of the carrier png') | |
encode_parser.add_argument('-c', '--carrier', nargs=1, required=True, help='The PNG file where the payload is hidden') | |
encode_parser.add_argument('-p', '--payload', nargs=1, required=True, help='The PNG with the secret message') | |
encode_parser.set_defaults(func=encode) | |
decode_parser = subparsers.add_parser('decode', help='decode help', description='Decode a carrier png, extracting the payload from the lower encode_bits') | |
decode_parser.add_argument('-s', '--suspect', nargs=1, required=True, help='The PNG with the suspected secret message') | |
decode_parser.set_defaults(func=decode) | |
return parser | |
def call_subcommand(args): | |
try: | |
args.func(args) | |
except PayloadLargerThanCarrierError: | |
print 'The payload image does not fit inside the carrier image.' | |
print 'Exiting...' | |
def main(): | |
parser = init_parser() | |
args = parser.parse_args() | |
call_subcommand(args) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment