DEV Community

Cover image for Tripling Data Capacity: Implementing CIHLHF Coverless Steganography in Python πŸ•΅οΈβ€β™‚οΈπŸš€
Anjasfedo
Anjasfedo

Posted on

Tripling Data Capacity: Implementing CIHLHF Coverless Steganography in Python πŸ•΅οΈβ€β™‚οΈπŸš€

In my previous post, we explored CIHMSB, a way to hide data in images without changing a single pixel by using the average value of image fragments. While it offers perfect invisibility, the main challenge is hiding capacity.

Today, we are looking at the next evolution: CIHLHF. By targeting the "Extremes"β€”the lowest and highest pixel values in a fragmentβ€”we can pack three times more information into the same image while maintaining uncrackable security.

Academic Attribution πŸ“š
This article is based on the 2023 research paper:
"A High-Capacity Coverless Information Hiding Based on the Lowest and Highest Image Fragments" by Kurnia Anggriani, Shu-Fen Chiou, Nan-I Wu, and Min-Shiang Hwang.
Published in Electronics 2023, 12, 395.
DOI: 10.3390/electronics12020395

It also references the foundational CIHMSB method:
"A Novel Coverless Information Hiding Method Based on the Most Significant Bit of the Cover Image" by Lina Yang, Haiyu Deng, and Xiaocui Dang (IEEE Access, 2020).
DOI: 10.1109/ACCESS.2020.3000993


🧠 Why CIHLHF?

The original CIHMSB method maps bits to the Average Intensity of a fragment. CIHLHF introduces two major upgrades:

  1. Dual-Value Mapping: Instead of a single average, it extracts the Minimum (L) and Maximum (H) values from each fragment.
  2. Increased Payload: By extracting multiple MSB bits from both the L and H values, the total bit pool per image is significantly larger.
  3. The Z-Key (Secret Mapping): Both the sender and receiver use a shared secret key (Z) to randomize the fragment sequence, ensuring that even if an attacker intercepts the image, the data remains scrambled.

Like its predecessor, it remains Coverless, meaning the stego image is identical to the cover image.


πŸ’» The Implementation

This Python implementation handles the extraction of extreme values and the random Z-key permutation logic.

import numpy as np

class CIHLHF:
    def __init__(self, fragment_size=8, first_msb_bits=1, second_msb_bits=1, seed=None):
        self.fragment_size = fragment_size
        self.first_msb_bits = first_msb_bits
        self.second_msb_bits = second_msb_bits
        self.seed = seed

    def _text_to_bin(self, text):
        binary_list = []
        for char in text:
            bin_char = format(ord(char), '07b')
            for bit in bin_char:
                binary_list.append(int(bit))
        return binary_list

    def _bin_to_text(self, binary_list):
        text = ""
        for i in range(0, len(binary_list), 7):
            chunk = binary_list[i:i+7]
            if len(chunk) < 7: break
            chunk_str = "".join(map(str, chunk))
            text += chr(int(chunk_str, 2))
        return text

    def _extract_min_max_msbs(self, image_array):
        h, w = image_array.shape
        h_trunc = h - (h % self.fragment_size)
        w_trunc = w - (w % self.fragment_size)

        msb_pool = []
        for i in range(0, h_trunc, self.fragment_size):
            for j in range(0, w_trunc, self.fragment_size):
                fragment = image_array[i:i+self.fragment_size, j:j+self.fragment_size]
                min_val = int(np.min(fragment)) 
                max_val = int(np.max(fragment)) 

                bin_min = format(min_val, '08b')
                for b in range(self.first_msb_bits):
                    msb_pool.append(int(bin_min[b]))

                bin_max = format(max_val, '08b')
                for b in range(self.second_msb_bits):
                    msb_pool.append(int(bin_max[b]))
        return msb_pool

    def _generate_z_key(self, capacity):
        if self.seed is None: return list(range(1, capacity + 1))
        state = np.random.get_state()
        np.random.seed(self.seed)
        z_key = np.random.permutation(np.arange(1, capacity + 1))
        np.random.set_state(state)
        return z_key.tolist()

    def embed(self, image_array, secret_text):
        secret_bits = self._text_to_bin(secret_text)
        msb_pool = self._extract_min_max_msbs(image_array)
        if len(secret_bits) > len(msb_pool):
            raise ValueError(f"Capacity {len(msb_pool)} bits is not enough.")

        z_key = self._generate_z_key(len(msb_pool))
        mapping_flag = [0] * len(secret_bits)
        for i in range(len(secret_bits)):
            target_index = z_key[i] - 1
            mapping_flag[i] = 1 if secret_bits[i] == msb_pool[target_index] else 0
        return mapping_flag

    def extract(self, image_array, mapping_flag):
        msb_pool = self._extract_min_max_msbs(image_array)
        z_key = self._generate_z_key(len(msb_pool))
        secret_bits = []
        for i in range(len(mapping_flag)):
            target_index = z_key[i] - 1
            t_i = 1 if mapping_flag[i] == msb_pool[target_index] else 0
            secret_bits.append(t_i)
        return self._bin_to_text(secret_bits)

Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Replicating the Paper: Benchmark Results

To verify the effectiveness of CIHLHF, I ran a full replication suite based on the experiments conducted by Anggriani et al. Here are the results from the Python test suite:

[1] CIHLHF EXPERIMENT DEMO
Original Message: CIHLHF_REPLICATION_2026
Extraction Match: True

[2] HIDING CAPACITY ANALYSIS
Total Fragments   : 4096
CIHMSB Capacity   : 16384 bits
CIHLHF Capacity   : 49152 bits
Improvement Factor: 3.0x

[3] SECURITY ANALYSIS (Brute-Force Complexity)
Total Fragments (Ci) : 4096
Secret Bits (Ti)     : 49152
Complexity (Years)   : 2.69 x 10^209255

[4] IMAGE QUALITY ASSESSMENT (IQA)
MSE  : 0.0
PSNR : inf dB
SSIM : 1.0
Qi   : 1.0
Enter fullscreen mode Exit fullscreen mode

Analysis of Results:

  • 3.0x Capacity Leap: By utilizing 12 bits per fragment (6 from the minimum value and 6 from the maximum), the capacity jumps from 16,384 bits to a massive 49,152 bits for a 512 x 512 carrier image.
  • Optimal Visual Quality: Since no pixels are modified, the PSNR remains infinite and the SSIM is a perfect 1.0. The image is mathematically identical to the original.

  • Extreme Security: The brute-force complexity for a 512x512 image reaches an astronomical 10^209255 combinations, making it physically impossible to crack without the secret mapping key Z.


🎯 Conclusion

CIHLHF proves that we don't have to sacrifice capacity for security. By shifting the mapping logic from the fragment's average value to its extreme values, we can hide massive amounts of data in plain sightβ€”unseen by both the human eye and digital steganalysis tools.

Source code and replication scripts are available on my GitHub! ⭐

https://github.com/Anjasfedo/cihlhf

Top comments (0)