Skip to content

Ezstego

stegobox.codec.Ezstego

Bases: BaseCodec

Source code in stegobox/codec/ezstego.py
class Ezstego(BaseCodec):
    def __init__(self) -> None:
        self.secret: list
        super().__init__()

    def encode(self, carrier: Image.Image, payload: str) -> list:
        im = carrier
        im = im.convert("P")
        palette = []
        im_palette = im.palette.palette
        for i in range(int(len(im_palette) / 3)):
            palette.append(
                (im_palette[3 * i], im_palette[3 * i + 1], im_palette[3 * i + 2])
            )
        y = []
        for i in range(len(palette)):
            y.append(
                0.299 * palette[i][0] + 0.587 * palette[i][1] + 0.114 * palette[i][2]
            )
        y_index = np.argsort(y)
        y_index_inverse = [0] * 256
        for i in range(len(y_index)):
            y_index_inverse[y_index[i]] = i
        palette_data = im.getpalette()
        if palette_data is None:
            raise ValueError("Image has no palette")
        byte_s = payload.encode("utf-8")
        # Convert payload to binary (removes leading '0b') contains string length
        hex_bin = byte_s.hex()
        oct_bin = oct(int(hex_bin, 16))
        binary_bin = bin(int(oct_bin, 8))[2:].zfill(len(hex_bin) * 4)
        lenth_bin = bin(len(binary_bin))[2:].zfill(12)
        binary_bin = lenth_bin + binary_bin
        for i in range(len(binary_bin)):
            _y_index = y_index_inverse[palette_data[i]]
            lower_bit = _y_index % 2
            if lower_bit == int(binary_bin[i]):
                continue
            elif (lower_bit, binary_bin[i]) == (0, "1"):
                palette_data[i] = y_index[_y_index + 1]
            elif (lower_bit, binary_bin[i]) == (1, "0"):
                palette_data[i] = y_index[_y_index - 1]
        self.secret = palette_data
        return palette_data

    def decode(self, carrier: Image.Image) -> str:
        im = carrier
        im = im.convert("P")
        palette = []
        im_palette = im.palette.palette
        for i in range(int(len(im_palette) / 3)):
            palette.append(
                (im_palette[3 * i], im_palette[3 * i + 1], im_palette[3 * i + 2])
            )
        y = []
        for i in range(len(palette)):
            y.append(
                0.299 * palette[i][0] + 0.587 * palette[i][1] + 0.114 * palette[i][2]
            )
        y_index = np.argsort(y)
        y_index_inverse = [0] * 256
        for i in range(len(y_index)):
            y_index_inverse[y_index[i]] = i
        palette_data = self.secret
        info = ""
        data_lenth = 14
        for i in range(len(palette_data)):
            _y_index = y_index_inverse[palette_data[i]]
            lower_bit = _y_index % 2
            if i >= data_lenth:
                break
            if lower_bit == 1:
                info += "1"
            elif lower_bit == 0:
                info += "0"
            if i == 13:
                data_lenth = int(info[:12], 2) + 12
        return bytes.fromhex(hex(int(info[12:], 2))[2:]).decode("utf-8")

    def getkey(self) -> list:
        return self.secret

    def setkey(self, key: list) -> None:
        self.secret = key