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