Harvard CS50P Final Project

LSB-StegoCrypt: A Technical Case Study

A detailed analysis of a Python privacy utility combining Least Significant Bit steganography, Fernet (AES-128) symmetric encryption, and zlib compression.

Project Overview

Understanding the design motivations and problem statements behind this implementation.

Core Purpose & Goal

Steganography conceals the existence of a message, while cryptography conceals its meaning. LSB-StegoCrypt integrates both paradigms into a unified command-line pipeline. This ensures that even if an attacker detects and extracts the hidden pixel payload, the data remains mathematically unreadable without the correct cryptographic key.

The Security Challenge

Standard LSB embedding is highly vulnerable to statistical detection (steganalysis) if the raw plaintext is embedded directly. Plaintext has high frequency patterns that stand out. By applying AES encryption first, we randomize the payload. Compressing the ciphertext then shrinks the footprint, reducing the number of modified pixels and maximizing stealth.

Pixel-Level Stealth

Interactive proof of imperceptibility. Drag the slider to compare the original cover image and the output stego-image containing the encrypted payload.

Original Cover Image Stego Image Output
Original Cover Image
Stego Image Output

Zoom Examiner (8x magnification)

Notice how color value modifications are kept at a maximum of +/- 1 unit out of 256. At this level of precision, the changes match natural sensor noise, making visual detection impossible.

Step-by-Step Processing Flow

The sequence of operations used to embed and retrieve hidden text data.

01

Input Processing

The program accepts a raw secret text message and an authentication password from the user.

02

Fernet Encryption

Hashes the password with SHA-256 to derive a 256-bit key, encrypting the plaintext via AES-128 in CBC mode with HMAC-SHA256.

03

zlib Compression

Applies zlib compression to the ciphertext, minimizing the bitstream length to optimize pixel capacity.

04

LSB Pixel Injection

Injects the binary bits of the compressed data into the least significant bits of the image's RGB channels, appending a 16-bit stopper marker.

05

Extraction & Decryption

Scans pixel bytes, extracts the 0th bit of each color channel, halts at the 16-bit marker, decompresses, and decrypts the payload.

Security Layer Breakdown

The multi-layered protective envelope enclosing the hidden data.

Fernet (AES-128) Cryptography

Fernet guarantees that any message encrypted with it cannot be read or modified without the key. It uses AES in CBC mode with a 128-bit key for encryption, and HMAC-SHA256 for integrity verification.

Key Derivation Function

Instead of using plain text passwords as keys, the script processes the password using the SHA-256 hashing algorithm to derive a standard 32-byte url-safe base64 encoded key suitable for Fernet.

zlib Data Compression

Encrypted text is highly entropic and cannot be compressed. The script compresses the payload before bit-shifting. This reduces the number of altered pixels, which directly reduces statistical vulnerability.

LSB Steganography

By modifying only the least significant bit (bit 0) of the RGB channels, the pixel values change by at most 1 unit. This minor change is visually indistinguishable and preserves image structure.

System Data Pipelines

Visualizing the data flows for the forward encoding (embedding) and reverse decoding (extraction) workflows.

Plaintext Input
Fernet Encrypt
zlib Compress
Binary Bitstream
LSB Embedding
Stego PNG Output

Encoding Flow: The plaintext is encrypted and compressed, converted to binary, appended with a 16-bit marker, and injected into image pixels.

Technology Stack & Core Modules

The languages, libraries, and frameworks utilized to build this portfolio project.

Python 3

The primary language used to build the command-line utility, handling file system interactions and CLI arguments.

Pillow (PIL)

The standard Python imaging library used to parse, manipulate RGB values, and output lossless PNG images.

Cryptography

A robust recipe library powering the secure Fernet symmetric encryption and authentication validation.

zlib Library

A built-in Python module utilized for deflate-based data compression to minimize pixel payload footprint.

Flask Server

Used as a lightweight micro-framework to build and serve this technical portfolio case study.

Codebase Blueprint

Review the files that structure the LSB-StegoCrypt project.

Workspace
LSB.py py
Contains core logic: main CLI handler, cryptographic setup, LSB bit-shifting, and output saving.

Engineering Challenges & Solutions

Technical hurdles encountered during design and how they were overcome.

Constraint

Image Capacity Limits

Encrypted and compressed payloads can exceed the storage capacity of small images, causing out-of-bounds errors during bit injection.

Resolution

Calculates available bytes (Width * Height * 3 channels) and asserts that payload size is smaller than this value before processing, throwing a clean error if exceeded.

Constraint

End-of-Stream Detection

During decoding, the system needs to know exactly when to stop reading bits, otherwise it will extract noise bytes from unmodified pixels.

Resolution

Appends a 16-bit trailing delimiter ('0000000000000000') to the bitstream during encoding. The decoder scans pixels until this sequence is matched, discarding trailing noise.

Constraint

Password Verification

Decrypting with a wrong password causes the cryptography library to raise an exception, which crashes execution.

Resolution

Wraps decompression and decryption in a try-except structure. If an exception occurs, it intercepts the error and returns a clean 'Wrong Password' message to the user.

Constraint

Lossy Format Vulnerabilities

Formats like JPEG compress pixel values lossily, which destroys the embedded least significant bits during saving.

Resolution

Enforces saving strictly in lossless formats (PNG) and validates output extensions. The application automatically appends '.png' if it is missing.

Technical Highlights

The design metrics behind the CS50P Final Project.

0-bit

AES Encryption

Military-grade ciphertext wrapper.

0-bit

End Marker

Unique '0000000000000000' stopper.

0%

Python Powered

Written cleanly with standard library tools.

0P

CS50 Project

Developed for Harvard's programming course.

Lessons Learned & Core Outcomes

Key concepts mastered during the execution of this CS50P project.

Cryptographic Protocols

Developed a practical understanding of symmetric encryption, key derivation functions, and ciphertext integrity protection.

Binary Data Operations

Gained experience with binary operations, bit-shifting techniques, byte conversions, and delimiter embedding algorithms.

Digital Image Anatomy

Learned about pixel layouts, RGB color channels, metadata preservation, and the mathematical properties of lossless formats.

Defensive Testing

Wrote comprehensive pytest automated test cases verifying edge cases, boundary checks, capacity errors, and validation logic.

Frequently Asked Questions

Everything you need to know about this implementation.

No. The least significant bit (LSB) changes RGB pixel channel values by at most 1 unit (e.g. from 255 to 254). To the human eye, this change in color intensity is completely invisible.

The program tries to decompress and decrypt the extracted bytes. A wrong password will result in a decryption mismatch or a zlib decompress error, which the code catches and reports as 'Wrong Password'.

Fernet uses AES-128 in CBC mode with HMAC-SHA256 authentication. It is a standard, cryptographically secure recipe. Even if an attacker extracts the raw bits from the image, they cannot read the message without the password.

Encryption increases entropy and size. Compression reduces the size of the ciphertext, minimizing the number of pixel modifications and maximizing the hidden storage capacity of a given cover image.

Open Source Code Access

The complete source code, test suites, and instructions are freely available in the public repository.

View Repository on GitHub