Introduction
Our goal is to build a simple application that is able to send and receive encrypted messages embedded inside images. The user is able to choose the image he wants and the program must tell if this image will suit the text or not. No pixel deformation or size distortion is allowed. TIF images may suffer slight size increments or decrements, but we will get to that later. The user can set a different password for every message he sends, which will enable the manager to transmit the same image to two groups, but with two different passwords and two different messages.
Using the code
Below, I've put the main procedure that will embed the text in the image. The code is commented for every step:
If MsgBox("You are about to encrypt and embed this message :[[
" + TextBox1.Text + " ]] , Shall We Proceed?", MsgBoxStyle.OkCancel,
"Stealth") = MsgBoxResult.Cancel Then Exit Sub
Label2.Text = ""
If RadioButton1.Checked = False And RadioButton2.Checked = False Then
MsgBox("Please select an extention")
Exit Sub
End If
Dim text As String
text = TextBox1.Text.ToString + " "
Dim passw As String = "" input("Enter Password To Protect Your Text", passw)
If passw = "" Then
MsgBox("Error ! You Didn,t Choose Any Password To Protect The Message.")
Exit Sub
ElseIf passw.Length < 4 Then
MsgBox("Error ! Password Is Too Short.")
Exit Sub
End If
Dim amount As Double = text.Length
process_pass_with_tx(passw, text)
TextBox1.Text = text
If RadioButton1.Checked = True Then GoTo mode2
between tow modes here
If Me.OpenFileDialog1.ShowDialog = DialogResult.Cancel Then
Exit Sub
Else
Dim infoReader As System.IO.FileInfo
infoReader = My.Computer.FileSystem.GetFileInfo(
Me.OpenFileDialog1.FileName)
If infoReader.Extension.Substring(1, 3).ToLower <> (
RadioButton2.Text.ToLower) Then
MsgBox("Error : Encryption mode requires different extension ")
Exit Sub
End If
LoadNewPict(Me.OpenFileDialog1.FileName)
If PictureBox1.Image.Size.Width < 64 Or PictureBox1.Image.Size.Height <
64 Then
MsgBox("Error! Tiny Images Cann,t Be Used In Steganography.")
Exit Sub
End If
Dim w1 As Integer = PictureBox1.Image.Size.Width - 1
Dim w2 As Integer = PictureBox1.Image.Size.Height - 1
Dim possible_size As Double = ((w1 - 7) * (w2 - 7)) / 8
If possible_size < amount Then
MsgBox("This image is not big enough to carry your message",
MsgBoxStyle.Critical)
Exit Sub
End If
End If
Dim dib, val, reslt As Integer
dib = FreeImage.FreeImage_Load(FreeImage.FREE_IMAGE_FORMAT.FIF_TIFF,
OpenFileDialog1.FileName, 0)
On Error GoTo rongex1
Dim ba As bits_array
ba.Initialize()
Dim sz As Long
Dim sz1, sz2, sz3, sz4 As Byte
Dim t As Double sz = text.Length
sz1 = Decimal.Remainder(sz, 100)
t = sz / 100
sz = Decimal.Truncate(t)
sz2 = Decimal.Remainder(sz, 100)
t = sz / 100
sz = Decimal.Truncate(t)
sz3 = Decimal.Remainder(sz, 100)
t = sz / 100
sz = Decimal.Truncate(t)
sz4 = Decimal.Remainder(sz, 100)
Dim i, j, m
i = 0
j = 1
ba.Initialize()
byte_to_bits(sz1, ba)
For m = 0 To 7 i = i + 1
FreeImage.FreeImage_GetPixelColor(dib, i, j, val)
If odd(val) = True Then
If ba.bits(m) = False Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, (val + 1))
End If
End If
If odd(val) = False Then
If ba.bits(m) = True Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, (val + 1))
End If
End If
Next m
ba.Initialize()
byte_to_bits(sz2, ba)
For m = 0 To 7 i = i + 1
FreeImage.FreeImage_GetPixelColor(dib, i, j, val)
If odd(val) = True Then
If ba.bits(m) = False Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, val + 1)
End If
End If
If odd(val) = False Then
If ba.bits(m) = True Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, val + 1)
End If
End If
Next m
ba.Initialize()
byte_to_bits(sz3, ba)
For m = 0 To 7 i = i + 1
FreeImage.FreeImage_GetPixelColor(dib, i, j, val)
If odd(val) = True Then
If ba.bits(m) = False Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, val + 1)
End If
End If
If odd(val) = False Then
If ba.bits(m) = True Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, val + 1)
End If
End If
Next m
ba.Initialize()
byte_to_bits(sz4, ba)
For m = 0 To 7 i = i + 1
FreeImage.FreeImage_GetPixelColor(dib, i, j, val)
If odd(val) = True Then
If ba.bits(m) = False Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, val + 1)
End If
End If
If odd(val) = False Then
If ba.bits(m) = True Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, val + 1)
End If
End If
Next m
Dim n As Byte
Dim count As Long
count = 0
For j = 2 To PictureBox1.Image.Size.Height Step 1
i = 1
Do While (i + 7) < PictureBox1.Image.Size.Width
n = Asc(CChar(text.Substring(count, 1)))
count = count + 1
If count = text.Length Then GoTo out
ba.Initialize()
byte_to_bits(n, ba) For m = 0 To 7
FreeImage.FreeImage_GetPixelColor(dib, i, j, val)
If odd(val) = True Then
If ba.bits(m) = False Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, val + 1)
End If
End If
If odd(val) = False Then
If ba.bits(m) = True Then
FreeImage.FreeImage_SetPixelColor(dib, i, j, val + 1)
End If
End If
i = i + 1
Next m
i = i + 8
Loop
Next j
out:
Dim fn As String
Dim con As Integer = 0
fn = OpenFileDialog1.FileName + "_stealth.tif"
up1:
If File.Exists(fn) = False Then
FreeImage.FreeImage_Save(FreeImage.FREE_IMAGE_FORMAT.FIF_TIFF,
dib, fn, &H800S)
Else
con = con + 1
MsgBox("FILE: " + fn + " ALREADY EXISTS ,WE WILL CHANGE THE NAME TO " +
OpenFileDialog1.FileName + con.ToString + "_stealth.tif")
fn = OpenFileDialog1.FileName + con.ToString + "_stealth.tif"
GoTo up1
End If
Label2.Text = fn
Exit Sub
mode2:
way, just change the lib calls according to the extension
Points of interest
You must put freeimage.dll in the same path as the EXE to make it work. This has already been done for you. The output image will be named the same as the input image plus "_stealth" if the output file already exists; the program will handle that. The textbox control has a maximum amount of characters to hold; if your text is bigger than that, try to separate it and use one image for each part. Lossy compression algorithms will damage encrypted data inside the image, so it is not recommended to compress the output images before transmission.
You may notice some size change for some images. This change can probably be ignored when compared to total file size. This probably happened because the compression algorithm that generated the native image that you supplied to the program is different from the compression algorithm in my DLL lib, which generated the output image, and not because the embedded data increased the size. The proof is this: before you start the program, write a mini program and use the same function that I used in my code to resave your image under a different name with the same extension. Let's say this new image is called beta. Now start the program and use it to embed some text. See the output image? It is exactly the same size as the image beta.
You can use any encryption algorithm you trust, i.e. Public-Private key, ElGamal... Our way of Stega is independent from the bit sampling (bits per pixel) of images, but it is strongly recommended that it be used with 24 bpp images or higher. This will guarantee the prevention of pixel deformation. Most of the web images are sampled 24 bpp.
Note that my key generation was weak, as I just repeated the key. If any character in the key were discovered, then all message characters encrypted by it would also be discovered. You should use good functions to generate a big string to make the xor or maybe use a better cipher. You can also use this program for authentication by using a background image somewhere inside your application to set a copyright text.
Low order bits (this program) don't work with JPEG because they use the Lossy compression algorithm. So, working with JPEG requires extra work. You can take the red/green/blue channel of the image and do the same. You can use this to take the channel whose histogram is approximated to a horizontal line and then divide the new image to squares like this:
1 1 1
1 x 1
1 1 1
In the central pixel, x, put a value that is bigger than all neighbors if we got bit 1. Put a value that is smaller than all neighbors if we got bit 0. Now the possible size to hold data here is divided by 9. This way may sometimes cause slight distortion because it works on high-order bits. We can only use the squares whose values are bigger than the proper threshold. Alternatively, divide the image into squares like this:
X Y
Y X
If X's pixels hold the same value, then the square represents 1 and Y's pixels must be changed to something different. If Y's pixels hold the same value, then the square represents 0 and X's pixels must be changed to something different. Now when we receive, if X's pixels and Y's pixels are different or both are the same, then someone was playing with this image and the bit is invalid. We can apply this on more than one channel, so if one is corrupted we will use the backup. Last 2 algorithms: Alaa Jebran © 2007.
Don't be surprised to know that some big agencies like the CIA, NSA, etc. use celebrities' web sites or pornographic web sites to send and receive encrypted messages using similar technologies.
History
- 14 May, 2007 -- Original version posted
- 25 June, 2007 -- Article edited and moved to the main CodeProject.com article base