diff --git a/stegano/console/wav.py b/stegano/console/wav.py deleted file mode 100644 index 40cd29d..0000000 --- a/stegano/console/wav.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python -# Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2025 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information : https://github.com/cedricbonhomme/Stegano -# -# 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 -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.7 $" -__date__ = "$Date: 2016/03/18 $" -__revision__ = "$Date: 2019/06/04 $" -__license__ = "GPLv3" - -try: - from stegano import wav -except Exception: - print("Install stegano: pipx install Stegano") - -import argparse - -from stegano import tools - -def main(): - parser = argparse.ArgumentParser(prog="stegano-lsb") - subparsers = parser.add_subparsers( - help="sub-command help", dest="command", required=True - ) - - # Subparser: Hide - parser_hide = subparsers.add_parser("hide", help="hide help") - # Original audio - parser_hide.add_argument( - "-i", - "--input", - dest="input_audio_file", - required=True, - help="Input audio file.", - ) - parser_hide.add_argument( - "-e", - "--encoding", - dest="encoding", - choices=tools.ENCODINGS.keys(), - default="UTF-8", - help="Specify the encoding of the message to hide." - " UTF-8 (default) or UTF-32LE.", - ) - - group_secret = parser_hide.add_mutually_exclusive_group(required=True) - # Non binary secret message to hide - group_secret.add_argument( - "-m", dest="secret_message", help="Your secret message to hide (non binary)." - ) - # Binary secret message to hide - group_secret.add_argument( - "-f", dest="secret_file", help="Your secret to hide (Text or any binary file)." - ) - - # Audio containing the secret - parser_hide.add_argument( - "-o", - "--output", - dest="output_audio_file", - required=True, - help="Output audio containing the secret.", - ) - - # Subparser: Reveal - parser_reveal = subparsers.add_parser("reveal", help="reveal help") - parser_reveal.add_argument( - "-i", - "--input", - dest="input_audio_file", - required=True, - help="Input audio file.", - ) - parser_reveal.add_argument( - "-e", - "--encoding", - dest="encoding", - choices=tools.ENCODINGS.keys(), - default="UTF-8", - help="Specify the encoding of the message to reveal." - " UTF-8 (default) or UTF-32LE.", - ) - - arguments = parser.parse_args() - - if arguments.command == "hide": - if arguments.secret_message is not None: - secret = arguments.secret_message - elif arguments.secret_file != "": - secret = tools.binary2base64(arguments.secret_file) - - wav.hide( - input_file=arguments.input_audio_file, - message=secret, - encoding=arguments.encoding, - output_file=arguments.output_audio_file - ) - - elif arguments.command == "reveal": - try: - secret = wav.reveal( - encoded_wav=arguments.input_audio_file, - encoding=arguments.encoding - ) - except IndexError: - print("Impossible to detect message.") - exit(0) - if arguments.secret_binary is not None: - data = tools.base642binary(secret) - with open(arguments.secret_binary, "wb") as f: - f.write(data) - else: - print(secret) diff --git a/stegano/exifHeader/exifHeader.py b/stegano/exifHeader/exifHeader.py index 1fefa93..33fec99 100644 --- a/stegano/exifHeader/exifHeader.py +++ b/stegano/exifHeader/exifHeader.py @@ -40,7 +40,7 @@ def hide( from zlib import compress if secret_file is not None: - with open(secret_file, "rb") as f: + with open(secret_file) as f: secret_message = f.read() try: diff --git a/stegano/wav/__init__.py b/stegano/wav/__init__.py deleted file mode 100644 index e2529fe..0000000 --- a/stegano/wav/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python - -from .wav import hide, reveal - -__all__ = ["hide", "reveal"] diff --git a/stegano/wav/wav.py b/stegano/wav/wav.py deleted file mode 100644 index 0593257..0000000 --- a/stegano/wav/wav.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# Stegano - Stéganô is a basic Python Steganography module. -# Copyright (C) 2010-2024 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information : https://github.com/cedricbonhomme/Stegano -# -# 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 -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.2 $" -__date__ = "$Date: 2010/10/01 $" -__revision__ = "$Date: 2017/02/06 $" -__license__ = "GPLv3" - -import wave -from typing import IO, Union -from stegano import tools - -def hide( - input_file: Union[str, IO[bytes]], - message: str, - output_file: Union[str, IO[bytes]], - encoding: str = "UTF-8" -): - """ - Hide a message (string) in a .wav audio file. - - Use the lsb of each PCM encoded sample to hide the message string characters as ASCII values. - The first eight bits are used for message_length of the string. - """ - message_length = len(message) - assert message_length != 0, "message message_length is zero" - assert message_length < 255, "message is too long" - - output = wave.open(output_file, "wb") - with wave.open(input_file, "rb") as input: - # get .wav params - nchannels, sampwidth, framerate, nframes, comptype, _ = input.getparams() - assert comptype == "NONE", "only uncompressed files are supported" - - nsamples = nframes * nchannels - - message_bits = f"{message_length:08b}" + "".join(tools.a2bits_list(message, encoding)) - assert len(message_bits) <= nsamples, "message is too long" - - # copy over .wav params to output - output.setnchannels(nchannels) - output.setsampwidth(sampwidth) - output.setframerate(framerate) - - # encode message in frames - frames = bytearray(input.readframes(nsamples)) - for i in range(nsamples): - if i < len(message_bits): - if message_bits[i] == "0": - frames[i] = frames[i] & ~1 - else: - frames[i] = frames[i] | 1 - - # write out - output.writeframes(frames) - - -def reveal(input_file: Union[str, IO[bytes]], encoding: str = "UTF-8"): - """ - Find a message in an image. - - Check the lsb of each PCM encoded sample for hidden message characters (ASCII values). - The first eight bits are used for message_length of the string. - """ - message = "" - encoding_len = tools.ENCODINGS[encoding] - with wave.open(input_file, "rb") as input: - nchannels, _, _, nframes, comptype, _ = input.getparams() - assert comptype == "NONE", "only uncompressed files are supported" - - nsamples = nframes * nchannels - frames = bytearray(input.readframes(nsamples)) - - # Read first 8 bits for message length - length_bits = "" - for i in range(8): - length_bits += str(frames[i] & 1) - message_length = int(length_bits, 2) - - # Read message bits - message_bits = "" - for i in range(8, 8 + message_length * encoding_len): - message_bits += str(frames[i] & 1) - - # Convert bits to string - chars = [chr(int(message_bits[i:i+encoding_len], 2)) for i in range(0, len(message_bits), encoding_len)] - message = "".join(chars) - return message diff --git a/tests/sample-files/free-software-song.wav b/tests/sample-files/free-software-song.wav deleted file mode 100644 index 405684a..0000000 Binary files a/tests/sample-files/free-software-song.wav and /dev/null differ diff --git a/tests/test_wav.py b/tests/test_wav.py deleted file mode 100644 index 86276d0..0000000 --- a/tests/test_wav.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# Stegano - Stegano is a pure Python steganography module. -# Copyright (C) 2010-2025 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information : https://github.com/cedricbonhomme/Stegano -# -# 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 -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.1 $" -__date__ = "$Date: 2016/05/19 $" -__license__ = "GPLv3" - -import os -import unittest - -from stegano import wav - - -class TestWav(unittest.TestCase): - def test_hide_empty_message(self): - """ - Test hiding the empty string. - """ - with self.assertRaises(AssertionError): - wav.hide("./tests/sample-files/free-software-song.wav", "", "./audio.wav") - - def test_hide_and_reveal(self): - messages_to_hide = ["a", "foo", "Hello World!", ":Python:"] - - for message in messages_to_hide: - wav.hide("./tests/sample-files/free-software-song.wav", message, "./audio.wav") - clear_message = wav.reveal("./audio.wav") - - self.assertEqual(message, clear_message) - - def test_with_too_long_message(self): - with open("./tests/sample-files/lorem_ipsum.txt") as f: - message = f.read() - with self.assertRaises(AssertionError): - wav.hide("./tests/sample-files/free-software-song.wav", message, "./audio.wav") - - def tearDown(self): - try: - os.unlink("./audio.wav") - except Exception: - pass - - -if __name__ == "__main__": - unittest.main()