[Tip] How To Protect Image File
Hello,
If you have ever wanted to protect your image file, especially within your Flash content,
this article will be helpful.
First of all, there are some points you need to protect when you deliver image data with Flash.
1. Internet Browser Cache.
Even though you use 'HTTPS' protocol when you deliver your file,
if it is pass the 'Network (or Protocol) Layer' of your machine, it is already in 'plain' data
(not encrypted), and can be read by Internet Browser.
Those 'plain' data will be stored in Internet Browser's Cache directory.
There are many Internet Browser Cache reader programs, and they can intercept those
data before the Internet Browser program deletes the cache files.
So, just using 'HTTPS' is not an answer. You need to encode your image data
with your own (or with well known cypher algorithms) encoding/decoding methods.
In this article, I want to explain more detail about this. I scramble the image
so that make it hard to figure out what the original image was, not only with human eyes
but also with 'strong enough' 'Key' length.
2. Screen Capture.
Even if you encoded your image data, it will be finally shown on your screen,
without any noises, or any other ugly filters.
So, you can 'Capture' your monitor screen itself.
There can be many ways to avoid this. First, you can erase the system's 'Clip board'.
You just can erase it regularly, or you can detect whether the Internet Browser -which is
showing your image data- is active or not and erase clip board when it is active (in foreground),
and hide your Flash area when the browser is inactive (in background).
If a hacker just take a picture of monitor screen with high quality Camera,
you still can avoid that, by showing some black rectangle, or any layers on the screen
regularly only in very short period (like mili-second). But I think this way sometimes you feel
tired or be annoyed when you watch that kind of blinking screen.
== About my code ==
What I want to present here today is 'modifying(encoding) the image' data itself.
There are many ways you can try for modifying your image data and revert it back to original data.
You can use legacy block cipher algorithms like DES, AES, for encoding and decoding the
image data, because the image data (RGB values) what Flash uses is actually a combination of 'bit's.
But if you decode the 'encoded image' data, you probably use 'get pixel'/'set pixel' function for
reconstructing the proper 'RGB' value of each pixel. But I am sorry that this function is extremely slow
so that you need to wait for a couple of minutes to see an image.
So, what I want to show you today is 'Scrambling the image' like 'picture puzzle',
with using 'copyPixels' function which is 'extremely fast'.
(Actually, there can be many other solutions for this, so please don't hesitate to proceed your own research)
Let's say you have 8*8 pixel size of image blocks, and you completely disorder those blocks for
entire image area. That size of block is too small to figure out where it should be, with our
humble human being's naked eyes. So, you need to know the 'answer', which is saying what image block
was originally on which 'cell'. This 'answer' is the 'key' for my simple 'image scramble' logic.
The 'key' I said here, you should understand that, is the 'key' value of normal 'block cipher' algorithms.
So, if you know the key, you can rearrange all those 'blocks' back, but if you don't know the key,
simply you can't do that, or you need to wates tremendous time for that.
Here, I use 16*16 size block for your eye's convenience, and with 8*8 size of 'matrix' which is
comprised of each 16*16 size blocks. I mean, one 8*8 matrix has 64 blocks, so each block of
one 'matrix' can be at '1st cell of matrix or 64th cell of matrix. So, the 'odds' is 'Factorial of 64',
which is 64! == 1.268869322e+89 .
Let's say you know my algorithm (complete source code), and you try every different 'matrix' values.
If it takes 0.001 seconds to test one 'matrix' value you have chosen, it will take
"(64!) / (1000*60*60*24*365) = 4.0235582250724303685766549129617e+78" Years!!!! to find exact matrix value.
== FLA sample ==
In my sample *.fla file, I embedded all those 'key' values. So, you can not use my sample directly for your
own project. But I think this sample will give you an useful idea for your own implementation.
<* Exchanging the key values *>
For example, you need to get the 'key' values from the server, only when it is needed.
You can use 'loadVariables()' function for this purpose. But, all data delivered by 'loadVariables()' or
'getUrl()', or any other functions of Flash, are 'Cached'.... So, you need to exchange that 'key' values
more safer way.
One of that 'safer key exchange' is to use 'token'. For example, when the client requests the key value
to the server, the client should generate an random value (I will call it 'token') and send it to the server.
Now the server modifies the 'key' value 'with the token', and sends back to client. Finally, the client
decode the 'key' value with the token value it generated.
As you can see, my logic is just 'rearranging each blocks' with respond to the given matrix values (key).
It is really really simple. There in the ActionScript code, you can see 3 matrix, and some other variables.
var chessunit = 16 // Size of block
var arrayw = 8 // Width of Matrix
var arrayh = 8 // Height of Matrix
var arraysize = arrayh * arrayw
var chessarray = Array (arraysize) // Matrix for decoding
chessarray = Array (64,37,5,35,45,25,52,38,12,61,34,23,3,26,39,58,32,28,17,2,60,16,59,19,57,48,43,18,47,21,40,15,31,6,44,46,30,51,27,56,20,24,13,7,42,1,49,54,36,53,9,50,4,8,10,14,33,11,22,63,55,29,41,62)
// In case of the width of an image is not exactly the multiple of the width of matrix,
// we need to encode the right most area of the image.
// So, I simply use another matrix for only that area.
//
// Maybe You can generate more bigger image, of which the width of it is exactly the multiple of
// the width of matrix. And you let the decoder to show only the original size of the image.
// Please try yourself. :)
var dwArray = Array (32,19,3,18,23,13,26,6,31,17,12,2,20,29,16,14,9,1,30,8,10,24,22,11,15,28,7,4,21,25,27,5)
// In case of the Height of an image is not exactly the multiple of the Height of matrix,
// we need to encode the bottom most area of the image.
// So, I simply use another matrix for only that area.
//
// Maybe You can generate more bigger image, of which the Height of it is exactly the multiple of
// the Height of matrix. And you let the decoder to show only the original size of the image.
// Please try yourself. :)
var dhArray = Array (40,23,4,22,29,16,33,24,8,38,21,15,2,17,37,20,18,11,1,10,12,36,30,27,13,25,28,14,19,32,35,7,5,26,3,31,34,6,9,39)
You can change the block size, matrix size, any values you want.
By the way, if the block size becomes smaller, you'll get more stronger security level. But you have to
compensate it with the slow down of the performance.
** Ooops, I can not attach my sample *.fla file, but only ActionScript code here...
But I believe you can try without testing my *.fla sample.
If you really want to test it, please mail me. (brucewang.korea@gmail.com)
** You need to 'export' image object, with the name of "img0".
== Image encoder ==
Encoding (Scramble) the original image is also simple. You just need to copy each blocks in reverse of
the 'ActionScript code'. That's all. You just use same 'matrix', but copy source and target reverse of
ActionScript code. I think you can write it with any languages you like, C/C++, C#, Java, and
anything.... right?
== Block cipher ==
Now, you know that my implementation is kind of 'intuitive' representation of 'block cipher'.
More precisely, it is called 'ECB', which encodes each encoding block (matrix) with the key values
from the beginning to the end. You can twist the matrix every time you pass one matrix area of an image.
It is called 'chaining'. There are some 'chaining' block cipher methods like 'CBC'...
(If you want to know more, please look up "http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation ")
Thank you for your time to read my humble article,
and wish you Good luck. :)
Attach Code
import flash.geom.Matrix;
import flash.display.BitmapData;
//*
var chessunit = 16
var arrayw = 8
var arrayh = 8
var arraysize = arrayh * arrayw
var chessarray = Array (arraysize)
chessarray = Array (64,37,5,35,45,25,52,38,12,61,34,23,3,26,39,58,32,28,17,2,60,16,59,19,57,48,43,18,47,21,40,15,31,6,44,46,30,51,27,56,20,24,13,7,42,1,49,54,36,53,9,50,4,8,10,14,33,11,22,63,55,29,41,62)
var dwArray = Array (32,19,3,18,23,13,26,6,31,17,12,2,20,29,16,14,9,1,30,8,10,24,22,11,15,28,7,4,21,25,27,5)
var dhArray = Array (40,23,4,22,29,16,33,24,8,38,21,15,2,17,37,20,18,11,1,10,12,36,30,27,13,25,28,14,19,32,35,7,5,26,3,31,34,6,9,39)
//*/
var now:BitmapData = BitmapData.loadBitmap("img0");//linkageId);
var tempbmp:BitmapData = new BitmapData(now.width,now.height);
var mc:MovieClip = this.createEmptyMovieClip("mc", this.getNextHighestDepth());
var wunit = Math.floor(now.width / (chessunit*arrayw));
var hunit = Math.floor(now.height / (chessunit*arrayh));
var xoff=0;
var yoff=0;
var dw = now.width-(wunit*(chessunit*arrayw));
var dh = now.height-(hunit*(chessunit*arrayh));
for(hc=0; hc<hunit; hc++)
{
xoff = 0;
yoff = int(hc*chessunit*arrayh);
for(wc=0; wc<wunit; wc++)
{
xoff = int(wc*chessunit*arrayw);
/*
// mix up the puzzle matrix again.
puzzmixfactor = 8;//chessarray[puzz]
for( puzz=0; puzz<arraysize; puzz++ )
{
chessarray[puzz] += puzzmixfactor;
if( chessarray[puzz] > arraysize )
{
chessarray[puzz] -= arraysize;
}
}
*/
// rearrange the each chess units
// by looking up the scramble matrix.
for(y=0;y<arrayh;y++)
{
for(x=0;x<arrayw;x++)
{
arrayindex= int(x+(y*arrayw));
xpos_d = int(xoff + int( (chessarray[arrayindex]-1) % arrayw ) * chessunit);
ypos_d = int(yoff + int( (chessarray[arrayindex]-1) / arrayw ) * chessunit);
xpos_s = int(xoff + x * chessunit);
ypos_s = int(yoff + y * chessunit);
tempbmp.copyPixels(
now,
new flash.geom.Rectangle(xpos_s, ypos_s, chessunit, chessunit),
new flash.geom.Point(xpos_d, ypos_d)
);
}
}
}
}
//*/
///*
if(dh>0)
{
tempbmp.copyPixels(
now,
new flash.geom.Rectangle(0, hunit*(chessunit*arrayh), now.width, dh),
new flash.geom.Point(0, hunit*(chessunit*arrayh))
);
if(dh>chessunit)
{
dh = int(dh/chessunit);
// 2007-03-22 brucewang
//dhArray = new int[dh*arrayw];
//fillScrambleMatrix(dhArray, dh*arrayw);
/// 2007-03-22 brucewang
///
/// Scramble the image
rect_dest.Width = chessunit;
rect_dest.Height = chessunit;
//for(int hc=0; hc<hunit; hc++)
//{
xoff = 0;
yoff = hunit*(chessunit*arrayh);
for(wc=0; wc<wunit; wc++)
{
xoff = wc*(chessunit*arrayw);
// rearrange the each chess units
// by looking up the scramble matrix.
for(y=0;y<dh;y++)
{
for(x=0;x<arrayw;x++)
{
arrayindex=x+(y*arrayw);
xpos_d = int(xoff + int( (dhArray[arrayindex]-1) % arrayw ) * chessunit);
ypos_d = int(yoff + int( (dhArray[arrayindex]-1) / arrayw ) * chessunit);
xpos_s = int(xoff + x * chessunit);
ypos_s = int(yoff + y * chessunit);
tempbmp.copyPixels(
now,
new flash.geom.Rectangle(xpos_s, ypos_s, chessunit, chessunit),
new flash.geom.Point(xpos_d, ypos_d)
);
}
}
}
//}
}
}
if(dw>0)
{
tempbmp.copyPixels(
now,
new flash.geom.Rectangle(wunit*(chessunit*arrayw), 0, dw, now.height),
new flash.geom.Point(wunit*(chessunit*arrayw), 0)
);
if(dw>chessunit)
{
dw = int(dw/chessunit);
// 2007-03-22 brucewang
//dwArray = new int[dw*arrayh];
//fillScrambleMatrix(dwArray, dw*arrayh);
/// 2007-03-22 brucewang
///
/// Scramble the image
rect_dest.Width = chessunit;
rect_dest.Height = chessunit;
xoff = int(wunit*chessunit*arrayw);
for(hc=0; hc<hunit; hc++)
{
yoff = int(hc*chessunit*arrayh);
//for(int wc=0; wc<wunit; wc++)
//{
//xoff = wc*(chessunit*arrayw);
// rearrange the each chess units
// by looking up the scramble matrix.
for(y=0;y<arrayh;y++)
{
for(x=0;x<dw;x++)
{
arrayindex=x+(y*dw);
xpos_d = int(xoff + int( (dwArray[arrayindex]-1) % dw ) * chessunit);
ypos_d = int(yoff + int( (dwArray[arrayindex]-1) / dw ) * chessunit);
xpos_s = int(xoff + x * chessunit);
ypos_s = int(yoff + y * chessunit);
tempbmp.copyPixels(
now,
new flash.geom.Rectangle(xpos_s, ypos_s, chessunit, chessunit),
new flash.geom.Point(xpos_d, ypos_d)
);
}
}
//}
}
}
}
///////
mc.attachBitmap(tempbmp, this.getNextHighestDepth());
mc._quality = "BEST";
stop();
Edited: 04/03/2007 at 07:16:25 PM by SunghyunWang (bruce)