Cryptography
Python
NumPy
Linear Algebra
Image Processing
Hill Cipher ImageX
High-performance image encryption engine using Linear Algebra and NumPy for lossless, vectorized cryptographic processing.
1. The Challenge
- Context: While standard encryption works well for text, securing visual data (images) requires processing millions of data points (pixels) efficiently. This project, co-developed with Muhammad Mobeen, aimed to implement the Hill Cipher—a classical linear algebra algorithm—specifically for image cryptography.
- The Obstacle: The core mathematical challenge of the Hill Cipher is the Invertible Matrix Problem. To decrypt a message, the Key Matrix must have a modular multiplicative inverse. If a user provides a random key matrix that is singular (determinant is 0) or shares a factor with the modulus (256 for pixel values), the image becomes mathematically impossible to decrypt, resulting in permanent data loss.
2. The Solution Architecture
The application treats images not as visual data, but as large numerical matrices.
- Ingestion: The image is read and converted into NumPy arrays (R, G, B channels).
- Transformation: The 2D image arrays are flattened and reshaped into blocks corresponding to the Key Matrix size ($N \times N$).
- Encryption/Decryption: We apply matrix multiplication modulo 256.
- Encryption: $C = (K \times P) \pmod{256}$
- Decryption: $P = (K^{-1} \times C) \pmod{256}$
- Key Decisions:
- NumPy over Loops: An image might have 2 million pixels. Iterating through them with Python loops would take minutes. I utilized NumPy's vectorized operations (
np.dotandnp.matmul), reducing processing time from minutes to milliseconds.
- NumPy over Loops: An image might have 2 million pixels. Iterating through them with Python loops would take minutes. I utilized NumPy's vectorized operations (
3. Implementation Highlights
A. Vectorized Encryption Logic
Instead of looping through every pixel, we utilize Linear Algebra. This snippet shows how we encrypt a chunk of the image vector against the key matrix in one operation.
import numpy as np
def encrypt(image_vector, key_matrix):
# Perform matrix multiplication
# logic: (Key x Image_Block) % 256
encrypted_vector = np.dot(key_matrix, image_vector) % 256
# Return as integer type for image reconstruction
return encrypted_vector.astype(np.uint8)
B. Validating the Key Matrix (The Mathematical Guardrail)
This is the most critical piece of engineering. Before allowing encryption, we must prove the key is mathematically valid. We check if the determinant is coprime to 256 (the pixel depth).
from math import gcd
def is_key_valid(key_matrix, modulus=256):
# Calculate determinant using linalg
det = int(np.round(np.linalg.det(key_matrix)))
# 1. Determinant cannot be zero (singular matrix)
# 2. Determinant and Modulus must be coprime (gcd == 1)
if det != 0 and gcd(det, modulus) == 1:
return True
return False
4. Challenges & Overcoming Roadblocks
- The Trap: Floating Point Drift.
When calculating the inverse of a matrix using standard libraries like
scipyornumpy.linalg.inv, the result is returned as floating-point numbers (e.g.,3.999999). - The Fix: Cryptography relies on exact integers. The standard inverse doesn't work for Modular Arithmetic. I had to implement a custom Modular Multiplicative Inverse function. This calculates the specific integer $X$ such that $(Det \times X) \equiv 1 \pmod{256}$. This ensures the decrypted image is a bit-perfect copy of the original, with zero noise.
5. Results & Impact
- Performance: The tool can encrypt/decrypt a 4K resolution image in under 2 seconds, thanks to vectorized NumPy operations.
- Reliability: The added validation logic prevents users from using "bad keys," eliminating the risk of accidental data corruption.
- Collaboration: This robust implementation was a joint effort, co-authored with Muhammad Mubeen, combining mathematical theory with software optimization.