Least Significant Bit in Steganography web application
In the realm of data security and privacy, there exists a fascinating technique called steganography. Derived from the Greek words “steganos” meaning “covered” or “hidden,” and “graphein” meaning “writing,” steganography is the art and science of concealing secret information within seemingly innocuous carriers such as images, audio files, or even text. It allows individuals to exchange hidden messages in plain sight, thereby ensuring confidentiality and anonymity. In this blog post, we delve into the world of steganography, exploring its principles, techniques, and real-world applications.
On the image above we can see that there are use to least significant bits. The advantage is we can encode a larger message but in the same time we are raising the probability of revealing it but not about that much. The more significant bit we change, bigger influence it has on the pixel color.
Least Significant Bit
Each pixel is made is 24 bits which makes up 3 bytes. Each byte is allocated to one of the pixel colors which is always R or G or B that makes up a color when combined as shown on the image below. Since each byte consists of eight bits and PNG file format use Big Endian, also called a Network endian which means that the most significant bit starts on the left side and less significants follow up to least significant on the right. If we change change the first, most significant bit we would affect the pixel color which would be pretty obvious when someone looks on the image, therefore we have to change the least significant one so no-one can see that with naked eye.
Utilizing the Least Significant Bit
To embed information without noticeable visual changes, the LSB technique targets the least significant bit of each color component. As the LSB has the smallest impact on the color value, modifying it allows for subtle changes that are difficult to detect by the human eye. By replacing the LSB of each color component with bits of the secret data, the image can be used to transmit concealed information.
Preserving Visual Integrity
By altering only the LSB, the overall visual integrity of the image remains intact. The changes introduced by the modified LSB are minimal and imperceptible to casual observers. However, specialized steganalysis techniques may be employed to detect the presence of hidden data, highlighting the importance of using strong encryption and robust steganographic methods.
Each character of the message in case of text, we have to convert to its binary character so it has same form as image and then each birth replaces the least significant bit in the image as we mentioned earlier. PNG images provide a suitable platform for applying LSB steganography due to their Big Endian byte order and the 24-bit composition of each pixel. Through careful manipulation of the least significant bit in the RGB color components, secret information can be hidden within the image while maintaining its visual appearance. Understanding the principles and limitations of LSB steganography is crucial for both concealing and extracting information, ensuring secure communication in various contexts.
from PIL import Image
import base64
class Key:
key = int
attr = 0
def Embedder(image_name, message):
if len(message) < 5:
raise ValueError("Message must be at least 3 characters.")
# Encode the message to base64 and convert to binary
encoded_message = base64.b64encode(message.encode("utf-8")).decode("utf-8")
message_bits = ''.join(format(ord(char), '08b') for char in encoded_message)
# Append a unique padding sequence to mark the end of the message
end_padding = "10000000" # 8-bit padding
message_bits += end_padding # Append padding to the message bits
Key.key = len(message_bits) # Track the total number of bits
print(f"[Embedder] Encoding '{message}' with bit length: {Key.key}")
# Open the image and check capacity
image = Image.open('images/' + image_name).convert('RGB')
if len(message_bits) > (image.size[0] * image.size[1] * 3):
raise ValueError("Message is larger than the image capacity!")
# Embed message bits into the image
image_pixels = list(image.getdata())
bitindex = 0
for i in range(len(message_bits) // 3):
pixel = list(image_pixels[i])
for j in range(3):
pixel[j] = (pixel[j] & 0xFE) | int(message_bits[bitindex])
bitindex += 1
image_pixels[i] = tuple(pixel)
modified_image = Image.new(image.mode, image.size)
modified_image.putdata(image_pixels)
Key.attr += 1
modified_image.save("stegged/steg" + str(Key.attr) + image_name)
return str(Key.key), "steg" + str(Key.attr) + image_name
def Extractor(picture, key):
modified_image = Image.open("to_decode/" + picture)
extracted_bits = ""
target_bits = int(key)
key_index = 0
print(f"[Extractor] Using key (bit length): {target_bits}")
# Extract up to the target bits
for pixel in modified_image.getdata():
for channel in pixel:
if key_index < target_bits:
extracted_bits += str(channel & 0x01)
key_index += 1
else:
break
if key_index >= target_bits:
break
# Locate padding to determine end of the actual message
end_padding = "10000000"
padding_index = extracted_bits.find(end_padding)
if padding_index != -1:
extracted_bits = extracted_bits[:padding_index] # Remove padding from bits
print(f"[Extractor] Extracted bit length after removing padding: {len(extracted_bits)}")
# Convert bits to characters
encoded_message = ""
for i in range(0, len(extracted_bits), 8):
byte = extracted_bits[i:i+8]
encoded_message += chr(int(byte, 2))
try:
# Decode the base64 message
plainmessage = base64.b64decode(encoded_message).decode("utf-8")
return plainmessage
except Exception as e:
print(f"[Extractor Error] {e}")
return "Invalid key"