<--- -------------------------------------------------------------------------------------- ---Blog Entry: Embedding Secret Messages In An Image Using ColdFusion Code Snippet: 2 Author: Ben Nadel / Kinky Solutions

Link: http://www.bennadel.com/index.cfm?event=blog.view&id=1806 Date Posted: Jan 8, 2010 at 5:16 PM ---- -------------------------------------------------------------------------------------- ---> <cfcomponent output="false" hint="I provide functionality to embed and extract messages within a giv en image based on a shared key image."> <cffunction name="init" access="public" returntype="any" output="false" hint="I return an intialized component."> <!--- Return this object reference. ---> <cfreturn this /> </cffunction> <cffunction name="copyImage" access="public" returntype="any" output="false" hint="I duplicate the given image."> <!--- Define arguments. ---> <cfargument name="image" type="any" required="true" hint="I am the image being duplicated." /> <!--- Copy the image and return it. ---> <cfreturn imageCopy( arguments.image, 0, 0,

---> <cfif ( (len( arguments. ---> <cfset local.Define the local scope.imageHeight) )> <!--.image ) ) /> </cffunction> <cffunction name="embedMessage" access="public" returntype="any" output="false" hint="I embed the given message in a duplicate of the give share d image key and return the message image.message ) * 8) gt (local.Define arguments.image ).image = this.copyImage( arguments.Throw message length error.imageWidth = imageGetWidth( local. ---> <cfset var local = {} /> <!--Duplicate the image key (since we don't want to alter the key itself but rather work based off of it)."> <!--." /> <cfargument name="message" type="string" required="true" hint="I am the message being embedded in the image key ( a duplicate of the image key).Get the width and height of the image. ---> <cfthrow type="MessageLength" message="The message you are trying to embed is too long for this image. Each character will require 8 pixels we are going to store one ASCII BIT per pixel (we need 8 bits to hold one character).imageWidth * local. ---> <cfargument name="keyImage" type="any" required="true" hint="I am the image key (the shared image) that will be used to create the message image." /> <!--.imageGetWidth( arguments." .image ) /> <cfset local.imageHeight = imageGetHeight( local.keyImage ) /> <!--.image ) /> <!--Check to see if this image is large enough to hold the given message. imageGetHeight( arguments. ---> <cfset local.

0 ).pixelBuffer = local. 0 ). ---> <cfset arrayResize( local. javaCast( "int". "" ).pixelBuffer ) ) /> <!--Now. This will give use access to all of the underlying pixel data for our "message" image.imageWidth * local.messagePixelBuffer.imageWidth ). ---> <cfset local. arrayLen( local.requiredRows ).image ) /> <!--Figure out the number of pixel rows that will be required to embed the message.getRGB( javaCast( "int". javaCast( "int". ---> <cfset local.imageWidth ) ) /> <!--Now that we have our pixel buffer. javaCast( "null". 0 ).requiredRows = ceiling( len( arguments.message )#] and the given image key can only accept me ssages of max length [#fix( local.messagePixelBuffer = [] /> <!--Resize to be the size of the original pixel buffer this will be faster to resize upfront than to later set values by appending them as needed. javaCast( "int". javaCast( "int". ." /> </cfif> <!--Get the underlying buffered image. javaCast( "int". NOTE: We can convert this back to a Java array when we overwrite the pixel buffer. ---> <cfset local.imageWidth ) /> <!--Get enough RGB pixels to embed the message based on the given rows. local. we need to create our own into which we can store the "message" pixels. ---> <cfset local.detail="The message you are trying to embed is o f length [#len( arguments.bufferedImage. let's split the message up into an array so that we can easily access each of the characters.imageHeight / 8 )#]. local.message ) * 8 / local. local.bufferedImage = imageGetBufferedImage( local.

. We will need 8 pixles (one per bit) to embed a single ascii character. If it is.character" array="#local.".---> <cfset local. ---> <cfset local.Loop over the characters.characterAscii.character ) /> <!--Make sure the ASCII value is not greater than 25 5. ---> <cfset arrayPrepend( local.characaterBits. ---> <cfloop index="local.pixelIndex = 1 /> <!--. we are going to replace it with the "? " mark to indicate that this is beyond the capabilities of the embedding. arguments. formatBaseN( local.Make sure the bit array is 8 bits.Prepend a zero.characterAscii = asc( local.characterAscii = 63 /> </cfif> <!--. Therefore we need to keep a seperate index for the pixel buffer than we do for the characters. "0" ) /> </cfloop> <!--Loop over the array bit array and use each bit t o update the pixel color.characaterBits = reMatch( ". ---> <cfset local.Convert the character to it's ASCII value. ---> <cfset local.characters#"> <!--.characters = reMatch( "[\w\W]". ---> <cfloop condition="arrayLen( local. ---> <cfif (local.characterAscii gt 255)> <!--.Convert the charater to a array of bits.Overwrite with "?" ascii.characaterBits ) lt 8 "> <!--.message ) /> <!--We are going to loop over the characters to embed them in the pixel colors. ---> <cfset local. 2 ) ) /> <!--.

requiredRows ). copy over the rest of the pixels (which aren't being used to embed character data). ---> <cfset local. ---> <cfset local.pixelIndex ] .characterBit) /> <cfelse> <!--.characaterBits#"> <!--Check to see if the current pixel value is positive or negative.messagePixelBuffer[ local. javaCast( "int".pixelIndex" from="#local.Negative value. ---> <cfset local.Increment the pixel index.imageWidth ). we are goint to move towards zero.pixelIndex++ /> </cfloop> </cfloop> <!--Now that we have copied over the characters. so subtract.setRGB( javaCast( "int". let's write the pixel buffer BACK into the message image. javaCast( "int[]".bufferedImage.pixelIndex ] /> </cfloop> <!--Now. 0 ).p ixelIndex ] = (local.---> <cfloop index="local. local.pixelBuffer )#" step="1"> <!--.pixelIndex ] + local.p ixelIndex ] = (local.characterBit) /> </cfif> <!--.pixelBuffer[ local.messagePixelBuffer ).pixelIndex ] = lo cal.pixelIndex#" to="#arrayLen( local.pixelIndex ]> <!--. so add. To be on the safe side.messagePixelBuffer[ local.characterBit" array="#local.pixelBuffer[ local. ---> <cfset local. ---> <cfloop index="local.pixelBuffer[ local. .Positive value. ---> <cfset local. local.local.Copy existing color. local. javaCast( "int". ---> <cfif local. javaCast( "int".pixelBuffer[ local.messagePixelBuffer[ local. 0 ).

Get the key image dimentions.messageImageWidth) && (local.Define arguments.Define the local scope.keyImage ) /> <cfset local.Get the message image dimentions.messageImageWidth = imageGetWidth( arguments. ---> <cfset var local = {} /> <!--."> <!--.keyImageHeight eq local.messag eImage ) /> <cfset local.imageWidth ) ) /> <!--." /> <cfargument name="messageImage" type="any" required="true" hint="I am the image with the embedded image. If not.mess ageImage ) /> <!--Check to make sure the images have the same dimensions." /> <!--.image /> </cffunction> <cffunction name="extractMessage" access="public" returntype="string" output="false" hint="I extract an embedded message from the given image based o n the shared key image.javaCast( "int". ---> <cfreturn local.keyImageHeight = imageGetHeight( arguments. local. javaCast( "int".messageImageHeight = imageGetHeight( arguments. ---> <cfset local.keyImageWidth eq local. 0 ). then correct key has not been supplied (not to mention we might get OutOfBounds errors when trying to extract the message). ---> <cfset local.keyImage ) /> <!--.Return the message image. ---> <cfargument name="keyImage" type="any" required="true" hint="I am the image key (the shared image) that will be used to extract the embedded message.keyImageWidth = imageGetWidth( arguments. ---> <cfif !( (local.messageImageHeight) )> <cfthrow .

messageImage ) . 0 ). ---> <cfset local.characters = [] /> <!--Create a bit array to hold the differnce between each pixel.keyImage ) .characterBits = [] /> <!--Loop over the pixel buffer to start adding pixel . javaCast( "int"." /> </cfif> <!--Get the pixels from the key image.getRGB( javaCast( "int". we'll just get all of the pixels. javaCast( "int".messageImageWidth ). ---> <cfset local. 0 ). "" ). Because we don't know how loarge the message is. javaCast( "int". javaCast( "int".keyImageWidth ) ) /> <!--Get the pixels from the message image. javaCast( "int". javaCast( "int".getRGB( javaCast( "int".messagePixels = imageGetBufferedImage( arguments. ---> <cfset local. 0 ). 0 ). javaCast( "int". we'll just get all of the pixels. ---> <cfset local.keyPixels = imageGetBufferedImage( arguments. As we find each character. javaCast( "int". javaCast( "int". Because we don't know how large the message is. local.type="IncorrectKeyImage" message="The key image you provided does not hav e the same dimensions as your message image.messageImageHeight ). local. 0 ).keyImageWidth ). we will append it to this array (to later be turned into a full string). local. local. 0 ). javaCast( "null".keyImageHeight ). "" ). local. local.messageImageWidth ) ) /> <!--Create a message character array. javaCast( "int". javaCast( "null".

characterBits ) eq 8)> <!--.We are done.messagePixels )#" step="1"> <!--Append the difference the character bits. 2 ) /> <!--Check to see if the ASCII value is zero.pixelInd ex ] ) ) ) /> <!--Check to see if our bit array is of lenth 8. ---> <cfif (arrayLen( local.keyPixels[ local. ---> <cfset local.pixelIndex ] ) abs( local.--> <!--. Break out of loop! --> <cfbreak /> <!--.messagePixels[ local. ---> <cfset arrayAppend( local. This would result if there was no difference between the two pixels. ---> <cfloop index="local. Be sur e to use the absolute values since the alpha chann el can give us odds values. "" ). this will signal the end of the string.differences to the bit array.------------------------------.Convert to ASCII value. If it is.------------------------------.characterAscii> <!--.characterAscii = inputBaseN( arrayToList( local. ---> <cfif !local.characterBits.--> </cfif> .pixelIndex" from="1" to="#arrayLen( local. abs( abs( local.characterBits. then we have collected an entire characte r which we can then convert to CHR and append to t he message array.

characters. ---> <cfset local.characters. ---> <!--Append character to building message cha racter array. chr( local. ---> <cfreturn arrayToList( local. "" ) /> </cffunction> </cfcomponent> . ---> <cfset arrayAppend( local.characterBits = [] /> </cfif> </cfloop> <!--Join the character array as a single string and return it. When we do this.characterAscii ) ) /> <!--Reset the bit array (so that we can star t building the next character).ASSERT: We are still matching.<!--. we have to conve rt the ASCII value to a printable character.