def test_export_medium(): ''' Can export binary data of medium ''' pm = PictureModifier() pm.load_medium(medium_data) assert pm.export_medium('BMP') == medium_data
def test_get_data_picture_not_load(): ''' If medium is not loaded, raise ValueError. ''' pm = PictureModifier() with pytest.raises(ValueError) as ve: pm.get_data(1) assert str(ve.value) == constants.PICTURE_NOT_LOADED_ERROR_MSG
def test_hide_data_picture_not_load(): ''' If medium is not loaded, raise ValueError. ''' secret = os.urandom(100) pm = PictureModifier() with pytest.raises(ValueError) as ve: pm.hide_data(secret) assert str(ve.value) == constants.PICTURE_NOT_LOADED_ERROR_MSG
def test_load_medium(): ''' Can load picture from binary data. ''' pm = PictureModifier() pm.load_medium(medium_data) assert pm.img.mode == 'RGB' assert pm.img_height == test_data_height assert pm.img_width == test_data_width assert pm.pixels is not None
def test_init(): ''' Can initialize ste_lvl, img and curr_read/write_pixel_index. ''' pm = PictureModifier() assert pm.ste_lvl == constants.DEFAULT_STEGANOGRAPHY_LEVEL for ste_lvl in range(1, 9): pm = PictureModifier(ste_lvl) assert pm.ste_lvl == ste_lvl \ and pm.curr_write_pixel_index == 0 \ and pm.curr_read_pixel_index == 0 \ and pm.img is None and pm.pixels is None \ and pm.img_width == 0 and pm.img_height == 0
def test_main_decryption_otp(mocker): ''' This package can be called straight from command line. Can decrypt data using OTP encryption. ''' my_namespace = namedtuple('Namespace', [ 'mode', 'encryption', 'key_stretch', 'level', 'medium', 'password', 'secret', 'result', 'format' ]) args = my_namespace(mode='decrypt', encryption='otp', key_stretch=None, \ level='2', medium='medium.bmp', password='******', \ secret='secret.txt', result='hidden.bmp', format='BMP') dummyEncryptor = OTPCryptor() dummyModifier = PictureModifier(int(args.level)) with mocker.patch('builtins.open', new_callable=mocker.mock_open()) as mo: with monkeypatch.context() as m: m.setattr(argparse.ArgumentParser, 'parse_args', lambda self: args) mocker.patch.object(OTPCryptor, '__init__') OTPCryptor.__init__.return_value = None mocker.patch.object(PictureModifier, '__init__') PictureModifier.__init__.return_value = None mocker.patch.object(Steganography, 'get_file') steganography_main() mo.assert_any_call(args.password, 'rb') mo.assert_any_call(args.result, 'wb') OTPCryptor.__init__.assert_called_once_with() PictureModifier.__init__.assert_called_once_with(int(args.level)) assert Steganography.get_file.call_count == 1
def test_get_data(): ''' Can get data from low-bit of pixel in image. ''' data_list1 = [ bytes.fromhex('ca'), os.urandom(100), os.urandom(200), os.urandom(400), ] data_list2 = [ bytes.fromhex('fe'), os.urandom(400), os.urandom(200), os.urandom(100), ] data_list3 = [ bytes.fromhex('babe'), os.urandom(100), os.urandom(200), os.urandom(400), ] data_list4 = [ os.urandom(100), os.urandom(100), os.urandom(100), os.urandom(100), ] for data1, data2, data3, data4 in zip(data_list1, data_list2, data_list3, data_list4): pm = PictureModifier() pm.load_medium(medium_data) pm.hide_data(data1) pm.hide_data(data2) pm.hide_data(data3) pm.hide_data(data4) assert pm.get_data(len(data1)) == data1 assert pm.get_data(len(data2)) == data2 assert pm.get_data(len(data3)) == data3 assert pm.get_data(len(data4)) == data4
def test_init_error_level_value(): ''' If steganography level is smaller than 1 or bigger than 0, raise ValueError. ''' ste_lvl_list = [0, -1, 9, 1000] for ste_lvl in ste_lvl_list: with pytest.raises(ValueError) as ve: PictureModifier(ste_lvl) assert str(ve.value) == constants.STEGANOGRAPHY_LEVEL_VALUE_ERROR_MSG
def test_init_error_type_value(): ''' If steganography level is not a number raise TypeError. ''' ste_lvl_list = ['aaa', [], None] for ste_lvl in ste_lvl_list: with pytest.raises(TypeError) as te: PictureModifier(ste_lvl) assert str(te.value) == constants.STEGANOGRAPHY_LEVEL_TYPE_ERROR_MSG
def test_get_data_from_pixel(): ''' Can get data from low-bit of individual pixel. ''' data1 = bitarray([True, False, False, False, True]) data2 = bitarray([False, False, False, True, True, True]) data3 = bitarray([False, False, True, True, True]) pm = PictureModifier() pm.load_medium(medium_data) assert pm.curr_write_pixel_index == 0 pm.hide_data_into_pixel(data1) pm.hide_data_into_pixel(data2) pm.hide_data_into_pixel(data3) assert pm.curr_write_pixel_index == 3 assert pm.get_data_from_pixel(pm.pixels[0, 0], 5) == data1 assert pm.get_data_from_pixel(pm.pixels[1, 0], 6) == data2 assert pm.get_data_from_pixel(pm.pixels[2, 0], 5) == data3
def test_get_data_value_error(): ''' If size is bigger than size of medium, raise TypeError ''' sizes = [ test_data_width * test_data_height * constants.MAX_COLOR_INDEX * test_depth, test_data_width * test_data_height * constants.MAX_COLOR_INDEX * test_depth, 0, -1, -10 ] messages = [ constants.GET_DATA_PARA_VALUE_ERROR_MSG, constants.GET_DATA_PARA_VALUE_ERROR_MSG, constants.GET_DATA_PARA_VALUE_NEGATIVE_MSG, constants.GET_DATA_PARA_VALUE_NEGATIVE_MSG, ] pm = PictureModifier() pm.load_medium(medium_data) for size, message in zip(sizes, messages): with pytest.raises(ValueError) as ve: pm.get_data(size) assert str(ve.value) == message
def test_hide_data_type_error(): ''' If data is not bytes, raise TypeError. ''' data_list = ['string', [], 1, None] pm = PictureModifier() pm.load_medium(medium_data) for data in data_list: with pytest.raises(TypeError) as te: pm.hide_data(data) assert str(te.value) == constants.INPUT_DATA_ERROR_MSG
def test_hide_data_size_error(): ''' If data is too big, raise ValueError ''' data = bytes(b'\0' * int(test_data_width * test_data_height * constants.MAX_COLOR_INDEX * test_depth / 8 + 1)) pm = PictureModifier() pm.load_medium(medium_data) with pytest.raises(ValueError) as ve: pm.hide_data(data) assert str(ve.value) == constants.DATA_SIZE_ERROR_MSG
def test_get_data_type_error(): ''' If size is not integer, raise TypeError. ''' sizes = [None, {}, 1] pm = PictureModifier() pm.load_medium(medium_data) for size in zip(sizes): with pytest.raises(TypeError) as te: pm.get_data(size) assert str(te.value) == constants.GET_DATA_PARA_TYPE_ERROR_MSG
def test_hide_data_into_pixel(): ''' Can hide data into low-bit of individual pixel ''' data = bitarray([True, False, False, False, True]) pm = PictureModifier() pm.load_medium(medium_data) pm.hide_data_into_pixel(data) assert pm.curr_write_pixel_index == 1 pixel = pm.pixels[0, 0] assert __get_bit(pixel[0], 0) == True assert __get_bit(pixel[0], 1) == False assert __get_bit(pixel[1], 0) == False assert __get_bit(pixel[1], 1) == False assert __get_bit(pixel[2], 0) == True
def test_hide_data(): ''' Can hide data into low-bit of pixel in image. ''' data = bytes.fromhex('05') pm = PictureModifier() pm.load_medium(medium_data) pm.hide_data(data) pixel1 = pm.pixels[0, 0] assert __get_bit(pixel1[0], 0) == False assert __get_bit(pixel1[0], 1) == False assert __get_bit(pixel1[1], 0) == False assert __get_bit(pixel1[1], 1) == False assert __get_bit(pixel1[2], 0) == False assert __get_bit(pixel1[2], 1) == True pixel2 = pm.pixels[1, 0] assert __get_bit(pixel2[0], 0) == False assert __get_bit(pixel2[0], 1) == True
def main(): ''' Entry point of script when run from command line. ''' parser=argparse.ArgumentParser() parser.add_argument('--mode', help='Encryption/Decryption flag. Allowed value: encrypt/decrypt') parser.add_argument('--encryption', help='Encryption method. Use \'aes\' for Fernet encryption or use \'otp\' for OTP encryption') parser.add_argument('--key_stretch', help='Number of iteration use when deriving Fernet key from password. Must be a number. Required if encryption mode is aes') parser.add_argument('--level', help='Number of low-bit in each pixel uses to store secret data. Must be between 1~8') parser.add_argument('--medium', help='Path to medium file') parser.add_argument('--password', help='Password for Fernet encryption or path to OTP pad') parser.add_argument('--secret', help='Path to secret file. Required in encryption mode') parser.add_argument('--result', help='Path to result file') parser.add_argument('--format', help='Format of picture with steganography. Allowed value: BMP/PNG (case insensitive). Required in encryption mode') args=parser.parse_args() # Input check. if not verify_argument(args): print(constants.HELP_MSG) return try: # Fernet encryption use args.password to derive encryption key. if args.encryption == constants.MODE_FERNET: encryptor = FernetCryptor(int(args.key_stretch)) password = args.password # OTP encryption read binary file at args.password and use it as encryption pad. else: encryptor = OTPCryptor() with open(args.password, 'rb') as f: password = f.read() modifier = PictureModifier(int(args.level)) steganography = Steganography(modifier, encryptor) with open(args.medium, 'rb') as f: medium_data = f.read() if args.mode == constants.MODE_ENCRYPT: with open(args.secret, 'rb') as f: secret_data = f.read() file_with_secret = steganography.hide_file(password, medium_data, secret_data, args.format) with open(args.result, 'wb') as f: f.write(file_with_secret) else: secret_data = steganography.get_file(password, medium_data) with open(args.result, 'wb') as f: f.write(secret_data) except FileNotFoundError as fe: print('Cannot open file, please check if file path is correct') print(str(fe)) except InvalidToken as it: print('Decryption password is incorrect.') print(str(it)) except ValueError as ve: print('An ValueError occurred. Please check value of all parameters') print(str(ve)) except TypeError as te: print('An TypeError occurred. Please check type of all parameters') print(str(te)) except Exception as e: print('An unknown error occurred.') print(str(e))
import steganosaurus.hash_utils as hash_utils from _pytest.monkeypatch import MonkeyPatch from pytest_mock import mocker from steganosaurus.fernet_cryptor import FernetCryptor from steganosaurus.otp_cryptor import OTPCryptor from steganosaurus.picture_modifier import PictureModifier from steganosaurus.steganography import Steganography from steganosaurus.steganography import \ is_positive_number as is_positive_number from steganosaurus.steganography import main as steganography_main from steganosaurus.steganography import verify_argument as steganography_verify monkeypatch = MonkeyPatch() fernet_cryptor = FernetCryptor(constants.KEY_STRETCH_ITERATION) otp_cryptor = OTPCryptor() picture_modifier = PictureModifier(2) secret_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'secret.txt') test_data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'medium.bmp') with open(secret_path, mode='rb') as file: secret_data = file.read() with open(test_data_path, mode='rb') as file: medium_data = file.read() def test_hide_file_fernet(mocker): ''' Can hide hidden data into medium. Hidden data is encrypted using Fernet encrpytion (AES-128). '''