diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 988e4c8..2dd4955 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Release History =============== +0.9.0 (2018-12-18) +------------------ + +* added the possibility to shift the encoded bits when using the lsbset module. + 0.8.6 (2018-11-05) ------------------ diff --git a/bin/stegano-lsb-set b/bin/stegano-lsb-set index e8dd2d2..e0c55b6 100755 --- a/bin/stegano-lsb-set +++ b/bin/stegano-lsb-set @@ -55,6 +55,8 @@ parser_hide.add_argument("-g", "--generator", dest="generator_function", choices=[generator[0] for generator in \ inspect.getmembers(generators, inspect.isfunction)], required=True, help="Generator") +parser_hide.add_argument("-s", "--shift", dest="shift", + default=0, help="Shift for the generator") group_secret = parser_hide.add_mutually_exclusive_group(required=True) # Non binary secret message to hide @@ -81,6 +83,8 @@ parser_reveal.add_argument("-g", "--generator", dest="generator_function", choices=[generator[0] for generator in \ inspect.getmembers(generators, inspect.isfunction)], required=True, help="Generator") +parser_reveal.add_argument("-s", "--shift", dest="shift", + default=0, help="Shift for the generator") parser_reveal.add_argument("-o", dest="secret_binary", help="Output for the binary secret (Text or any binary file).") @@ -111,7 +115,8 @@ if arguments.command == 'hide': elif arguments.secret_file != "": secret = tools.binary2base64(arguments.secret_file) - img_encoded = lsbset.hide(arguments.input_image_file, secret, generator) + img_encoded = lsbset.hide(arguments.input_image_file, secret, generator, + int(arguments.shift)) try: img_encoded.save(arguments.output_image_file) except Exception as e: @@ -120,7 +125,8 @@ if arguments.command == 'hide': elif arguments.command == 'reveal': try: - secret = lsbset.reveal(arguments.input_image_file, generator) + secret = lsbset.reveal(arguments.input_image_file, generator, + int(arguments.shift)) except IndexError: print("Impossible to detect message.") exit(0) diff --git a/setup.py b/setup.py index 86b26cf..48c5a08 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ with codecs.open(os.path.join(here, 'CHANGELOG.rst'), encoding='utf-8') as f: setup( name='Stegano', - version='0.8.6', + version='0.9.0', author='Cédric Bonhomme', author_email='cedric@cedricbonhomme.org', packages=packages, diff --git a/stegano/lsbset/lsbset.py b/stegano/lsbset/lsbset.py index d76697a..4fb48eb 100644 --- a/stegano/lsbset/lsbset.py +++ b/stegano/lsbset/lsbset.py @@ -2,7 +2,7 @@ #-*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. -# Copyright (C) 2010-2017 Cédric Bonhomme - https://www.cedricbonhomme.org +# Copyright (C) 2010-2018 Cédric Bonhomme - https://www.cedricbonhomme.org # # For more information : https://github.com/cedricbonhomme/Stegano # @@ -20,9 +20,9 @@ # along with this program. If not, see __author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.5 $" +__version__ = "$Revision: 0.6 $" __date__ = "$Date: 2016/03/13 $" -__revision__ = "$Date: 2017/05/04 $" +__revision__ = "$Date: 2018/12/18 $" __license__ = "GPLv3" import sys @@ -36,6 +36,7 @@ from . import generators def hide(input_image: Union[str, IO[bytes]], message: str, generator: Iterator[int], + shift: int = 0, encoding: str = 'UTF-8', auto_convert_rgb: bool = False): """Hide a message (string) in an image with the @@ -68,6 +69,9 @@ def hide(input_image: Union[str, IO[bytes]], if len_message_bits > npixels * 3: raise Exception("The message you want to hide is too long: {}". \ format(message_length)) + while shift != 0: + next(generator) + shift -= 1 while index + 3 <= len_message_bits : generated_number = next(generator) @@ -97,6 +101,7 @@ def hide(input_image: Union[str, IO[bytes]], def reveal(input_image: Union[str, IO[bytes]], generator: Iterator[int], + shift: int = 0, encoding: str = 'UTF-8'): """Find a message in an image (with the LSB technique). """ @@ -107,6 +112,10 @@ def reveal(input_image: Union[str, IO[bytes]], bitab = [] limit = None + while shift != 0: + next(generator) + shift -= 1 + while True: generated_number = next(generator) # color = [r, g, b] diff --git a/tests/test_lsb.py b/tests/test_lsb.py index 0030fea..d186aec 100644 --- a/tests/test_lsb.py +++ b/tests/test_lsb.py @@ -54,20 +54,20 @@ class TestLSB(unittest.TestCase): def test_hide_and_reveal_UTF32LE(self): messages_to_hide = 'I love 🍕 and 🍫!' secret = lsb.hide("./tests/sample-files/Lenna.png", - messages_to_hide, 'UTF-32LE') + messages_to_hide, encoding='UTF-32LE') secret.save("./image.png") - clear_message = lsb.reveal("./image.png", 'UTF-32LE') + clear_message = lsb.reveal("./image.png", encoding='UTF-32LE') self.assertEqual(messages_to_hide, clear_message) def test_with_transparent_png(self): messages_to_hide = ['🍕', 'a', 'foo', 'Hello World!', ':Python:'] for message in messages_to_hide: secret = lsb.hide("./tests/sample-files/transparent.png", - message, 'UTF-32LE') + message, encoding='UTF-32LE') secret.save("./image.png") - clear_message = lsb.reveal("./image.png", 'UTF-32LE') + clear_message = lsb.reveal("./image.png", encoding='UTF-32LE') self.assertEqual(message, clear_message) @@ -75,19 +75,19 @@ class TestLSB(unittest.TestCase): def test_manual_convert_rgb(self, input): message_to_hide = 'I love 🍕 and 🍫!' secret = lsb.hide("./tests/sample-files/Lenna-grayscale.png", - message_to_hide, 'UTF-32LE') + message_to_hide, encoding='UTF-32LE') @patch('builtins.input', return_value='n') def test_refuse_convert_rgb(self, input): message_to_hide = 'I love 🍕 and 🍫!' with self.assertRaises(Exception): secret = lsb.hide("./tests/sample-files/Lenna-grayscale.png", - message_to_hide, 'UTF-32LE') + message_to_hide, encoding='UTF-32LE') def test_auto_convert_rgb(self): message_to_hide = 'I love 🍕 and 🍫!' secret = lsb.hide("./tests/sample-files/Lenna-grayscale.png", - message_to_hide, 'UTF-32LE', True) + message_to_hide, encoding='UTF-32LE', auto_convert_rgb=True) def test_with_text_file(self): text_file_to_hide = './tests/sample-files/lorem_ipsum.txt' diff --git a/tests/test_lsbset.py b/tests/test_lsbset.py index 4cd2f50..20861df 100644 --- a/tests/test_lsbset.py +++ b/tests/test_lsbset.py @@ -53,17 +53,29 @@ class TestLSBSet(unittest.TestCase): generators.eratosthenes()) self.assertEqual(message, clear_message) + + def test_hide_and_reveal_with_shift(self): + messages_to_hide = ["a", "foo", "Hello World!", ":Python:"] + for message in messages_to_hide: + secret = lsbset.hide("./tests/sample-files/Lenna.png", message, + generators.eratosthenes(), 4) + secret.save("./image.png") + + clear_message = lsbset.reveal("./image.png", + generators.eratosthenes(), 4) + + self.assertEqual(message, clear_message) def test_hide_and_reveal_UTF32LE(self): messages_to_hide = 'I love 🍕 and 🍫!' secret = lsbset.hide("./tests/sample-files/Lenna.png", messages_to_hide, generators.eratosthenes(), - 'UTF-32LE') + encoding='UTF-32LE') secret.save("./image.png") clear_message = lsbset.reveal("./image.png", generators.eratosthenes(), - 'UTF-32LE') + encoding='UTF-32LE') self.assertEqual(messages_to_hide, clear_message) def test_with_transparent_png(self):