diff --git a/puzzle_generator/configurators/common.py b/puzzle_generator/configurators/common.py deleted file mode 100644 index cf46bb4..0000000 --- a/puzzle_generator/configurators/common.py +++ /dev/null @@ -1,8 +0,0 @@ -import hashlib - - -def get_hasher_name(in_hasher) -> str: - return "hashlib." + in_hasher().name - - -DefaultHasher = hashlib.sha512 diff --git a/puzzle_generator/configurators/simple/common.py b/puzzle_generator/configurators/simple/common.py index c28d957..43a86f2 100644 --- a/puzzle_generator/configurators/simple/common.py +++ b/puzzle_generator/configurators/simple/common.py @@ -1,23 +1,47 @@ import secrets -from ... import bytestr_utils as bu +from ...encryption_algorithms.simple import common +from ... import bytestr_utils as bsu +from ... import bytes_utils as bu +from ... import puzzle_data_encryption as pde +from ... import run_puzzle as rp MODULES = ["hashlib", "base64", "sys", "typing"] +OBJECTS = [ + common.hash_bytes, + common.derive_key, + common.split_data_and_signature, + common.digest_size, + common.xor_bytes, + bsu.bytestr_to_bytes, + bu.bytes_to_int, + bu.split, + pde.decrypt_data, + rp.run_puzzle, +] + def scrypt_params(**kwargs): - print(kwargs) + in_params = kwargs.get("scrypt_params", {}) default = {"salt": secrets.token_bytes(16), "n": 2**14, "r": 8, "p": 1} - if "maxmem" in kwargs: - default["maxmem"] = kwargs["maxmem"] - res = {_k: kwargs.get(_k, _v) for _k, _v in default.items()} + if "maxmem" in in_params: + default["maxmem"] = in_params["maxmem"] + res = {_k: in_params.get(_k, _v) for _k, _v in default.items()} if "maxmem" not in res: res["maxmem"] = (128 + 100) * res["n"] * res["r"] * res["p"] return res +def signature_params(**kwargs): + res = kwargs.get("signature_params", {"hasher": {"name": "sha512"}, "digest": {}}) + if "digest" not in res: + res["digest"] = {} + return res + + def scrypt_params_to_code_str(**kwargs) -> str: pieces = [f'"{_k}":{_v}' for _k, _v in kwargs.items() if _k != "salt"] - salt_str = '"' + bu.bytes_to_bytestr(kwargs["salt"]) + '"' + salt_str = '"' + bsu.bytes_to_bytestr(kwargs["salt"]) + '"' pieces.append(f'"salt":bytestr_to_bytes({salt_str})') return f"_SCRYPT_PARAMS = {{{','.join(pieces)}}}" diff --git a/puzzle_generator/configurators/simple/simple.py b/puzzle_generator/configurators/simple/simple.py index 36fff16..2cf66c4 100644 --- a/puzzle_generator/configurators/simple/simple.py +++ b/puzzle_generator/configurators/simple/simple.py @@ -1,47 +1,29 @@ import typing -from ... import bytestr_utils as bu -from ...encryption_algorithms.simple import common from ...encryption_algorithms.simple import simple as se -from ...puzzle_data_encryption import decrypt_data -from .. import common as cc -from .common import MODULES, scrypt_params, scrypt_params_to_code_str -from ...run_puzzle import run_puzzle -from ...bytes_utils import bytes_to_int, split +from . import common as csc class Simple: def __init__(self, **kwargs): - self._scrypt_params = scrypt_params(**kwargs) - self._signature_hasher = kwargs.get("signature_hasher", cc.DefaultHasher) + self._scrypt_params = csc.scrypt_params(**kwargs) + self._signature_params = csc.signature_params(**kwargs) def get_modules(self) -> typing.List[str]: - return MODULES + return csc.MODULES def get_encrypt(self): - return se.get_encrypt(self._signature_hasher, self._scrypt_params) + return se.get_encrypt(self._scrypt_params, self._signature_params) def get_needed_objects(self): - return [ - common.hash_bytes, - common.split_data_and_signature, - common.derive_key, - common.xor_bytes, - bu.bytestr_to_bytes, + return csc.OBJECTS + [ se.get_decrypt, - bytes_to_int, - split, - decrypt_data, - run_puzzle, ] def get_constants_str( self, ) -> str: - _scrypt_params = scrypt_params_to_code_str(**self._scrypt_params) - decrypt: str = ( - "_DECRYPT = get_decrypt(" - f"{cc.get_hasher_name(self._signature_hasher)}, " - "_SCRYPT_PARAMS)" - ) - return "\n".join([_scrypt_params, decrypt]) + _scrypt_params = csc.scrypt_params_to_code_str(**self._scrypt_params) + _signature_params = "_SIGNATURE_PARAMS = " + repr(self._signature_params) + decrypt: str = "_DECRYPT = get_decrypt(_SCRYPT_PARAMS, _SIGNATURE_PARAMS)" + return "\n".join([_scrypt_params, _signature_params, decrypt]) diff --git a/puzzle_generator/configurators/simple/spiced.py b/puzzle_generator/configurators/simple/spiced.py index aa319e6..4c78c98 100644 --- a/puzzle_generator/configurators/simple/spiced.py +++ b/puzzle_generator/configurators/simple/spiced.py @@ -2,13 +2,8 @@ import typing from ... import bytestr_utils as bu -from ...encryption_algorithms.simple import common from ...encryption_algorithms.simple import spiced as sse -from ...puzzle_data_encryption import decrypt_data -from .. import common as cc -from .common import MODULES, scrypt_params, scrypt_params_to_code_str -from ...run_puzzle import run_puzzle -from ...bytes_utils import bytes_to_int, split +from . import common as csc def _get_some_spices(): @@ -26,34 +21,25 @@ def _list_of_bytes_to_codestr(in_list: typing.List[bytes]) -> str: class Spiced: def __init__(self, **kwargs): - self._scrypt_params = scrypt_params(**kwargs) - self._signature_hasher = kwargs.get("signature_hasher", cc.DefaultHasher) + self._scrypt_params = csc.scrypt_params(**kwargs) + self._signature_params = csc.signature_params(**kwargs) self._proc_spices = kwargs.get("proc_spices", _get_some_spices()) self._signature_spices = kwargs.get("signature_spices", _get_some_spices()) def get_modules(self) -> typing.List[str]: - return MODULES + return csc.MODULES def get_encrypt(self): return sse.get_encrypt( - self._signature_hasher, self._proc_spices, self._signature_spices, self._scrypt_params, + self._signature_params, ) def get_needed_objects(self): - return [ - common.hash_bytes, - common.derive_key, - common.split_data_and_signature, - common.xor_bytes, - bu.bytestr_to_bytes, + return csc.OBJECTS + [ sse.get_decrypt, - bytes_to_int, - split, - decrypt_data, - run_puzzle, ] def get_constants_str( @@ -65,12 +51,14 @@ def get_constants_str( signature_spices: str = ( f"_SIGNATURE_SPICES = {_list_of_bytes_to_codestr(self._signature_spices)}" ) - _scrypt_params = scrypt_params_to_code_str(**self._scrypt_params) + _scrypt_params = csc.scrypt_params_to_code_str(**self._scrypt_params) + _signature_params = "_SIGNATURE_PARAMS = " + repr(self._signature_params) decrypt: str = ( "_DECRYPT = get_decrypt(" - f"{cc.get_hasher_name(self._signature_hasher)}, " "_PROC_SPICES, " "_SIGNATURE_SPICES, " - "_SCRYPT_PARAMS)" + "_SCRYPT_PARAMS, _SIGNATURE_PARAMS)" + ) + return "\n".join( + [proc_spices, signature_spices, _scrypt_params, _signature_params, decrypt] ) - return "\n".join([proc_spices, signature_spices, _scrypt_params, decrypt]) diff --git a/puzzle_generator/encryption_algorithms/simple/common.py b/puzzle_generator/encryption_algorithms/simple/common.py index 0061127..db15348 100644 --- a/puzzle_generator/encryption_algorithms/simple/common.py +++ b/puzzle_generator/encryption_algorithms/simple/common.py @@ -2,8 +2,19 @@ import typing -def hash_bytes(in_bytes: bytes, in_hasher) -> bytes: - return in_hasher(in_bytes).digest() +def digest_size(params) -> int: + hasher = hashlib.new(**params["hasher"]) + res = hasher.digest_size + if res > 0: + return res + return params["digest"]["length"] + + +def hash_bytes(in_bytes: bytes, params) -> bytes: + hasher = hashlib.new(**params["hasher"]) + hasher.update(in_bytes) + res = hasher.digest(**params["digest"]) + return res def derive_key(**kwargs): diff --git a/puzzle_generator/encryption_algorithms/simple/simple.py b/puzzle_generator/encryption_algorithms/simple/simple.py index 789efee..1bd7162 100644 --- a/puzzle_generator/encryption_algorithms/simple/simple.py +++ b/puzzle_generator/encryption_algorithms/simple/simple.py @@ -5,16 +5,17 @@ derive_key, xor_bytes, hash_bytes, + digest_size, merge_data_and_signature, split_data_and_signature, ) def get_encrypt( - signature_hasher, scrypt_params + scrypt_params, signature_params ) -> typing.Callable[[bytes, bytes], bytes]: def _encrypt(in_bytes: bytes, in_pass: bytes) -> bytes: - signature = hash_bytes(in_bytes, signature_hasher) + signature = hash_bytes(in_bytes, signature_params) merged = merge_data_and_signature(in_bytes, signature) key = derive_key(password=in_pass, dklen=len(merged), **scrypt_params) return xor_bytes(merged, key) @@ -23,16 +24,16 @@ def _encrypt(in_bytes: bytes, in_pass: bytes) -> bytes: def get_decrypt( - signature_hasher, scrypt_params + scrypt_params, signature_params ) -> typing.Callable[[bytes, bytes], bytes | None]: def _decrypt(in_bytes: bytes, in_pass: bytes) -> bytes | None: key = derive_key(password=in_pass, dklen=len(in_bytes), **scrypt_params) data = xor_bytes(in_bytes, key) decrypted, signature = split_data_and_signature( - data, signature_hasher().digest_size + data, digest_size(signature_params) ) - if hash_bytes(decrypted, signature_hasher) == signature: + if hash_bytes(decrypted, signature_params) == signature: return decrypted return None diff --git a/puzzle_generator/encryption_algorithms/simple/spiced.py b/puzzle_generator/encryption_algorithms/simple/spiced.py index aebd36e..baad052 100644 --- a/puzzle_generator/encryption_algorithms/simple/spiced.py +++ b/puzzle_generator/encryption_algorithms/simple/spiced.py @@ -5,23 +5,24 @@ derive_key, xor_bytes, hash_bytes, + digest_size, merge_data_and_signature, split_data_and_signature, ) def get_encrypt( - signature_hasher, proc_spices: typing.List[bytes], signature_spices: typing.List[bytes], scrypt_params, + signature_params, ) -> typing.Callable[[bytes, bytes], bytes]: assert proc_spices # nosec B101 assert signature_spices # nosec B101 def _encrypt(in_bytes: bytes, in_pass: bytes) -> bytes: signature_spice = secrets.choice(signature_spices) - signature = hash_bytes(in_bytes + signature_spice, signature_hasher) + signature = hash_bytes(in_bytes + signature_spice, signature_params) merged = merge_data_and_signature(in_bytes, signature) proc_spice = secrets.choice(proc_spices) key = derive_key( @@ -33,10 +34,10 @@ def _encrypt(in_bytes: bytes, in_pass: bytes) -> bytes: def get_decrypt( - signature_hasher, proc_spices: typing.List[bytes], signature_spices: typing.List[bytes], scrypt_params, + signature_params, ) -> typing.Callable[[bytes, bytes], bytes | None]: assert proc_spices # nosec B101 assert signature_spices # nosec B101 @@ -48,11 +49,11 @@ def _decrypt(in_bytes: bytes, in_pass: bytes) -> bytes | None: ) data = xor_bytes(in_bytes, key) decrypted, signature = split_data_and_signature( - data, signature_hasher().digest_size + data, digest_size(signature_params) ) if any( - hash_bytes(decrypted + _, signature_hasher) == signature + hash_bytes(decrypted + _, signature_params) == signature for _ in signature_spices ): return decrypted diff --git a/pyproject.toml b/pyproject.toml index 5a07aaf..92d7fad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "puzzle-generator" -version = "0.9.0" +version = "0.10.0" description = "Generates python code representing a puzzle" authors = ["piotr.idzik "] readme = "./puzzle_generator/README.md" diff --git a/tests/encryption_algorithms/test_common.py b/tests/encryption_algorithms/test_common.py new file mode 100644 index 0000000..3b345a1 --- /dev/null +++ b/tests/encryption_algorithms/test_common.py @@ -0,0 +1,10 @@ +import pytest + +import puzzle_generator.encryption_algorithms.simple.common as eac +from .. import utils + + +@pytest.mark.parametrize("in_hash_params", utils.SOME_SIGNATURE_PARAMS) +def test_digest_size(in_hash_params): + some_hash = eac.hash_bytes(b"some_msg", in_hash_params) + assert eac.digest_size(in_hash_params) == len(some_hash) diff --git a/tests/encryption_algorithms/test_encryption_algorithms.py b/tests/encryption_algorithms/test_encryption_algorithms.py index e21a9a6..ce7fbfd 100644 --- a/tests/encryption_algorithms/test_encryption_algorithms.py +++ b/tests/encryption_algorithms/test_encryption_algorithms.py @@ -1,41 +1,11 @@ -import hashlib -import itertools import pytest from .. import utils -_SOME_HASHES = [ - hashlib.md5, - hashlib.sha512, - hashlib.sha3_512, - hashlib.blake2s, -] - -_SOME_SCRYPT_PARAMS = [ - {"salt": b"some_bad_salt", "n": 8, "r": 10, "p": 1}, - {"salt": b"some_other_bad_salt", "n": 16, "r": 20, "p": 1}, -] - -_PROC_SPICES = [b"a", b"bb", b"ccc", b"dddd"] -_SIGNATURE_SPICES = [b"XXX", b"YY", b"Z"] - - @pytest.mark.parametrize("in_bytes", utils.BYTES_LIST) @pytest.mark.parametrize("in_pass", utils.BYTES_LIST) -@pytest.mark.parametrize( - ("encrypt", "decrypt"), - [ - utils.get_simple_encrypt_decrypt_pair(hash, scrypt_params) - for hash, scrypt_params in itertools.product(_SOME_HASHES, _SOME_SCRYPT_PARAMS) - ] - + [ - utils.get_spiced_simple_encrypt_decrypt_pair( - hash, _PROC_SPICES, _SIGNATURE_SPICES, scrypt_params - ) - for hash, scrypt_params in itertools.product(_SOME_HASHES, _SOME_SCRYPT_PARAMS) - ], -) +@pytest.mark.parametrize(("encrypt", "decrypt"), utils.ENCRYPT_DECRYPT_PAIRS) def test_encryption_decryption(in_bytes, in_pass, encrypt, decrypt): encrypted = encrypt(in_bytes, in_pass) diff --git a/tests/test_create_puzzle.py b/tests/test_create_puzzle.py index b4bf592..9658319 100644 --- a/tests/test_create_puzzle.py +++ b/tests/test_create_puzzle.py @@ -1,8 +1,6 @@ import pathlib -import hashlib import subprocess # nosec B404 import typing -import itertools import collections import black import pytest @@ -85,23 +83,42 @@ def _run_puzzle_str( _CONFIGURATIONS = [ {}, - {"signature_hasher": hashlib.sha3_224, "n": 2**2}, - {"signature_hasher": hashlib.blake2b, "salt": b"very_bad_salt"}, - {"encryption": "simple", "signature_hasher": hashlib.blake2b}, + {"encryption": "simple"}, + {"encryption": "spiced"}, + {"scrypt_params": {"n": 2**4, "p": 2, "maxmem": 200000}}, { - "encryption": "spiced", - "signature_hasher": hashlib.sha3_224, - "proc_spices": [b"\0"], - "n": 2**3, - "maxmem": 0, + "signature_params": { + "hasher": {"name": "sha3_384"}, + } + }, + {"encryption": "simple", "scrypt_params": {"n": 2**3, "maxmem": 100000}}, + { + "encryption": "simple", + "signature_params": {"hasher": {"name": "blake2b", "digest_size": 17}}, + }, + { + "encryption": "simple", + "signature_params": { + "hasher": {"name": "shake256", "data": b"init"}, + "digest": {"length": 91}, + }, }, { "encryption": "spiced", - "signature_hasher": hashlib.sha224, "proc_spices": [b"\1"], - "signature_spices": [b"\2"], - "salt": b"even_worse_salt!", - "r": 16, + "signature_params": { + "hasher": {"name": "shake128"}, + "digest": {"length": 5}, + }, + }, + { + "encryption": "spiced", + "signature_spices": [b"\0", b"\10"], + "signature_params": { + "hasher": {"name": "sha3_256", "data": b"00000"}, + "digest": {}, + }, + "scrypt_params": {"n": 2**5, "r": 16, "salt": b"testSalt!!!"}, }, ] @@ -146,30 +163,6 @@ def _input_simulator() -> str: return _input_simulator -_SOME_HASHES = [ - hashlib.sha3_384, - hashlib.sha3_256, -] - -_PROC_SPICES = [b"11", b"22"] -_SIGNATURE_SPICES = [b"27", b"07", b"2024"] - -_SOME_SCRYPT_PARAMS = [ - {"salt": b"salt_1", "n": 8, "r": 5, "p": 1}, - {"salt": b"salt_two", "n": 4, "r": 2, "p": 1}, -] - -_ENCRYPT_DECRYPT_PAIRS = [ - utils.get_simple_encrypt_decrypt_pair(hash, scrypt_params) - for hash, scrypt_params in itertools.product(_SOME_HASHES, _SOME_SCRYPT_PARAMS) -] + [ - utils.get_spiced_simple_encrypt_decrypt_pair( - hash, _PROC_SPICES, _SIGNATURE_SPICES, scrypt_params - ) - for hash, scrypt_params in itertools.product(_SOME_HASHES, _SOME_SCRYPT_PARAMS) -] - - def _get_input_simulator(answers: typing.List[str]) -> typing.Callable[[], str]: cur_input = 0 @@ -182,7 +175,7 @@ def _input_simulator() -> str: return _input_simulator -@pytest.mark.parametrize(("encrypt", "decrypt"), _ENCRYPT_DECRYPT_PAIRS) +@pytest.mark.parametrize(("encrypt", "decrypt"), utils.ENCRYPT_DECRYPT_PAIRS) def test_run_puzzle_all_good_answers(capsys, puzzle_tc, encrypt, decrypt) -> None: encrypted_puzzle = pde.encrypt_data( cp.question_answer_list_to_dict(puzzle_tc.qa_list), encrypt @@ -194,7 +187,7 @@ def test_run_puzzle_all_good_answers(capsys, puzzle_tc, encrypt, decrypt) -> Non assert captured.out == puzzle_tc.correct.output -@pytest.mark.parametrize(("encrypt", "decrypt"), _ENCRYPT_DECRYPT_PAIRS) +@pytest.mark.parametrize(("encrypt", "decrypt"), utils.ENCRYPT_DECRYPT_PAIRS) def test_run_puzzle_wrong_answers(capsys, puzzle_tc, encrypt, decrypt) -> None: for cur_wrong in puzzle_tc.wrong: encrypted_puzzle = pde.encrypt_data( diff --git a/tests/test_puzzle_data_encryption.py b/tests/test_puzzle_data_encryption.py index 16a4d88..8a39593 100644 --- a/tests/test_puzzle_data_encryption.py +++ b/tests/test_puzzle_data_encryption.py @@ -1,25 +1,8 @@ -import hashlib -import itertools import pytest import puzzle_generator.puzzle_data_encryption as pde from . import utils -_SOME_HASHES = [ - hashlib.sha1, - hashlib.sha256, -] - - -_PROC_SPICES = [b"a"] -_SIGNATURE_SPICES = [b"1", b"12"] - - -_SOME_SCRYPT_PARAMS = [ - {"salt": b"some_bad_salt_0", "n": 8, "r": 5, "p": 1}, - {"salt": b"some_other_bad_salt_1", "n": 4, "r": 2, "p": 1}, -] - @pytest.mark.parametrize( "in_puzzle", @@ -63,19 +46,7 @@ }, ], ) -@pytest.mark.parametrize( - ("encrypt", "decrypt"), - [ - utils.get_simple_encrypt_decrypt_pair(hash, scrypt_params) - for hash, scrypt_params in itertools.product(_SOME_HASHES, _SOME_SCRYPT_PARAMS) - ] - + [ - utils.get_spiced_simple_encrypt_decrypt_pair( - hash, _PROC_SPICES, _SIGNATURE_SPICES, scrypt_params - ) - for hash, scrypt_params in itertools.product(_SOME_HASHES, _SOME_SCRYPT_PARAMS) - ], -) +@pytest.mark.parametrize(("encrypt", "decrypt"), utils.ENCRYPT_DECRYPT_PAIRS) def test_pde(in_puzzle, encrypt, decrypt): encrypted_puzzle = pde.encrypt_data(in_puzzle, encrypt) tmp_puzzle_data = in_puzzle diff --git a/tests/utils.py b/tests/utils.py index 7852198..133183c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,17 +1,10 @@ import string +import itertools import puzzle_generator.encryption_algorithms.simple.simple as se import puzzle_generator.encryption_algorithms.simple.spiced as sse -def get_simple_encrypt_decrypt_pair(*args): - return se.get_encrypt(*args), se.get_decrypt(*args) - - -def get_spiced_simple_encrypt_decrypt_pair(*args): - return sse.get_encrypt(*args), sse.get_decrypt(*args) - - STRS = [ "", "some_STR?!", @@ -24,3 +17,50 @@ def get_spiced_simple_encrypt_decrypt_pair(*args): ] BYTES_LIST = [_.encode() for _ in STRS] + [b"\0", b"1235"] + +SOME_SCRYPT_PARAMS = [ + {"salt": b"some_bad_salt", "n": 8, "r": 10, "p": 1}, + {"salt": b"some_other_bad_salt", "n": 16, "r": 20, "p": 1}, +] + +PROC_SPICES = [b"a", b"bb", b"ccc", b"dddd"] +SIGNATURE_SPICES = [b"XXX", b"YY", b"Z"] + +SOME_SIGNATURE_PARAMS = [ + {"hasher": {"name": "sha512"}, "digest": {}}, + {"hasher": {"name": "sha3_512", "data": b"initial_data"}, "digest": {}}, + {"hasher": {"name": "blake2b", "digest_size": 63}, "digest": {}}, + {"hasher": {"name": "blake2s", "digest_size": 10, "person": b"tmp?"}, "digest": {}}, + {"hasher": {"name": "shake_256"}, "digest": {"length": 999}}, + { + "hasher": { + "name": "shake_128", + "data": b"some_initial_data", + "usedforsecurity": False, + }, + "digest": {"length": 10}, + }, +] + + +def _get_simple_encrypt_decrypt_pair(*args): + return se.get_encrypt(*args), se.get_decrypt(*args) + + +def _get_spiced_simple_encrypt_decrypt_pair(*args): + return sse.get_encrypt(*args), sse.get_decrypt(*args) + + +ENCRYPT_DECRYPT_PAIRS = [ + _get_simple_encrypt_decrypt_pair(scrypt_params, signature_params) + for scrypt_params, signature_params in itertools.product( + SOME_SCRYPT_PARAMS, SOME_SIGNATURE_PARAMS + ) +] + [ + _get_spiced_simple_encrypt_decrypt_pair( + PROC_SPICES, SIGNATURE_SPICES, scrypt_params, signature_params + ) + for scrypt_params, signature_params in itertools.product( + SOME_SCRYPT_PARAMS, SOME_SIGNATURE_PARAMS + ) +]