Can images be encrypted? Well, of course — what kind of question is that? Any file –any bitstream— can be encrypted. But encrypting a file (image or otherwise) turns it into random-looking garbage. Therefore, in a sense, an encrypted image file would cease to be an image file at all (e.g. you wouldn’t be able to open it in Photoshop or Gimp, transcode it to a different format, or display it directly in a web browser). There may be image format extensions that allow encryption, but I never heard of them, and many applications wouldn’t support them.
But what about encrypting the image data itself, the pixels, rather than the file as a whole? That can certainly be done, but using a standard encryption algorithm would only make sense with an uncompressed format that stores raw data (like BMP), because otherwise the compression algorithm would likely perform poorly on the encrypted data, and in the case of lossy compression, it would mess it up. Also, depending on the cipher, the encrypted data (ciphertext) might be longer than the original (due to padding), and that would mess up the image or require a change in dimensions.
Since standard encryption algorithms cannot be used, alternative, graphical methods must be used instead. A quick google search for “image encryption” summons lots of results with (apparently) advanced stuff, but here I will discuss a very simple method I invented to “encrypt” images that has a few advantages. I write “encrypt”, in quotes, because this simple method is not cryptographically secure at all, but it may be used to prevent image hotlinking, or to make it hard for someone to steal (especially mass-steal) images from your server or app, etc. In fact, I came up with this technique while working on a game, to protect my artwork, because a few images from other apps of mine had been stolen, and I was a little paranoid back then.
Perhaps the best term to describe this method is scrambling rather than encryption. The idea is very simple: slice the image into small squares and shuffle them, making it look like an unsolved picture puzzle.
The shuffling has to look random, but it must be deterministic and repeatable in order to be easily reversible. This can be achieved with any PRNG (pseudo-random number generator), like the stock ones provided in most platforms (e.g Math.random()
in JavaScript). The seed can be thought of as the “encryption” key: each seed number will yield a different block arrangement, and “decryption” is performed by reversing the shuffle process using the same seed.
There’s a problem, though: using stock random()
functions will not work across platforms! Each language/platform may use a different algorithm –or different algorithm settings– and this may even happen within a single “platform”, as is the case with JavaScript in browsers (each one has a different Math.random()
implementation).
The solution for this is simple: we just write our own PRNG! Since this method is not meant to be cryptographically secure, we can use a simple PRNG like an LCG (linear congruntial generator), which is really easy to code (it just involves a state variable, three carefully chosen constants, one multiplication, one addition, one modulus and one division). My Javascript version is:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var LCG = function(seed) { this.state = seed; }; (function() { var m = 4294967296, a = 1664525, c = 1013904223; LCG.prototype.rand = function() { this.state = (this.state * a + c) % m; return this.state / m; } })(); |
One last thing to consider is block size: how small should the squares be? It is clear that, the smaller they are, the harder it will be to reconstruct the image without the key, and that is visually intuitive: if we just slice an image into four quadrants, it’s trivial to figure out their order in one glance, as the image is still perfectly clear. If we split each quadrant into another four quadrants, and so on, it quickly becomes much harder to guess what it is you are looking at. The extreme case would be to use 1×1 blocks, effectively shuffling individual pixels into a big block of colourful noise.
As we shrink the blocks, however, two problems may arise. One is that the computational cost increases, but this may not be much of an issue, since it’ll probably be fast enough anyway. The real problem is image compression: the more the image looks like noise, the worse all algorithms will perform. This is because large solid color areas, repeating patterns or smooth transitions (which are features that different algorithms target or expect) will be destroyed.
I planned to use JPEG, and in that specific case, scrambling not only causes poor compression but may also introduce undesirable artifacts or noise in the image, particularly when low quality settings are used. Luckily, knowing a little about the internals of the JPEG algorithm allowed me to solve that problem: since JPEG slices the image in 8×8 squares (or 16×16, or 16×8 in some cases) and processes those independently, minimum interference is achieved by using 8 or 16 as the scrambler’s block size.
The following images illustrate all this. Consider this source image:
Its file size is 285.9 KB. Scrambling it with a block size of 4, we get this one:
which is pretty scrambled indeed (you can’t tell what the original image looked like). But this image has a file size of 566.8 KB. Twice as big! Here we observe the scrambling interefering with compression. On the other hand, if we use a block size of 8 to minimize interference, we get:
which is still pretty unintelligible, but has a file size of 286.2 KB. The size difference is now only just about 300 bytes!
You can try a demo, or check my JavaScript source code on GitHub. I adapted it from a previous ActionScript version I had written for my game.