A detailed analysis of a Python privacy utility combining Least Significant Bit steganography, Fernet (AES-128) symmetric encryption, and zlib compression.
Understanding the design motivations and problem statements behind this implementation.
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.
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.
Interactive proof of imperceptibility. Drag the slider to compare the original cover image and the output stego-image containing the encrypted payload.
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.
The sequence of operations used to embed and retrieve hidden text data.
The program accepts a raw secret text message and an authentication password from the user.
Hashes the password with SHA-256 to derive a 256-bit key, encrypting the plaintext via AES-128 in CBC mode with HMAC-SHA256.
Applies zlib compression to the ciphertext, minimizing the bitstream length to optimize pixel capacity.
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.
Scans pixel bytes, extracts the 0th bit of each color channel, halts at the 16-bit marker, decompresses, and decrypts the payload.
The multi-layered protective envelope enclosing the hidden data.
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.
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.
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.
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.
Visualizing the data flows for the forward encoding (embedding) and reverse decoding (extraction) workflows.
Encoding Flow: The plaintext is encrypted and compressed, converted to binary, appended with a 16-bit marker, and injected into image pixels.
The languages, libraries, and frameworks utilized to build this portfolio project.
The primary language used to build the command-line utility, handling file system interactions and CLI arguments.
The standard Python imaging library used to parse, manipulate RGB values, and output lossless PNG images.
A robust recipe library powering the secure Fernet symmetric encryption and authentication validation.
A built-in Python module utilized for deflate-based data compression to minimize pixel payload footprint.
Used as a lightweight micro-framework to build and serve this technical portfolio case study.
Review the files that structure the LSB-StegoCrypt project.
Technical hurdles encountered during design and how they were overcome.
Encrypted and compressed payloads can exceed the storage capacity of small images, causing out-of-bounds errors during bit injection.
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.
During decoding, the system needs to know exactly when to stop reading bits, otherwise it will extract noise bytes from unmodified pixels.
Appends a 16-bit trailing delimiter ('0000000000000000') to the bitstream during encoding. The decoder scans pixels until this sequence is matched, discarding trailing noise.
Decrypting with a wrong password causes the cryptography library to raise an exception, which crashes execution.
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.
Formats like JPEG compress pixel values lossily, which destroys the embedded least significant bits during saving.
Enforces saving strictly in lossless formats (PNG) and validates output extensions. The application automatically appends '.png' if it is missing.
The design metrics behind the CS50P Final Project.
Military-grade ciphertext wrapper.
Unique '0000000000000000' stopper.
Written cleanly with standard library tools.
Developed for Harvard's programming course.
Key concepts mastered during the execution of this CS50P project.
Developed a practical understanding of symmetric encryption, key derivation functions, and ciphertext integrity protection.
Gained experience with binary operations, bit-shifting techniques, byte conversions, and delimiter embedding algorithms.
Learned about pixel layouts, RGB color channels, metadata preservation, and the mathematical properties of lossless formats.
Wrote comprehensive pytest automated test cases verifying edge cases, boundary checks, capacity errors, and validation logic.
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.
The complete source code, test suites, and instructions are freely available in the public repository.
View Repository on GitHub