Change path in example

This commit is contained in:
Valentin Moguérou 2022-03-19 19:51:20 +01:00
parent 7156129ef3
commit 89c34c408c

View File

@ -1,145 +1,145 @@
""" """
A library for the steganography program A library for the steganography program
Copyright (C) 2022 Valentin Moguérou Copyright (C) 2022 Valentin Moguérou
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from typing import BinaryIO from typing import BinaryIO
from PIL import Image from PIL import Image
# ================= BINARY OPERATIONS ================= # ================= BINARY OPERATIONS =================
def write_lsb_in_bin(byte, bit): def write_lsb_in_bin(byte, bit):
"Write the specified bit in the LSB of the specified byte" "Write the specified bit in the LSB of the specified byte"
return byte & 254 | bit if bit==1 else byte & 254 return byte & 254 | bit if bit==1 else byte & 254
def read_lsb_in_bin(byte): def read_lsb_in_bin(byte):
return byte & 1 return byte & 1
def combine_bits_to_byte(bits): def combine_bits_to_byte(bits):
""" """
Turns a sequence of bits into an integer. Turns a sequence of bits into an integer.
Example: turns [0, 1, 1, 0, 1, 1, 0, 1] into 0b01101101 or 109 Example: turns [0, 1, 1, 0, 1, 1, 0, 1] into 0b01101101 or 109
""" """
return sum(bits[-1-i]<<i for i in range(len(bits))) return sum(bits[-1-i]<<i for i in range(len(bits)))
def split_byte_into_bits(integer): def split_byte_into_bits(integer):
""" """
Turns an integer into an array of bits Turns an integer into an array of bits
Example: turns 109 or 0b01101101 into [0, 1, 1, 0, 1, 1, 0, 1] Example: turns 109 or 0b01101101 into [0, 1, 1, 0, 1, 1, 0, 1]
""" """
return [integer>>(7-i)&1 for i in range(8)] return [integer>>(7-i)&1 for i in range(8)]
def hex_print(byte_list, margin=0, line_width=16): def hex_print(byte_list, margin=0, line_width=16):
"Prints a byte list in hexadecimal" "Prints a byte list in hexadecimal"
for line in range(0, len(byte_list), line_width): for line in range(0, len(byte_list), line_width):
print(' '*margin + ' '.join(f'{byte:02X}' for byte in byte_list[line:line+line_width])) print(' '*margin + ' '.join(f'{byte:02X}' for byte in byte_list[line:line+line_width]))
# ================= STRING OPERATIONS ================= # ================= STRING OPERATIONS =================
def encode_string(string: str) -> bytearray: def encode_string(string: str) -> bytearray:
"Turns the given string into a byte array" "Turns the given string into a byte array"
return bytearray(ord(ch) for ch in string+'\0') return bytearray(ord(ch) for ch in string+'\0')
def decode_string(bytelist: bytearray) -> str: def decode_string(bytelist: bytearray) -> str:
"Turns the given byte array into a string" "Turns the given byte array into a string"
return ''.join(chr(b) for b in bytelist) return ''.join(chr(b) for b in bytelist)
# ================= WRITE OPERATIONS ================= # ================= WRITE OPERATIONS =================
def write_band(band: bytearray, byte_list: bytearray, starting_pos=0) -> bool: def write_band(band: bytearray, byte_list: bytearray, starting_pos=0) -> bool:
"Writes a byte sequence in the given band" "Writes a byte sequence in the given band"
for index in range(starting_pos, len(band), 8): for index in range(starting_pos, len(band), 8):
band[index:index+8] = (write_lsb_in_bin(band[index+i], bit) for i, bit in enumerate(split_byte_into_bits(byte_list[index//8]))) band[index:index+8] = (write_lsb_in_bin(band[index+i], bit) for i, bit in enumerate(split_byte_into_bits(byte_list[index//8])))
if byte_list[index//8] == 0: if byte_list[index//8] == 0:
return True, index+8 # finished writing return True, index+8 # finished writing
return False, index+8 # didn't finish writing, return last index return False, index+8 # didn't finish writing, return last index
def write_image(img: Image.Image, byte_list, verbose=False): def write_image(img: Image.Image, byte_list, verbose=False):
data = [bytearray(band.tobytes()) for band in img.split()] data = [bytearray(band.tobytes()) for band in img.split()]
ok, position = write_band(data[0], byte_list) ok, position = write_band(data[0], byte_list)
if not ok: if not ok:
ok, position = write_band(data[1], byte_list, position) ok, position = write_band(data[1], byte_list, position)
if not ok: if not ok:
ok, position = write_band(data[2], byte_list, position) ok, position = write_band(data[2], byte_list, position)
if not ok: if not ok:
raise ValueError("The byte sequence is too long.") raise ValueError("The byte sequence is too long.")
if verbose: if verbose:
print(f"{position} bits, {position//8} bytes successfully written.") print(f"{position} bits, {position//8} bytes successfully written.")
return Image.merge(img.mode, [Image.frombytes('L', img.size, bytes(band)) for band in data]) return Image.merge(img.mode, [Image.frombytes('L', img.size, bytes(band)) for band in data])
def write_file(from_file: BinaryIO, to_file: BinaryIO, byte_list, verbose=False): def write_file(from_file: BinaryIO, to_file: BinaryIO, byte_list, verbose=False):
write_image(Image.open(from_file), byte_list, verbose=verbose).save(to_file) write_image(Image.open(from_file), byte_list, verbose=verbose).save(to_file)
# ================= READ OPERATIONS ================= # ================= READ OPERATIONS =================
def read_band(band: bytes, byte_list: bytearray) -> bool: def read_band(band: bytes, byte_list: bytearray) -> bool:
"Turns a sequence of pixels into a byte list" "Turns a sequence of pixels into a byte list"
for i in range(0, len(band), 8): for i in range(0, len(band), 8):
bits = [read_lsb_in_bin(b) for b in band[i:i+8]] bits = [read_lsb_in_bin(b) for b in band[i:i+8]]
if all(b==0 for b in bits): if all(b==0 for b in bits):
return True # finished reading return True # finished reading
byte_list.append(combine_bits_to_byte(bits)) byte_list.append(combine_bits_to_byte(bits))
return False # didn't finish reading return False # didn't finish reading
def read_image(img: Image.Image, verbose=False): def read_image(img: Image.Image, verbose=False):
"Read the whole image" "Read the whole image"
data = [band.tobytes() for band in img.split()] data = [band.tobytes() for band in img.split()]
byte_list = bytearray() byte_list = bytearray()
if read_band(data[0], byte_list): if read_band(data[0], byte_list):
if verbose: if verbose:
print("Successfully finished reading.") print("Successfully finished reading.")
elif read_band(data[1], byte_list): elif read_band(data[1], byte_list):
if verbose: if verbose:
print("Successfully finished reading.") print("Successfully finished reading.")
print("Read the whole red band.") print("Read the whole red band.")
elif read_band(data[1], byte_list): elif read_band(data[1], byte_list):
if verbose: if verbose:
print("Successfully finished reading.") print("Successfully finished reading.")
print("Read the whole green band.") print("Read the whole green band.")
else: else:
if verbose: if verbose:
print("Read the whole blue band.") print("Read the whole blue band.")
raise ValueError("Invalid image, did not find end control sequence.") raise ValueError("Invalid image, did not find end control sequence.")
return byte_list return byte_list
def read_file(from_file: BinaryIO, verbose=False): def read_file(from_file: BinaryIO, verbose=False):
return read_image(Image.open(from_file), verbose=verbose) return read_image(Image.open(from_file), verbose=verbose)
# ================= EXAMPLE PROGRAM ================= # ================= EXAMPLE PROGRAM =================
def main(): def main():
s = "This is a string" s = "This is a string"
oldimg = Image.open('guitar.jpg') oldimg = Image.open('default_image.jpg')
oldimg.load() oldimg.load()
print(encode_string(s)) print(encode_string(s))
newimg = write_image(oldimg, encode_string(s)) newimg = write_image(oldimg, encode_string(s))
newimg.save('dissimulated.png', 'PNG') newimg.save('dissimulated.png', 'PNG')
print('Written...') print('Written...')
print(f"String: --> {read_image(Image.open('guitar.jpg'))} (guitar.jpg)") print(f"String: --> {read_image(Image.open('guitar.jpg'))} (guitar.jpg)")
print(f"String: --> {decode_string(read_image(Image.open('dissimulated.png')))} (dissimulated.png)") print(f"String: --> {decode_string(read_image(Image.open('dissimulated.png')))} (dissimulated.png)")
if __name__ == '__main__': if __name__ == '__main__':
main() main()