diff --git a/bin/stegano-lsb-set b/bin/stegano-lsb-set index e0c55b6..67f65c1 100755 --- a/bin/stegano-lsb-set +++ b/bin/stegano-lsb-set @@ -1,5 +1,5 @@ #!/usr/bin/env python -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2017 Cédric Bonhomme - https://www.cedricbonhomme.org @@ -22,7 +22,7 @@ __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.7 $" __date__ = "$Date: 2016/03/18 $" -__revision__ = "$Date: 2017/05/16 $" +__revision__ = "$Date: 2019/06/04 $" __license__ = "GPLv3" import inspect @@ -40,71 +40,87 @@ import argparse parser = argparse.ArgumentParser(prog='stegano-lsb-set') subparsers = parser.add_subparsers(help='sub-command help', dest='command') + +class ValidateGenerator(argparse.Action): + def __call__(self, parser, args, values, option_string=None): + valid_generators = [generator[0] for generator in inspect.getmembers( + generators, inspect.isfunction)] + # Verify that the generator is valid + generator = values[0] + if generator not in valid_generators: + raise ValueError("Unknown generator: %s" % generator) + # Set the generator_function arg of the parser + setattr(args, self.dest, values) + + # Subparser: Hide parser_hide = subparsers.add_parser('hide', help='hide help') # Original image parser_hide.add_argument("-i", "--input", dest="input_image_file", - required=True, help="Input image file.") + required=True, help="Input image 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.") + choices=tools.ENCODINGS.keys(), default='UTF-8', + help="Specify the encoding of the message to hide." + + " UTF-8 (default) or UTF-32LE.") # Generator parser_hide.add_argument("-g", "--generator", dest="generator_function", - choices=[generator[0] for generator in \ - inspect.getmembers(generators, inspect.isfunction)], - required=True, help="Generator") + action=ValidateGenerator, + nargs='*', required=True, help="Generator (with optional arguments)") parser_hide.add_argument("-s", "--shift", dest="shift", - default=0, help="Shift for the generator") + default=0, help="Shift for the generator") 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).") + 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).") + help="Your secret to hide (Text or any binary file).") # Image containing the secret parser_hide.add_argument("-o", "--output", dest="output_image_file", - required=True, help="Output image containing the secret.") + required=True, help="Output image containing the secret.") # Subparser: Reveal parser_reveal = subparsers.add_parser('reveal', help='reveal help') parser_reveal.add_argument("-i", "--input", dest="input_image_file", - required=True, help="Input image file.") + required=True, help="Input image 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.") + choices=tools.ENCODINGS.keys(), default='UTF-8', + help="Specify the encoding of the message to reveal." + + " UTF-8 (default) or UTF-32LE.") parser_reveal.add_argument("-g", "--generator", dest="generator_function", - choices=[generator[0] for generator in \ - inspect.getmembers(generators, inspect.isfunction)], - required=True, help="Generator") + action=ValidateGenerator, + nargs='*', required=True, help="Generator (with optional arguments)") parser_reveal.add_argument("-s", "--shift", dest="shift", - default=0, help="Shift for the generator") + 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).") + help="Output for the binary secret (Text or any binary file).") # Subparser: List generators parser_list_generators = subparsers.add_parser('list-generators', - help='list-generators help') + help='list-generators help') arguments = parser.parse_args() if arguments.command != 'list-generators': try: - arguments.generator_function + arguments.generator_function[0] except AttributeError: print('You must specify the name of a generator.') parser.print_help() exit(1) try: - generator = getattr(generators, arguments.generator_function)() + if (len(arguments.generator_function) > 1): + generator = getattr(generators, arguments.generator_function[0])( + *[int(e) for e in arguments.generator_function[1:]]) + else: + generator = getattr(generators, arguments.generator_function[0])() + except AttributeError as e: print("Unknown generator: {}".format(arguments.generator_function)) exit(1) diff --git a/stegano/lsbset/generators.py b/stegano/lsbset/generators.py index 5db3a7f..e2b2479 100644 --- a/stegano/lsbset/generators.py +++ b/stegano/lsbset/generators.py @@ -22,7 +22,7 @@ __author__ = "Cedric Bonhomme" __version__ = "$Revision: 0.3 $" __date__ = "$Date: 2011/12/28 $" -__revision__ = "$Date: 2017/03/10 $" +__revision__ = "$Date: 2019/06/04 $" __license__ = "GPLv3" import itertools @@ -107,15 +107,24 @@ def carmichael() -> Iterator[int]: yield m -def ackermann_naive(m: int, n: int) -> int: +def ackermann_slow(m: int, n: int) -> int: """Ackermann number. """ if m == 0: return n + 1 elif n == 0: - return ackermann_naive(m - 1, 1) + return ackermann_slow(m - 1, 1) else: - return ackermann_naive(m - 1, ackermann_naive(m, n - 1)) + return ackermann_slow(m - 1, ackermann_slow(m, n - 1)) + + +def ackermann_naive(m: int) -> Iterator[int]: + """Naive Ackermann encapsulated in a generator + """ + n = 0 + while True: + yield ackermann_slow(m, n) + n += 1 def ackermann_fast(m: int, n: int) -> int: @@ -136,6 +145,7 @@ def ackermann_fast(m: int, n: int) -> int: else: return n + 1 + def ackermann(m: int) -> Iterator[int]: """Ackermann encapsulated in a generator. """ @@ -144,6 +154,7 @@ def ackermann(m: int) -> Iterator[int]: yield ackermann_fast(m, n) n += 1 + def fibonacci() -> Iterator[int]: """Generate the sequence of Fibonacci. https://oeis.org/A000045 diff --git a/tests/test_generators.py b/tests/test_generators.py index 58d9df3..9450a01 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # Stéganô - Stéganô is a basic Python Steganography module. # Copyright (C) 2010-2017 Cédric Bonhomme - https://www.cedricbonhomme.org @@ -30,78 +30,104 @@ import itertools from stegano.lsbset import generators + class TestGenerators(unittest.TestCase): def test_identity(self): """Test the identity generator. """ self.assertEqual(tuple(itertools.islice(generators.identity(), 15)), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)) + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)) def test_fibonacci(self): """Test the Fibonacci generator. """ self.assertEqual(tuple(itertools.islice(generators.fibonacci(), 20)), - (1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, - 987, 1597, 2584, 4181, 6765, 10946)) + (1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, + 987, 1597, 2584, 4181, 6765, 10946)) def test_eratosthenes(self): """Test the Eratosthenes sieve. """ - with open('./tests/expected-results/eratosthenes', 'r') as f: + with open('./tests/expected-results/eratosthenes', 'r') as f: self.assertEqual(tuple(itertools.islice(generators.eratosthenes(), 168)), - tuple(int(line) for line in f)) + tuple(int(line) for line in f)) def test_composite(self): """Test the composite sieve. """ - with open('./tests/expected-results/composite', 'r') as f: + with open('./tests/expected-results/composite', 'r') as f: self.assertEqual(tuple(itertools.islice(generators.composite(), 114)), - tuple(int(line) for line in f)) + tuple(int(line) for line in f)) def test_fermat(self): """Test the Fermat generator. """ - with open('./tests/expected-results/fermat', 'r') as f: + with open('./tests/expected-results/fermat', 'r') as f: self.assertEqual(tuple(itertools.islice(generators.fermat(), 9)), tuple(int(line) for line in f)) def test_triangular_numbers(self): """Test the Triangular numbers generator. """ - with open('./tests/expected-results/triangular_numbers', 'r') as f: + with open('./tests/expected-results/triangular_numbers', 'r') as f: self.assertEqual(tuple(itertools.islice(generators.triangular_numbers(), 54)), tuple(int(line) for line in f)) def test_mersenne(self): """Test the Mersenne generator. """ - with open('./tests/expected-results/mersenne', 'r') as f: + with open('./tests/expected-results/mersenne', 'r') as f: self.assertEqual(tuple(itertools.islice(generators.mersenne(), 20)), tuple(int(line) for line in f)) def test_carmichael(self): """Test the Carmichael generator. """ - with open('./tests/expected-results/carmichael', 'r') as f: + with open('./tests/expected-results/carmichael', 'r') as f: self.assertEqual(tuple(itertools.islice(generators.carmichael(), 33)), tuple(int(line) for line in f)) - def test_ackermann_naive(self): - """Test the Ackermann set. - """ - - self.assertEqual(generators.ackermann_naive(3, 1), 13) - self.assertEqual(generators.ackermann_naive(3, 2), 29) - - def test_ackermann(self): + def test_ackermann_slow(self): """Test the Ackermann set. """ with open('./tests/expected-results/ackermann', 'r') as f: - self.assertEqual(generators.ackermann_fast(3, 1), int(f.readline())) - self.assertEqual(generators.ackermann_fast(3, 2), int(f.readline())) - self.assertEqual(generators.ackermann_fast(4, 1), int(f.readline())) - self.assertEqual(generators.ackermann_fast(4, 2), int(f.readline())) + self.assertEqual(generators.ackermann_slow( + 3, 1), int(f.readline())) + self.assertEqual(generators.ackermann_slow( + 3, 2), int(f.readline())) + + def test_ackermann_naive(self): + """Test the Naive Ackermann generator + """ + gen = generators.ackermann_naive(3) + next(gen) + with open('./tests/expected-results/ackermann', 'r') as f: + self.assertEqual(next(gen), int(f.readline())) + self.assertEqual(next(gen), int(f.readline())) + + def test_ackermann_fast(self): + """Test the Ackermann set. + """ + with open('./tests/expected-results/ackermann', 'r') as f: + self.assertEqual(generators.ackermann_fast( + 3, 1), int(f.readline())) + self.assertEqual(generators.ackermann_fast( + 3, 2), int(f.readline())) + self.assertEqual(generators.ackermann_fast( + 4, 1), int(f.readline())) + self.assertEqual(generators.ackermann_fast( + 4, 2), int(f.readline())) + + def test_ackermann(self): + """Test the Ackermann generator + """ + gen = generators.ackermann(3) + next(gen) + with open('./tests/expected-results/ackermann', 'r') as f: + self.assertEqual(next(gen), int(f.readline())) + self.assertEqual(next(gen), int(f.readline())) + if __name__ == '__main__': unittest.main()