Accéder au contenu principal

Hiding one picture into another using HTML5 canvas

Back in the years when I was still coding in QBASIC (pretty nice language to start programmation), I took an interest in steganography (see Wikipedia for more information on the subject) and quickly came across an article describing a technique to hide a picture into another one. 

The aim of this post is to share this technique and rapidly shows how it can be implemented in HTML5 using canvas elements. The original piece of software I built with QBASIC, I  called it "Blackshell", did work neatly, but was very slow (about 1 min of processing to hide a 100*100 picture). As I've been looking for a project to get myself started with the use of canvases with HTML5 for a few months, I thought I might just try to port that tool in HTML/JS.

You can find a first draft here:
http://manuthommes.be/toolbox/html5exp/

EDIT: it seems that the script does not run properly on my website any longer (beats me why...). If you encounter trouble too (i.e. the colors are all weird when you hide an image), just try and downoad the files and execute the page from your computer. You just need the following three files:
http://manuthommes.be/toolbox/html5exp/index.html
http://manuthommes.be/toolbox/html5exp/guest.jpg
http://manuthommes.be/toolbox/html5exp/host.jpg



The source is not obfuscated, so don't hesitate checking, copying and adapting. I'll first explain the concept, then review some technical details (although it is not too complicated).  Don't hesitate correcting me if you find something incorrect in the following paragraph,as it's been 6-7 years since I first read about this technique, and my memory might not be so good :-)


Each channel (RVB) of a pixel contains pieces of information about the contrast and the brightness of that very pixel. When the data of a pixel is converted to a binary value, you will actually get an 8-position number, such as for instance: 10010001 (note that you could have fewer positions, but we want 8 of them here, so that 0100 means 00000100 to us).The first four bytes (strong) indicate the contrast, while the last four (weak) indicate the brightness. The contrast is they key factor to discriminate one pixel from another (at least, for the human eye), so here's the concept (illustrated by a chart further down):

Convert all pixels of the host and guest images to a binary number (1100 0101 and 0010 1001). To determine the information of the new pixel (host image with guest image hidden), take the strong bytes of the host then append the strong bytes of the guest (1100 0010). Following this method, you will get a picture that is almost identical to the host, but that contains enough information to rebuild the guest. The quality of the result picture however depends a lot on the pictures you want to hide or use as host.

To retrieve the guest, convert again each pixel to a binary number, then create the new pixel by concatenating the weak bytes of the result picture then four zeros. The guest picture will definitely lose some quality, but should remain pretty close to the original.


 


How do you do that in HTML5/JS?

- Create two canvases and two images (the latter will be painted on the canvases)
- To get pixel information of the two canvases, you will need to use getImageData(left,top,width,height)
Loop the array of pixels of the host/guest image and process independantly the RVB channels to get their binary value and build the new pixel
- Ouput the result on a canvas (can be a new one or one of the two already created)
- To retrieve the guest, proceed the same way, with the only difference that you only need to get the information of the result image to restore the guest

I just want to make a special note on the following snippet (placed within a for loop):

var hpxl=pix[i].toString(2); while(hpxl.length<8)hpxl="x"+hpxl;    // hpxl = host pixel red
var gpxl=pixB[i].toString(2); while(gpxl.length<8)gpxl="x"+gpxl;    // gpxl = guest pixel red      var npxl=hpxl.substr(0,4)+gpxl.substr(0,4);npxl=npxl.replace(/\x/g,"0");
var newPixel=parseInt(npxl, 2);
pixB[i] =  newPixel; // red channel

This is the function used to create the new pixel of the host picture. 

First, we get the binary value of the red channel of the current pixel of the host picture (blue channel is pix[i+1], red is i+2 and alpha (not used here) is i+3). We basically convert an integer to a string, passing the parameter radix=2 (binay representation).
>> var hpxl=pix[i].toString(2);

Earlier, I mentionned we needed 8 positions. Sadly, JS (and many other languages) truncate binary representations that begin with 0's. Since we do need 8 positions, we will temporarily add as many "x" character as needed to have 8 positions.
>> while(hpxl.length<8)hpxl="x"+hpxl; 

Then, we do the same thing for the guest pixel. 
>> var gpxl=pixB[i].toString(2); while(gpxl.length<8)gpxl="x"+gpxl;

We now compose the new pixel's red value (first 4 bytes of the host + fist 4 bytes of the guest)
>> var npxl=hpxl.substr(0,4)+gpxl.substr(0,4);

We can then replace all our x's with actual 0's (doesn't matter if the string is truncated at this point)
>> npxl=npxl.replace(/\x/g,"0");

Last but not least, we convert our string (it's a binary representation, but still a string!) to an integer:
>> var newPixel=parseInt(npxl, 2);

Same thing goes for the blue and green channel of the current pixel. Once this is done, we jump to the next pixel.  

Final note


HTML canvases produce PNG images, which are supposedly lossless (like BMPs) so you will have no problem sharing pictures and have people decypher them. If you however compress the host (containing the guest) in - say - JPG or GIF, you will obviously never be able to retrieve the guest.
I insist on the fact that the quality of the technique is very dependant on the pictures you will use as host and guest (hiding a plain text usually doesn't yield good results). Finally, the algorithm can be optimized by using more or less bytes of the host/guest pictures (5-3 instead of 4-4).



Commentaires

Posts les plus consultés de ce blog

HTML tables and auto-sizing cell heights : a workaround

Although this was common practice in the early days of the Web, working with HTML tables when it comes to building a webpage layout should be avoided as much as possible nowadays. CSS offers all the tools that are needed to create perfectly dynamic and flexible layouts, so you really should only rely on HTML tables to present actual data. This being said, it happens that you don't have the choice, and that this just what I've learned recently in my work. In the context of a client project, the framework I've had to use (Peoplesoft, an HRIS system) indeed generates HTML pages presenting a structure quite "2000"-ish, consisting in a complex nesting of HTML tables. Moreover, each time you add a component on the page (via the system itself, not when hardcoding it), a system TABLE is create to wrap the object. This can cause obviously a lot of problems, but in this case, my problem was quite specific. A few pages of the system use a well-defined table layout :

Lightbox effect with jQuery (3) - ready-to-use library

I've been pretty busy with work over the past days, but I managed to finalize my lightbox javascript library still. Although I can't confirm it is 100% cross-browser, it seems to be running well under IE8/9, Firefox and Chrome. I reckon it should work as well on Safari, but you never know. The actual library and a sample HTML file can be found here: https://sourceforge.net/projects/delightbox/files/ How to use ? First, include jQuery and, needless to say, the library. Second, call the function init from dlb , once the DOM has been fully created and the page is ready. <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript" src="./delightbox-latest.js"></script>         <script>         jQuery(document).ready(function(){             dlb.init();         }); </script> Delightbox allows to create a lightbox e

A binary clock in Javascript

Just a short post to share a tiny project I recently worked on: a binary clock. The script is written in pure Javascript (no jQuery for a change), and the design of the buttons was rapidly made in Photoshop, so nothing fancy...but I really wanted to create this :-) http://manuthommes.be/toolbox/binaryclock/ As usual, the code is not obfuscated so you can have a look and feel free to copy/edit.