Version 1 of Steganography

Updated 2003-08-04 14:35:13

if 0 {Richard Suchenwirth 2003-08-02 - Steganography is a cryptographic technique, where data is hidden inside other data. A simple example is a color image, where every pixel has r(ed)-g(reen)-b(lue) values each between 0 and 255. If the least significant bit of each color value is taken as part of the data to hide, the visual impression of the image will hardly change. 3 pixels together offer 9 bits of "hiding room", which is enough for characters from Unicode pages 0 and 1. A small 100x100 image thus can hide 10000/3 = 3333 characters, which is quite a lengthy "subtext".

First lets read the "subtext" from a given photo image - the l.s. bit is easily obtained by modulo 2. You can choose whether you want to "read" all pixels, which takes long, or only until the first NUL byte:}

 proc stega_get {image {all 0}} {
    set w [image width $image]
    set h [image height $image]
    set n 0; set int 0; set res ""
    for {set i 0} {$i<$h} {incr i} {
        for {set j 0} {$j<$w} {incr j} {
            foreach {r g b} [$image get $j $i] break
            set int [expr {$int*8 + $r%2*4 + $g%2*2 + $b%2}]
            if {[incr n]==3} {
                if {!$all && $int==0} {return $res}
                if {$int<32} {set int 32} ;# blank out control chars
                append res [format %c $int]
                set n 0; set int 0
            }
        }
    }
    set res
 }

if 0 {On an untreated test image, this returns quite some gibberish. But now let's put a subtext into such an image. I restrict the character range to page 0 of the Unicode, because binary scan is so convenient, but works on bytes. Masking out the least-significant bit is done by bit-wise ANDing with the pattern 0xFE (i.e. all but the lsb set).}

 proc stega_put {image text} {
    set w [image width $image]
    set h [image height $image]
    set i 0; set j 0
    foreach char [split $text\x0 ""] {
        binary scan $char B* bits
        set bits [split 0$bits ""] ;# prepend 0 to make it 9
        foreach triplet [list [lrange $bits 0 2] \
              [lrange $bits 3 5] [lrange $bits 6 8]] {
           foreach {r g b} [$image get $j $i] {R G B} $triplet break
           set r [expr {($r & 0xFE) + $R}]
           set g [expr {($g & 0xFE) + $G}]
           set b [expr {($b & 0xFE) + $B}]
           set color [format #%02x%02x%02x $r $g $b]
           $image put $color -to $j $i
           if {[incr j]>=$w} {
               if {[incr i]>=$h} return ;# truncate on image full
               set j 0
           }
       }
    }
 }

if 0 {For transferring such an image "with a message", save it to a file. However, some file formats optimize away the least significant bit, losing the message. Writing in GIF format conserves the lsb, but may bail out because of "too many colors" (it appears to have a limited color table, and in worst case steganography makes eight times as many colors). Also, transparent pixels suddenly turn to black (or almost black), revealing that something is fishy with the image. JPEG drops the lsb. I found TIFF and PPM to work reliably, albeit uncompressed, so the file may get 6 times bigger than the original JPEG. }


Category Cryptography | Arts and crafts of Tcl-Tk programming