At 11:24 -0600 03/14/2002, I wrote:
[re noise filter]
>This is a crude example, though. The early incarnations don't write
>to LSBs for the pixels and you can see the image has been altered in
>some way. I have a *much* sleeker version that will in fact write to
>LSBs -- it even does conversion of decimal to 32-bit binary and back
>again -- and if you want I can send along the file as it is now.
>It's slow and wonky -- could use all kinds of optimization
And actually I just realized how to optimize it. I don't need to do a
dec-to-binary conversion for the RGB data to write to the LSB using
iLingo. That represents a vast speed improvement.
In binary the last position in a number always corresponds to the
integer 1, and all digits to the *left* are doubles of the one
immediately preceding.
This means that if the final bit in a nonzero binary number is 1, the
decimal value of that number will *always* be odd. There can be no
exception. Similarly, if the last bit is set to 0, the decimal number
will *always* be even.
Well, the blue channel in an RGB color (at least in Director)
represents the least significant *channel* when handling its value.
Thus any time you have a color you can take its colorValue.blue and
end up with an 8-bit number that represents the LSB for the color.
This is because the colors are ( red * 256 * 256 ) + ( green * 256 )
+ blue for the decimal version of the RGB color; hence the blue
channel represents the one holding the least amount of information
that is represented in the RGB color. The blue channel will always be
an integer ranging from 0 to 255 inclusive. (They all are, but
remember there are multipliers for g and r!)
Why this is interesting is you can then use a modulus test to
determine if this integer is odd or even, and then modify it to
represent your binary stream.
OK, let's say you have a ten-char message in ASCII. You do a
charToNum on each one to get the integer value for each char. You
then convert that simple stream into eight-bit binary numbers, which
overall goes pretty damn fast. (Much faster, anyway, than taking a
300 by 200 image at 24+ bits and converting *that*.)
Once you have your bitstream, you go through it a char at a time and
twiddle the values for each blue channel in successive pixels in your
image. If your bit is 0, you set the image pixel's value to an even
number (from, for instance, 33 to 32). If your bit is 1, you set the
image pixel value to an odd number (32 --> 33, or even 32 --> 31; you
just need to look out for 0 and 255 and be sure you don't go beyond
them). If the value for the pixel is already where it should be --
and there's an averaged 50 percent chance that will happen for each
bit you are embedding -- you leave it alone.
Then you feed that data back into the image. You've just done LSB
modifying without having to convert the entire pixel value into
binary.
You end up diddling 80 pixels that way. All the rest you can ignore
or, if you want to allow variable length messages, you could write a
specific block of bits to indicate the end of the file, say eight 0s.
Heh, ASCII null, which is in fact an EOF marker.
Reconstructing the message would then be cake, because all you need
is the modulus of each pixel's blue value passed through 2. If the
blue value is even the modulus would be 0, and if the value is odd
the mod is 1. That's your binary bitstream. Keep going until you have
your eight 0s (your EOF marker). Take it in 8-bit chunks, convert it
to decimal and switch that back into text with numToChar.
What is also nice about this is it lets you salt your data randomly
in the image itself, as I discuss online in the NF section. It's a
little anal, of course, but it can help make it that much harder to
see if an image has been used to transmit text or anything else via
steg. (There are programs now that can detect commonly used stego
routines in images, most of which I assume begin writing at either
the first or last bit in an image and proceed linearly from there.)
And of course what you do to the original message to take it out of
plaintext to begin with is also up to you.
Some of this is getting off the topic of Chris's original question,
but suppose you take the LSBs of the first 32 pixels in the image and
write them out as a binary value using the method above. You would
*probably* have a random stream of 1s and 0s. Convert it to an
integer, a 32-bit value.
You could then use that to xOr the RGB values for the rest of the image:
colorVal = getPixel ( someMember.image, x, y, #integer )
newVal = bitXOr ( yourPixel, colorVal )
someMember.image.draw( point ( x, y ), newVal )
...the above is rather untested, mind you.
What you'd get would probably be visual garbage.
You then write *that single 32-bit number* to a pixel -- you'd have
to sacrifice the data in this one pixel to do this -- whose location
is determined by setting the randomSeed equal to (for instance) the
image h * its width. Something reproducible, because of course random
numbers in computing aren't if they're algorithmically derived. You
bang your 32-bit pixel into that position and write the file:
the randomSeed = someMember.image.width * someMember.image.height
nLocH = random ( someMember.image.width )
nLocV = random ( someMember.image.height )
someMember.image.draw( point ( nLocH, nLocV, yourPixel )
That would be your pseudorandom location for the xOr key pixel, and
every time the h or w of the image was different its location would
be different -- but the thing is that you can *recover* its location
in reading back the image by going through the same procedure, first
setting the randomSeed and then getting the H and V coords to find
your xOr pixel:
the randomSeed = someMember.image.width * someMember.image.height
nLocH = random ( someMember.image.width )
nLocV = random ( someMember.image.height )
yourPixel = getPixel ( someMember.image, x, y, #integer )
You then pass the image through the xOr filter a second time and
reconstruct the whole file, with the one exception of the pixel you
sacrificed to store the key.
And actually you could even determine beforehand which pixel was
going to get hosed, and write its value out to the LSBs of 32 other
pixels in the image. Then you xOr the rest as before, and go ahead
and write the key into the pixel as before. Reconstructing is just
the reverse of the process, but when you're done you go to those 32
"restore" pixels, read their LABs, and then feed that value back into
the "key" pixel, resetting it to its pristine state before you wrote
your key into it.
This means, of course, that 32 pixels in the image will not be
precisely the same as when you started, but the differences will be
visually indistinguishable. Literally -- the human eye can only
discern about 4 to 5 million shades, so this kind of variation is
nothing.
That gives you a file format that's totally valid -- BMP for instance
-- but which, opened in anything but your program, should show
nothing but garbage. (Though I wonder if it wouldn't just turn into
something that looks like you did an add pin color blend to it!)
--
Warren Ockrassa | http://www.nightwares.com/
Director help | Free files | Sample chapters | Freelance | Consulting
Author | Director 8.5 Shockwave Studio: A Beginner's Guide
Published by Osborne/McGraw-Hill
http://www.osborne.com/indexes/beginners_guides.shtml
[To remove yourself from this list, or to change to digest mode, go to
http://www.penworks.com/lingo-l.cgi To post messages to the list, email
[EMAIL PROTECTED] (Problems, email [EMAIL PROTECTED]). Lingo-L is for
learning and helping with programming Lingo. Thanks!]