Skip to content

SteCoStegKeyString

stegobox.codec.SteCoStegKeyString

Bases: BaseCodec

SteCoStegKeyString - This module implements SteCoStegKeyString in JPEG images.

  • To encode:

    1. Specify a JPEG image as a carrier.
    2. Specify a payload to encode.
    3. Retreive the output PNG image and the respective key.
  • To decode:

    1. Specify a PNG image as a carrier.
    2. Specify a key retrieve the original payload.
Source code in stegobox/codec/stecosteg_key_str.py
class SteCoStegKeyString(BaseCodec):
    """SteCoStegKeyString - This module implements SteCoStegKeyString in JPEG images.

    * To encode:
        1. Specify a JPEG image as a carrier.
        2. Specify a payload to encode.
        3. Retreive the output PNG image and the respective key.

    * To decode:
        1. Specify a PNG image as a carrier.
        2. Specify a key retrieve the original payload.
    """

    def __init__(self) -> None:
        self.key: str
        super().__init__()

    def encode(self, carrier: Image.Image, payload: str) -> Image.Image:
        image = carrier.convert("RGB")
        width, height = image.size
        byte_s = payload.encode("utf-8")
        hex_bin = byte_s.hex()
        colors: dict = {}
        maxlength = max(len(hex(width)) - 2, len(hex(height)) - 2)
        for x in range(width):
            for y in range(height):
                if image.getpixel((x, y)) in colors:
                    colors[image.getpixel((x, y))] += 1
                else:
                    colors[image.getpixel((x, y))] = 1
        colorslist = [(item[1], item) for item in colors.items()]
        colorslist.sort()
        colorssorted = [value[1] for value in colorslist]
        substitutioncolors: dict = {}
        if len(hex_bin) % 3 == 1:
            lenth_hex = hex(len(hex_bin))[2:].zfill(6)
            lenth_hex = "aa" + lenth_hex
        if len(hex_bin) % 3 == 2:
            lenth_hex = hex(len(hex_bin))[2:].zfill(6)
            lenth_hex = "b" + lenth_hex
        if len(hex_bin) % 3 == 0:
            lenth_hex = hex(len(hex_bin))[2:].zfill(6)
        secret_bin = lenth_hex + hex_bin  # type: ignore
        for i in range(int(len(secret_bin) / 3)):
            substitutioncolors[colorssorted[i][0]] = [
                (
                    int(secret_bin[i * 3], 16),
                    int(secret_bin[i * 3 + 1], 16),
                    int(secret_bin[i * 3 + 2], 16),
                ),
                i,
            ]
        key: list = []
        for i in range(len(substitutioncolors)):
            key.append((0, 0))
        for x in range(width):
            for y in range(height):
                if image.getpixel((x, y)) in substitutioncolors.keys():
                    key[substitutioncolors[image.getpixel((x, y))][1]] = (x, y)
                    image.putpixel(
                        (x, y), substitutioncolors[image.getpixel((x, y))][0]
                    )
        hexes = ""
        for coordinate in key:
            for value in coordinate:
                # might be buggy with small images < 16 pixels
                value = hex(value)[2:]
                if len(value) < maxlength:
                    for i in range(int(maxlength - len(value))):
                        value = "0" + value
                hexes += value
        self.key = hexes
        return image

    def decode(self, carrier: Image.Image) -> str:
        coordinates: list = []
        image = carrier.convert("RGB")
        width, height = image.size
        maxlength = max(len(hex(width)) - 2, len(hex(height)) - 2)
        for i in range(int(len(self.key) / maxlength / 2)):
            xx = ""
            yy = ""
            for j in range(maxlength):
                xx += self.key[i * maxlength * 2 + j]
                yy += self.key[i * maxlength * 2 + maxlength + j]
            coordinates.append((int(xx, 16), int(yy, 16)))
        str = ""
        for coordinate in coordinates:
            str += hex(image.getpixel(coordinate)[0])[2:]
            str += hex(image.getpixel(coordinate)[1])[2:]
            str += hex(image.getpixel(coordinate)[2])[2:]
        if str[0] == "b":
            return bytes.fromhex(str[7:]).decode("utf-8")
        elif str[0] == "a":
            return bytes.fromhex(str[8:]).decode("utf-8")
        else:
            return bytes.fromhex(str[6:]).decode("utf-8")

    def getkey(self) -> str:
        return self.key