def recover_message_from_image(input_image, num_lsb): """Returns the message from the steganographed image""" start = time() if isinstance(input_image, Image.Image): steg_image = input_image else: steg_image = Image.open(input_image) color_data = [v for t in steg_image.getdata() for v in t] file_size_tag_size = bytes_in_max_file_size(steg_image, num_lsb) tag_bit_height = roundup(8 * file_size_tag_size / num_lsb) bytes_to_recover = int.from_bytes( lsb_deinterleave_list(color_data[:tag_bit_height], 8 * file_size_tag_size, num_lsb), byteorder=sys.byteorder, ) log.debug(f"Files read".ljust(30) + f" in {time() - start:.2f}s") start = time() data = lsb_deinterleave_list(color_data[tag_bit_height:], 8 * bytes_to_recover, num_lsb) log.debug(f"{bytes_to_recover} bytes recovered".ljust(30) + f" in {time() - start:.2f}s") return data
def recover_message_from_image(input_image, num_lsb): """Returns the message from the steganographed image""" start = time() if isinstance(input_image, Image.Image): steg_image = input_image else: steg_image = Image.open(input_image) color_data = [v for t in steg_image.getdata() for v in t] file_size_tag_size = bytes_in_max_file_size(steg_image, num_lsb) tag_bit_height = roundup(8 * file_size_tag_size / num_lsb) bytes_to_recover = int.from_bytes( lsb_deinterleave_list(color_data[:tag_bit_height], 8 * file_size_tag_size, num_lsb), byteorder=sys.byteorder, ) maximum_bytes_in_image = num_lsb * len(color_data[tag_bit_height:]) // 8 if bytes_to_recover > maximum_bytes_in_image: raise ValueError( f"This image appears to be corrupted.\n" + f"It claims to hold {bytes_to_recover} B, " + f"but can only hold {maximum_bytes_in_image} B with {num_lsb} LSBs" ) log.debug(f"Files read".ljust(30) + f" in {time() - start:.2f}s") start = time() data = lsb_deinterleave_list(color_data[tag_bit_height:], 8 * bytes_to_recover, num_lsb) log.debug(f"{bytes_to_recover} bytes recovered".ljust(30) + f" in {time() - start:.2f}s") return data
def test_random_interleaving(self, num_trials=256, filename_length=5): filename = "".join( choice(string.ascii_lowercase) for _ in range(filename_length) ) png_input_filename = filename + ".png" payload_input_filename = filename + ".txt" png_output_filename = filename + "_steg.png" payload_output_filename = filename + "_recovered.txt" np.random.seed(0) for _ in range(num_trials): width = np.random.randint(1, 256) height = np.random.randint(1, 256) num_lsb = np.random.randint(1, 9) file_size_tag_length = roundup( int(3 * width * height * num_lsb).bit_length() / 8 ) payload_len = (3 * width * height * num_lsb - 8 * file_size_tag_length) // 8 if payload_len < 0: continue self.write_random_image(png_input_filename, width=width, height=height) self.write_random_file(payload_input_filename, num_bytes=payload_len) try: hide_data( png_input_filename, payload_input_filename, png_output_filename, num_lsb, compression_level=1, ) recover_data(png_output_filename, payload_output_filename, num_lsb) except ValueError as e: os.remove(png_input_filename) os.remove(payload_input_filename) os.remove(png_output_filename) os.remove(payload_output_filename) raise e with open(payload_input_filename, "rb") as input_file, open( payload_output_filename, "rb" ) as output_file: input_payload_data = input_file.read() output_payload_data = output_file.read() os.remove(png_input_filename) os.remove(payload_input_filename) os.remove(png_output_filename) os.remove(payload_output_filename) self.assertEqual(input_payload_data, output_payload_data)
def bytes_in_max_file_size(image, num_lsb): """Returns the number of bits needed to store the size of the file.""" return roundup(max_bits_to_hide(image, num_lsb).bit_length() / 8)