From hacky Python to performant Rust

How I turned a 24 hour Python project into Rust’s first stable steganography library

How it got started

The idea for this project came while I was at a hackathon in Montreal, Canada with Justin Leger and @icefalcn. Our plan was to hide sensitive files in unassuming ones like vacation photos and PDFs. While we were successful in hiding the files in an easy to use system, the implementation was well… hacky. Our solution was to encrypt a sensitive file and append it to the end of an image. Looking at the image in a viewer wouldn’t give away the encrypted file added to the end, but looking at the file size or cat ing the image definitely would. With another hackathon come and gone, I set out to implement it right.

Why Rust

I chose Rust because it had good library support for image manipulation, it worked seamlessly on both my Chromebook and Windows boxes, and most importantly, I wanted to gain expertise with the language.

Getting my grips

The library started simply as reading and writing buffers to the alpha channel of an image. It grew as I added utility functions converting a String to an array of bytes and reading or writing that. Then I added converting whole files to byte arrays. And I realized something: my library was starting to become about encoding things as byte arrays to read/write into the alpha channel. It should have been about adding actual steganographic methods.

The Big Refactor AKA: 1.0.0 (stable)

I factored the program into 3 modules: Encoder, Decoder and Util. I moved all the “added functionality” of converting things to byte arrays into the Util module. The core of the library, the actual steganography, now exists in Encoder and Decoder. With the functionality of the library nicely separated, it was very easy to add some new features! Alongside encoding bytes into the alpha channel of images, I also added encoding bytes as an image.

“This is a steganography demo!” is encoded into the alpha channel of the first 31 pixels

notepad.exe (Binary size: 237 KB, Image size: 194 KB)

mspaint.exe (Binary size: 6.35 MB, Image size: 3.16 MB)

If you look closely at the bottom of mspaint.exe, there’s some nice 🌈s.

Next steps

Short term, adding a command line interface is top priority.

It can be easy to spot fishiness when encoding bytes straight into the alpha channels. So I’d like to add some established steganographic methods like F5, JSteg and LSB.

The crate should be (and is!) easy to use. It’s how I was sidetracked into spending so much time at the beginning developing utility functions. I want to keep the focus on steganography, but I see a big benefit in adding some simple encryption functions to the Utility module.

Steganography isn’t limited to just images… The next big step will be adding methods to discreetly encode data in audio and video.

A performant binary which can run on many platforms that allows for advanced steganographic methods through a simple interface will have a profound impact on privacy. I imagine a DSLR camera that can hide pictures in other pictures on the fly, giving reporters more freedom to not fear reporting the truth.