Have you ever wondered how to hide your data away from prying eyes? Of course, there are many sophisticated techniques out there to accomplish the task, but there are some simple yet effective techniques too. In this write-up, I will introduce you to one such way of hiding data: Steganography.
What is Steganography?
Steganography is basically a technique used to high data in “plain sight”. Yes, that's right. Now, you might wonder, how can the data be hidden if it is present in plain sight? Well to do that, we use another file as a carrier of the hidden data. This carrier can be anything like a photo, audio, video, or any other file. In this tutorial, I will show you a simple steganography technique to hide plaintext messages in image files.
We can express any ASCII string as a binary string. The range of ASCII values is [0, 255]. So, we need at most 8 bits to convert an ASCII character to its binary representation. We then concatenate the binary representations of all the characters to form the final binary string.
Strategy: First of all we read the image as an array. Then after reshaping it to a 1-D array, we can use each element of this array to represent a bit as follows:
1. If the bit to be stored is one, we ensure that the element is odd:
- If the element is already odd, no change is made.
- If the element is even, we reduce its value by 1 and make it even. Also, if the value is 0, we increase it by 1 to ensure that the value remains in the range [0, 255].
2. If the bit to be stored is 0, we ensure that the element is even:
- If the element is already even, no change is made.
- If the element is odd, we reduce its value by 1 to make it even.
Clearly, in this way, we can store any binary string. Note that increasing/decreasing the color intensity value by 1 doesn’t affect the picture much In fact, it is virtually impossible to differentiate this doctored image from the real one by just looking at the image.
Problem with JPEG images
JPEG images use compression algorithms. This means that after saving the image, the color intensity values might be slightly different when we read the image next time. But, we need the exact values for our strategy to work. Therefore, we save the final image in the png format which preserves the exact color intensity values.
Now, that you understand the strategy, let's get our hands dirty.
Show me the Code!
First of all, we make the necessary imports. We use cv2 to read/save image files. And we use sys to accept command-line arguments from the user.
After that, we write a helper function that converts an ASCII string to a binary string.
We will also need a function to convert a binary string to an ASCII string during decryption.
Now, coming to the encryption method, first of all, we read the image as an array. Then the message string is converted to a binary string using the str_to_binary function defined above. Note that, we will also need to know the length of the binary string during decryption. So, to do that, we can reserve the first few bits (16 in this implementation) for storing the length of the binary string. As discussed earlier, we will use the elements of the image array to store the bits.
For decryption, first of all, we extract the length of the string using the first 16 elements and after that use the elements to get the binary string. This binary string is then converted to the ASCII message using the binary_to_str function defined above.
Finally, to make it usable as a command-line tool, we add the following:
The above approach is just a start. To get more out of the technique, we can first convert the plaintext into ciphertext and then use the above strategy. This will add one additional layer of security for the data.
You can view the combined source code here.
That’s all folks!