Steganographic Methods
Laboratory Assignment: Steganographic Methods
Objective
- Understand the concept of steganography and its difference from cryptography.
- Learn to implement basic steganographic techniques using Python.
- Gain hands-on experience with LSB (Least Significant Bit) image steganography.
- Explore text-based steganography methods.
- Understand the challenges and limitations of steganographic techniques.
Tools Required
- Python 3.x
- Python libraries: Pillow (PIL), numpy
- Text editor or IDE
- Sample image files (PNG format recommended)
Part 1: Introduction to Steganography
Task 1: Understanding Steganography
- Read the theory: Steganographic methods for information protection
- Understand the difference between steganography and cryptography
- Learn about different types of steganography (image, audio, text)
Part 2: LSB Image Steganography
Task 2: Installing Required Libraries
Install the necessary Python libraries:
1pip install pillow numpy
Task 3: Basic LSB Encoding
Create a Python script to hide text in an image using LSB technique:
1from PIL import Image
2import numpy as np
3
4def text_to_binary(text):
5 """Convert text to binary string"""
6 binary = ''.join(format(ord(char), '08b') for char in text)
7 return binary
8
9def binary_to_text(binary):
10 """Convert binary string to text"""
11 text = ''
12 for i in range(0, len(binary), 8):
13 byte = binary[i:i+8]
14 if len(byte) == 8:
15 text += chr(int(byte, 2))
16 return text
17
18def hide_text_in_image(image_path, text, output_path):
19 """Hide text in image using LSB technique"""
20 # Open image
21 img = Image.open(image_path)
22 pixels = np.array(img)
23
24 # Convert text to binary
25 binary_text = text_to_binary(text)
26 binary_text += '1111111111111110' # Delimiter
27
28 # Flatten pixel array
29 flat_pixels = pixels.flatten()
30
31 # Check if image is large enough
32 if len(binary_text) > len(flat_pixels):
33 raise ValueError("Text too long for this image")
34
35 # Hide text in LSB
36 for i in range(len(binary_text)):
37 # Modify LSB of pixel value
38 flat_pixels[i] = (flat_pixels[i] & ~1) | int(binary_text[i])
39
40 # Reshape and save
41 modified_pixels = flat_pixels.reshape(pixels.shape)
42 result_img = Image.fromarray(modified_pixels.astype(np.uint8))
43 result_img.save(output_path)
44 print(f"Text hidden successfully in {output_path}")
45
46def extract_text_from_image(image_path):
47 """Extract hidden text from image"""
48 # Open image
49 img = Image.open(image_path)
50 pixels = np.array(img)
51
52 # Flatten pixel array
53 flat_pixels = pixels.flatten()
54
55 # Extract LSB
56 binary_text = ''
57 for pixel in flat_pixels:
58 binary_text += str(pixel & 1)
59 # Check for delimiter
60 if binary_text[-16:] == '1111111111111110':
61 break
62
63 # Remove delimiter and convert to text
64 binary_text = binary_text[:-16]
65 return binary_to_text(binary_text)
66
67# Example usage
68if __name__ == "__main__":
69 # Hide text
70 secret_message = "Hello, this is a secret message!"
71 hide_text_in_image("sample_image.png", secret_message, "stego_image.png")
72
73 # Extract text
74 extracted = extract_text_from_image("stego_image.png")
75 print(f"Extracted message: {extracted}")
Task 4: Testing LSB Steganography
- Create or download a sample PNG image (name it
sample_image.png) - Run the script to hide a secret message
- Verify that the extracted message matches the original
- Compare the original and stego images visually
Task 5: Advanced LSB with RGB Channels
Enhance the LSB technique to use all RGB channels:
1def hide_text_rgb(image_path, text, output_path):
2 """Hide text using all RGB channels"""
3 img = Image.open(image_path)
4 pixels = np.array(img)
5 height, width, channels = pixels.shape
6
7 binary_text = text_to_binary(text)
8 binary_text += '1111111111111110' # Delimiter
9
10 # Calculate required pixels
11 required_pixels = len(binary_text) // 3 + (1 if len(binary_text) % 3 else 0)
12
13 if required_pixels > height * width:
14 raise ValueError("Text too long for this image")
15
16 # Hide text in RGB channels
17 index = 0
18 for i in range(height):
19 for j in range(width):
20 for c in range(channels):
21 if index < len(binary_text):
22 pixels[i, j, c] = (pixels[i, j, c] & ~1) | int(binary_text[index])
23 index += 1
24 else:
25 break
26 if index >= len(binary_text):
27 break
28 if index >= len(binary_text):
29 break
30
31 result_img = Image.fromarray(pixels.astype(np.uint8))
32 result_img.save(output_path)
33 print(f"Text hidden in RGB channels: {output_path}")
34
35def extract_text_rgb(image_path):
36 """Extract text from RGB channels"""
37 img = Image.open(image_path)
38 pixels = np.array(img)
39 height, width, channels = pixels.shape
40
41 binary_text = ''
42 for i in range(height):
43 for j in range(width):
44 for c in range(channels):
45 binary_text += str(pixels[i, j, c] & 1)
46 if binary_text[-16:] == '1111111111111110':
47 break
48 if binary_text[-16:] == '1111111111111110':
49 break
50 if binary_text[-16:] == '1111111111111110':
51 break
52
53 binary_text = binary_text[:-16]
54 return binary_to_text(binary_text)
Part 3: Text-Based Steganography
Task 6: Whitespace Steganography
Create a script to hide text using whitespace manipulation:
1def hide_text_whitespace(text, output_file):
2 """Hide text using whitespace (spaces and tabs)"""
3 binary_text = text_to_binary(text)
4
5 # Use space for '0' and tab for '1'
6 whitespace_text = ''
7 for bit in binary_text:
8 if bit == '0':
9 whitespace_text += ' '
10 else:
11 whitespace_text += '\t'
12
13 # Add some visible text to make it look normal
14 cover_text = "This is a normal looking text file.\n" + whitespace_text + "\nEnd of file."
15
16 with open(output_file, 'w') as f:
17 f.write(cover_text)
18
19 print(f"Text hidden in whitespace: {output_file}")
20
21def extract_text_whitespace(input_file):
22 """Extract text from whitespace"""
23 with open(input_file, 'r') as f:
24 content = f.read()
25
26 # Extract whitespace between lines
27 lines = content.split('\n')
28 if len(lines) >= 3:
29 whitespace_line = lines[1]
30
31 binary_text = ''
32 for char in whitespace_line:
33 if char == ' ':
34 binary_text += '0'
35 elif char == '\t':
36 binary_text += '1'
37
38 return binary_to_text(binary_text)
39
40 return ""
41
42# Example usage
43hide_text_whitespace("Secret message here!", "whitespace_secret.txt")
44extracted = extract_text_whitespace("whitespace_secret.txt")
45print(f"Extracted: {extracted}")
Task 7: Word Length Steganography
Implement word-length based text steganography:
1def hide_text_word_length(text, cover_text, output_file):
2 """Hide text using word lengths (even=0, odd=1)"""
3 binary_text = text_to_binary(text)
4
5 words = cover_text.split()
6 if len(words) < len(binary_text):
7 raise ValueError("Cover text too short for message")
8
9 # Modify word lengths
10 modified_words = []
11 for i, word in enumerate(words):
12 if i < len(binary_text):
13 if binary_text[i] == '0':
14 # Ensure even length
15 if len(word) % 2 == 1:
16 word += 's' # Add 's' to make even
17 else:
18 # Ensure odd length
19 if len(word) % 2 == 0:
20 word += 'x' # Add 'x' to make odd
21 modified_words.append(word)
22
23 result_text = ' '.join(modified_words)
24
25 with open(output_file, 'w') as f:
26 f.write(result_text)
27
28 print(f"Text hidden using word lengths: {output_file}")
29
30def extract_text_word_length(input_file):
31 """Extract text from word lengths"""
32 with open(input_file, 'r') as f:
33 text = f.read()
34
35 words = text.split()
36 binary_text = ''
37
38 for word in words:
39 if len(word) % 2 == 0:
40 binary_text += '0'
41 else:
42 binary_text += '1'
43 # Look for delimiter pattern (8 consecutive zeros)
44 if binary_text[-8:] == '00000000':
45 break
46
47 # Remove delimiter and convert
48 binary_text = binary_text[:-8]
49 return binary_to_text(binary_text)
Part 4: Steganalysis and Detection
Task 8: Basic Steganalysis
Create a simple steganalysis tool to detect LSB steganography:
1import statistics
2
3def analyze_lsb_distribution(image_path):
4 """Analyze LSB distribution to detect hidden data"""
5 img = Image.open(image_path)
6 pixels = np.array(img)
7 flat_pixels = pixels.flatten()
8
9 # Extract LSBs
10 lsb_values = [pixel & 1 for pixel in flat_pixels]
11
12 # Calculate statistics
13 zeros = lsb_values.count(0)
14 ones = lsb_values.count(1)
15 total = len(lsb_values)
16
17 print(f"LSB Analysis for {image_path}:")
18 print(f"Total pixels: {total}")
19 print(f"Zeros: {zeros} ({zeros/total*100:.2f}%)")
20 print(f"Ones: {ones} ({ones/total*100:.2f}%)")
21
22 # Simple detection: if distribution is too close to 50-50, it might be suspicious
23 ratio = min(zeros, ones) / max(zeros, ones)
24 if ratio > 0.9:
25 print("⚠️ Suspicious: LSB distribution is very balanced (possible steganography)")
26 elif ratio > 0.7:
27 print("⚠️ Moderately suspicious: LSB distribution is quite balanced")
28 else:
29 print("✓ Normal: LSB distribution shows natural variation")
30
31def analyze_image_quality(original_path, stego_path):
32 """Compare original and stego images"""
33 original = Image.open(original_path)
34 stego = Image.open(stego_path)
35
36 # Convert to same mode if needed
37 if original.mode != stego.mode:
38 stego = stego.convert(original.mode)
39
40 # Calculate MSE (Mean Squared Error)
41 orig_pixels = np.array(original)
42 stego_pixels = np.array(stego)
43
44 mse = np.mean((orig_pixels - stego_pixels) ** 2)
45 print(f"Mean Squared Error: {mse}")
46
47 if mse < 1:
48 print("✓ Very low visual distortion")
49 elif mse < 10:
50 print("✓ Low visual distortion")
51 else:
52 print("⚠️ Significant visual distortion detected")
Part 5: Advanced Techniques
Task 9: Error-Correction and Encryption
Enhance steganography with error correction and encryption:
1import hashlib
2from cryptography.fernet import Fernet
3
4def generate_key(password):
5 """Generate encryption key from password"""
6 return hashlib.sha256(password.encode()).digest()
7
8def encrypt_message(message, password):
9 """Encrypt message using password"""
10 key = generate_key(password)
11 fernet = Fernet(Fernet.generate_key())
12 return fernet.encrypt(message.encode())
13
14def decrypt_message(encrypted_message, password):
15 """Decrypt message using password"""
16 key = generate_key(password)
17 fernet = Fernet(key)
18 return fernet.decrypt(encrypted_message).decode()
19
20def hide_encrypted_text(image_path, text, password, output_path):
21 """Hide encrypted text in image"""
22 # Encrypt the message
23 encrypted = encrypt_message(text, password)
24
25 # Convert to binary and hide
26 binary_encrypted = ''.join(format(byte, '08b') for byte in encrypted)
27
28 # Use the LSB function to hide encrypted data
29 img = Image.open(image_path)
30 pixels = np.array(img)
31 flat_pixels = pixels.flatten()
32
33 if len(binary_encrypted) + 16 > len(flat_pixels): # +16 for delimiter
34 raise ValueError("Encrypted text too long for this image")
35
36 binary_encrypted += '1111111111111110' # Delimiter
37
38 for i in range(len(binary_encrypted)):
39 flat_pixels[i] = (flat_pixels[i] & ~1) | int(binary_encrypted[i])
40
41 modified_pixels = flat_pixels.reshape(pixels.shape)
42 result_img = Image.fromarray(modified_pixels.astype(np.uint8))
43 result_img.save(output_path)
44 print(f"Encrypted text hidden: {output_path}")
Part 6: Practical Exercises
Task 10: Complete Steganography System
Create a comprehensive steganography tool with command-line interface:
1import argparse
2import os
3
4def main():
5 parser = argparse.ArgumentParser(description='Steganography Tool')
6 parser.add_argument('action', choices=['hide', 'extract', 'analyze'],
7 help='Action to perform')
8 parser.add_argument('-i', '--input', required=True,
9 help='Input image file')
10 parser.add_argument('-o', '--output',
11 help='Output file')
12 parser.add_argument('-t', '--text',
13 help='Text to hide')
14 parser.add_argument('-f', '--file',
15 help='File containing text to hide')
16 parser.add_argument('-p', '--password',
17 help='Password for encryption')
18 parser.add_argument('-m', '--method', choices=['lsb', 'rgb', 'text'],
19 default='lsb', help='Steganography method')
20
21 args = parser.parse_args()
22
23 try:
24 if args.action == 'hide':
25 if not args.output:
26 print("Error: Output file required for hiding")
27 return
28
29 # Get text to hide
30 if args.file:
31 with open(args.file, 'r') as f:
32 text = f.read()
33 elif args.text:
34 text = args.text
35 else:
36 print("Error: Text or file required for hiding")
37 return
38
39 # Hide text
40 if args.method == 'lsb':
41 hide_text_in_image(args.input, text, args.output)
42 elif args.method == 'rgb':
43 hide_text_rgb(args.input, text, args.output)
44 elif args.method == 'text':
45 hide_text_whitespace(text, args.output)
46
47 print(f"Text hidden successfully in {args.output}")
48
49 elif args.action == 'extract':
50 # Extract text
51 if args.method == 'lsb':
52 text = extract_text_from_image(args.input)
53 elif args.method == 'rgb':
54 text = extract_text_rgb(args.input)
55 elif args.method == 'text':
56 text = extract_text_whitespace(args.input)
57
58 if args.output:
59 with open(args.output, 'w') as f:
60 f.write(text)
61 print(f"Text extracted to {args.output}")
62 else:
63 print(f"Extracted text: {text}")
64
65 elif args.action == 'analyze':
66 analyze_lsb_distribution(args.input)
67
68 except Exception as e:
69 print(f"Error: {e}")
70
71if __name__ == "__main__":
72 main()
Part 7: Testing and Validation
Task 11: Create Test Suite
Create a comprehensive test to validate your steganography implementation:
1import unittest
2import os
3import tempfile
4
5class TestSteganography(unittest.TestCase):
6
7 def setUp(self):
8 """Create temporary files for testing"""
9 self.temp_dir = tempfile.mkdtemp()
10 self.test_image = os.path.join(self.temp_dir, "test.png")
11 self.stego_image = os.path.join(self.temp_dir, "stego.png")
12 self.test_text = os.path.join(self.temp_dir, "secret.txt")
13
14 # Create a simple test image
15 test_img = Image.new('RGB', (100, 100), color='red')
16 test_img.save(self.test_image)
17
18 # Create test text
19 with open(self.test_text, 'w') as f:
20 f.write("This is a secret test message!")
21
22 def tearDown(self):
23 """Clean up temporary files"""
24 import shutil
25 shutil.rmtree(self.temp_dir)
26
27 def test_text_to_binary_conversion(self):
28 """Test text to binary conversion"""
29 text = "ABC"
30 binary = text_to_binary(text)
31 self.assertEqual(binary, "010000010100001001000011")
32
33 # Test reverse conversion
34 converted_back = binary_to_text(binary)
35 self.assertEqual(converted_back, text)
36
37 def test_lsb_hide_extract(self):
38 """Test LSB hiding and extraction"""
39 secret_message = "Hello, Steganography!"
40
41 # Hide message
42 hide_text_in_image(self.test_image, secret_message, self.stego_image)
43
44 # Extract message
45 extracted = extract_text_from_image(self.stego_image)
46
47 self.assertEqual(extracted, secret_message)
48
49 def test_rgb_hide_extract(self):
50 """Test RGB channel hiding and extraction"""
51 secret_message = "RGB steganography test"
52
53 # Hide message
54 hide_text_rgb(self.test_image, secret_message, self.stego_image)
55
56 # Extract message
57 extracted = extract_text_rgb(self.stego_image)
58
59 self.assertEqual(extracted, secret_message)
60
61 def test_whitespace_hide_extract(self):
62 """Test whitespace steganography"""
63 secret_message = "Whitespace test"
64
65 # Hide message
66 output_file = os.path.join(self.temp_dir, "whitespace.txt")
67 hide_text_whitespace(secret_message, output_file)
68
69 # Extract message
70 extracted = extract_text_whitespace(output_file)
71
72 self.assertEqual(extracted, secret_message)
73
74# Run tests
75if __name__ == '__main__':
76 unittest.main()
Part 8: Real-World Applications
Task 12: Create a Digital Watermarking System
Implement a simple digital watermarking system:
1def create_watermark(original_image, watermark_text, output_image):
2 """Create a simple digital watermark"""
3 # Hide watermark in multiple locations for robustness
4 img = Image.open(original_image)
5 pixels = np.array(img)
6
7 # Convert watermark to binary
8 binary_watermark = text_to_binary(watermark_text)
9
10 # Embed watermark in multiple locations (corners and center)
11 locations = [
12 (0, 0), # Top-left
13 (pixels.shape[1]-50, 0), # Top-right
14 (0, pixels.shape[0]-50), # Bottom-left
15 (pixels.shape[1]//2, pixels.shape[0]//2) # Center
16 ]
17
18 for start_x, start_y in locations:
19 index = 0
20 for y in range(start_y, min(start_y + 50, pixels.shape[0])):
21 for x in range(start_x, min(start_x + 50, pixels.shape[1])):
22 if index < len(binary_watermark):
23 for c in range(3): # RGB channels
24 if index < len(binary_watermark):
25 pixels[y, x, c] = (pixels[y, x, c] & ~1) | int(binary_watermark[index])
26 index += 1
27
28 result_img = Image.fromarray(pixels.astype(np.uint8))
29 result_img.save(output_image)
30 print(f"Watermark embedded: {output_image}")
31
32def verify_watermark(watermarked_image, original_watermark):
33 """Verify if watermark exists in image"""
34 img = Image.open(watermarked_image)
35 pixels = np.array(img)
36
37 # Try to extract watermark from different locations
38 locations = [
39 (0, 0), # Top-left
40 (pixels.shape[1]-50, 0), # Top-right
41 (0, pixels.shape[0]-50), # Bottom-left
42 (pixels.shape[1]//2, pixels.shape[0]//2) # Center
43 ]
44
45 binary_watermark = text_to_binary(original_watermark)
46
47 for start_x, start_y in locations:
48 extracted_binary = ''
49 index = 0
50 for y in range(start_y, min(start_y + 50, pixels.shape[0])):
51 for x in range(start_x, min(start_x + 50, pixels.shape[1])):
52 if index < len(binary_watermark):
53 for c in range(3): # RGB channels
54 if index < len(binary_watermark):
55 extracted_binary += str(pixels[y, x, c] & 1)
56 index += 1
57
58 # Check if extracted matches original
59 if extracted_binary == binary_watermark:
60 return True, f"Watermark found at location ({start_x}, {start_y})"
61
62 return False, "Watermark not found"
Submission Guidelines
Submit a report that includes:
- Detailed explanation of each steganographic technique implemented
- Screenshots of original and modified images
- Code snippets for each implementation
- Analysis of capacity vs. detectability trade-offs
- Discussion of ethical considerations and legitimate use cases
- Reflection on the challenges of steganalysis and detection
Include all Python scripts and test results
Demonstrate successful hiding and extraction of messages using different techniques
Discuss the robustness of each method against various attacks (compression, filtering, etc.)
Additional Challenges (Optional)
- Audio Steganography: Implement LSB steganography for WAV files
- Adaptive Steganography: Create a system that adapts embedding based on image characteristics
- Multi-layer Security: Combine steganography with strong encryption
- Steganalysis Tools: Develop more sophisticated detection algorithms
- Performance Analysis: Compare different techniques for capacity, speed, and detectability