Encrypt data in your video using the least significant bit
Published in · 6 min read · May 18, 2020
--
Steganography is the practice of concealing a file, message, image, or video within another file, message, image, or video. It has existed for a long time, and nowadays, digital steganography is used to hide data inside images. We can hide all kinds of data by using different digital steganographic methods.
In this article, we are going to use a method known as Least Significant Bit (LSB) transformation to hide a text inside a video.
Above is a representation of the digit 149 as an 8-bit binary digit, with its rightmost bit highlighted. If we change it to 0, we will get 10010100 (decimal equivalent of 148). If we change any other bit in the binary number above, the change would be much greater. So in this example, the rightmost bit is the least significant — changing it results in the least change to the original number.
A digital image is a representation of pixel values, and every pixel value will have numbers containing information regarding the pixel. A digital color image will have red, green, and blue channels and eight bits to represent each channel, so every channel can take a value from 0-255, and this value represents the intensity of the pixel. (R, G, B)=(0,0,0)
is the representation of the color black and (255,255,255)
represents the color white.
Take an array of pixels as an example and suppose we want to hide the character A in it. Let’s see how it’s done:
(R, G, B)= (11101010 11101001 11001010),(10111001,11001011,11101000),(11001001 00100100 11101001)
This is a pixel array, and we want to hide A in it. The ASCII value of A is 65. If we convert it to binary, we get 01000001. So if we use LSB transformation, we can change the LSB of all the numbers in our array and get:
(R,G, B)= (11101010 11101001 11001010),(10111000,11001010,11101000),(11001000 00100101 11101001)
Now that we have hidden A in the pixel array, let’s use Python to hide a text inside an image.
This is the image that we are going to use. First, we will install the dependencies and then look at the code step by step.
1. Install the dependencies
We are going to use stegano library in Python. We can install it by running:
$pip install stegano
Now we need to import the library and the lsb
class from it:
from stegano import lsb
Now that we have imported the lsb
class, let’s use its hide()
method to hide our text inside the image:
secret=lsb.hide('./lenna.png','hello world')
This will transform the LSB of our image and hide our message in it. Now we have to save the image:
secret.save('./encoded_image.png')
That’s it. With just three lines of code, we have hidden the message.
Image comparison
As you can see, there is no visible difference between these two images.
To reveal the secret message, simply use the lsb.reveal()
method with the image as the argument and the message will be printed:
lsb.reveal('./encoded_image.png')Output:- hello world
We have used LSB transformation to hide data inside an image, but how can we use this to hide data inside a video?
A video is a collection of frames, and each frame is an image. So if we pull out all the frames from a video, we can use this method to store our data using LSB steganography and stitch those frames back into a video with the secret message.
The original source video is quite long, so I have just used an eight-second portion of it.
1. Extracting frames from a video
To extract frames from a video, we can use the most popular computer vision library, OpenCv.
First, install OpenCv and import it:
import cv2
We have to read the video. Go through the video frame by frame and save all the images into a new directory.
To load the video, we run:
vidcap = cv2.VideoCapture("video.mp4")
This loads the video into vidcap. Now we can use the read
method to read the frames from “video.mp4:"
success, image = vidcap.read()
We can define a loop to go through all the frames and save it with a unique filename that we can easily sort:
Once this is done, we are left with all the frames from the video.
But a video is not just a collection of images. There is audio as well. To extract that audio, we will use FFmpeg.
2. Extracting audio from a video
FFmpeg is a free and open-source command-line tool for transcoding multimedia files. It contains a set of shared audio and video libraries such as libavcodec, libavformat, and libavutil. With FFmpeg, you can extract audio files from a video, convert your PNG image files into video, and much more.
To install FFmpeg in Ubuntu, first update the package list:
$sudo apt update
Then run the command below to install FFmpeg:
$sudo apt install ffmpeg
To validate that FFmpeg is installed properly, run:
ffmpeg -version
If you get something like the message below, everything is OK:
ffmpeg version n4.1.4 Copyright (c) 2000-2019 the FFmpeg developers
To use FFmpeg in Python, we have to import call and STDOUT from the subprocess library:
from subprocess import call,STDOUT
And then we can run the code below in Python:
call(["ffmpeg", "-i","video.mp4" , "-q:a", "0", "-map", "a", "tmp/audio.mp3", "-y"],stdout=open(os.devnull, "w"), stderr=STDOUT)
This code will extract the audio from the given video file and save it as “audio.mp3” in the tmp folder. After we encode the frames with our text, we can then use this audio file to give our encoded video file the proper audio.
3. Encoding the text inside frames
Now that we have all the frames, we can divide the strings into small chunks and hide each chunk of the message inside a frame using the lsb.hide()
method:
split_string
is a helper function to split strings into small portions. As we do not need to hide all the text in the first frame itself, we divide the frames and hide it in many frames. The full code can be found on GitHub.
We have loaded a video, pulled it to as many frames as possible, and encoded it. Now we have to put together the frames together into a video.
4. Making video from frames
We can use FFmpeg to stitch together all our frames with a hidden message to form a video and then lay out the audio:
call(["ffmpeg", "-i", "tmp/frame%d.png" , "-vcodec", "png", "video.mov", "-y"],stdout=open(os.devnull, "w"), stderr=STDOUT)
Running the code above creates the video with our secret message hidden in it.
We can run the code below if we want sounds in our video:
call(["ffmpeg", "-i", "temp/video.mov", "-i", "temp/audio.mp3", "-codec", "copy","data/enc-" + str(file_name)+".mov", "-y"],stdout=open(os.devnull, "w"), stderr=STDOUT)
And we have done it. The output video is almost 900 MB. I have uploaded a small portion of the video to show that video quality is not affected by this method.
Now that we have done the hard part of encoding, let’s look at how a person can decode our video and read the message.
Decrypting the message from the video
We have to do the same steps as with encryption, but in the opposite order. So we extract all the frames from the video and extract the information from the LSB:
As you can see, with modern libraries and languages like Python, it is really simple to do steganography and cryptography.
The only problem with this method is that the new video is huge in size when compared to the original video. If we could reduce the size, it would be a good addition to this project.
The full code for this project can be found on GitHub.