Skip to content

Commit

Permalink
πŸ—“ Jan 3, 2025 8:53:05β€―PM
Browse files Browse the repository at this point in the history
✨ out_as_any to output in the current format the state is in
πŸ”₯ bytes_to_ascii renamed to list_to_bytes to handle byte ints
πŸ™ update str_to_hex
πŸ”₯ str_to_list -> to_list to handle bytes and bytearrays
πŸ™ xor updated to include raw key_type
πŸ€– types added/updated
πŸ§ͺ tests added/updated
  • Loading branch information
securisec committed Jan 4, 2025
1 parent 15beb6d commit 326b83b
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 27 deletions.
9 changes: 9 additions & 0 deletions chepy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,15 @@ def o(self):
return self.state.encode()
return self.state

@property
def out_as_any(self):
"""Get the final output
Returns:
Any: Final output
"""
return self.state

@property
def out(self) -> Any:
"""Get the final output
Expand Down
2 changes: 1 addition & 1 deletion chepy/core.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class ChepyCore:
def o(self): ...
@property
def out(self: ChepyCoreT) -> ChepyCoreT: ...
def out_as_str(self: ChepyCoreT) -> str: ...
def out_as_any(self: ChepyCoreT) -> str: ...
def get_by_index(self: ChepyCoreT, *indexes: int) -> ChepyCoreT: ...
def get_by_key(self: ChepyCoreT, *keys: Union[str, bytes], py_style: bool=False, split_key: Union[str, None] = '.') -> ChepyCoreT: ...
def copy_to_clipboard(self: ChepyCoreT) -> None: ...
Expand Down
74 changes: 67 additions & 7 deletions chepy/modules/dataformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,20 @@ def eval_state(self) -> DataFormatT:
return self

@ChepyDecorators.call_stack
def bytes_to_ascii(self) -> DataFormatT:
def list_to_bytes(self, ascii: bool = False) -> DataFormatT:
"""Convert bytes (array of bytes) to ascii
Args:
ascii (bool, optional): Output as ascii, by default False
Returns:
Chepy: The Chepy object.
"""
assert isinstance(self.state, list), "Data in state is not a list"
self.state = bytearray(self.state).decode()
if ascii:
self.state = bytearray(self.state).decode()
else:
self.state = bytes(bytearray(self.state))
return self

@ChepyDecorators.call_stack
Expand Down Expand Up @@ -729,13 +735,19 @@ def hex_to_str(self, ignore: bool = False) -> DataFormatT:
return self

@ChepyDecorators.call_stack
def str_to_hex(self) -> DataFormatT:
def str_to_hex(self, delimiter: Union[str, bytes] = b"") -> DataFormatT:
"""Converts a string to a hex string
Args:
delimiter (Union[str, bytes], optional): Format delimiter. Defaults to b'
Returns:
Chepy: The Chepy object.
"""
self.state = binascii.hexlify(self._convert_to_bytes())
data = self._convert_to_bytes()
delimiter = self._bytes_to_str(delimiter)
out = delimiter + delimiter.join(f"{byte:02x}" for byte in data)
self.state = out
return self

@ChepyDecorators.call_stack
Expand Down Expand Up @@ -903,7 +915,7 @@ def bytearray_to_str(
raise TypeError("State is not a bytearray")

@ChepyDecorators.call_stack
def str_to_list(self) -> DataFormatT:
def to_list(self) -> DataFormatT:
"""Convert string to list
Converts the string in state to an array of individual characyers
Expand All @@ -912,10 +924,13 @@ def str_to_list(self) -> DataFormatT:
Chepy: The Chepy object.
Examples:
>>> Chepy("abc").str_to_list().o
>>> Chepy("abc").to_list().o
["a", "b", "c"]
"""
self.state = list(self._convert_to_str())
if isinstance(self.state, (bytearray, bytes, str)):
self.state = list(self.state)
else: # pragma: no cover
raise ValueError("State is not a valid data type")
return self

@ChepyDecorators.call_stack
Expand Down Expand Up @@ -1365,6 +1380,51 @@ def from_nato(
self.state = join_by.join([d.get(p, p) for p in data])
return self

@ChepyDecorators.call_stack
def swap_values(
self, indices1: Union[str, List[int]], indices2: Union[str, List[int]]
):
"""Swaps the values in the global bytes based on indices specified in two lists.
Args:
indices1 (Union[str, List[int]]): The first indices. If string, it should be , delimited
indices2 (Union[str, List[int]]): The second indices. If string, it should be , delimited
Returns:
Chepy: The Chepy object.
"""
if isinstance(indices1, str):
indices1 = [int(x.strip()) for x in indices1.split(",")]
if isinstance(indices2, str):
indices2 = [int(x.strip()) for x in indices2.split(",")]

is_list = isinstance(self.state, list)
if is_list:
data = self.state
else:
data = bytearray(self._convert_to_bytes())

# Check if both lists are of the same length
if len(indices1) != len(indices2): # pragma: no cover
raise ValueError("The two argument lists must have the same length.")

# Swap values in the byte list
for idx1, idx2 in zip(indices1, indices2):
# Validate indices are within bounds
if (
idx1 < 0 or idx1 >= len(data) or idx2 < 0 or idx2 >= len(data)
): # pragma: no cover
raise IndexError(
f"Index out of range. Lenth of state is {len(data)}, indices provided {idx1} {idx2}"
)

# Perform the swap
data[idx1], data[idx2] = data[idx2], data[idx1]

# Convert the modified list back to bytes
self.state = data if is_list else bytes(data)
return self

@ChepyDecorators.call_stack
def swap_strings(self, by: int) -> DataFormatT:
"""Swap characters in string
Expand Down
9 changes: 5 additions & 4 deletions chepy/modules/dataformat.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ..core import ChepyCore
from typing import Any, Literal, TypeVar, Union
from typing import Any, Literal, TypeVar, Union, List

yaml: Any
DataFormatT = TypeVar('DataFormatT', bound='DataFormat')
Expand All @@ -8,7 +8,7 @@ class DataFormat(ChepyCore):
def __init__(self, *data: Any) -> None: ...
state: Any = ...
def eval_state(self: DataFormatT) -> DataFormatT: ...
def bytes_to_ascii(self: DataFormatT) -> DataFormatT: ...
def list_to_bytes(self: DataFormatT, ascii: bool=False) -> DataFormatT: ...
def list_to_str(self: DataFormatT, join_by: Union[str, bytes]=...) -> DataFormatT: ...
def str_list_to_list(self: DataFormatT) -> DataFormatT: ...
def join(self: DataFormatT, join_by: Union[str, bytes]=...) -> DataFormatT: ...
Expand Down Expand Up @@ -36,7 +36,7 @@ class DataFormat(ChepyCore):
def hex_to_int(self: DataFormatT) -> DataFormatT: ...
def hex_to_bytes(self: DataFormatT) -> DataFormatT: ...
def hex_to_str(self: DataFormatT, ignore: bool=...) -> DataFormatT: ...
def str_to_hex(self: DataFormatT) -> DataFormatT: ...
def str_to_hex(self: DataFormatT, delimiter: Union[str, bytes]=b'') -> DataFormatT: ...
def int_to_hex(self: DataFormatT) -> DataFormatT: ...
def int_to_str(self: DataFormatT) -> DataFormatT: ...
def binary_to_hex(self: DataFormatT) -> DataFormatT: ...
Expand All @@ -47,7 +47,7 @@ class DataFormat(ChepyCore):
def from_url_encoding(self: DataFormatT) -> DataFormatT: ...
def to_url_encoding(self: DataFormatT, safe: str=..., all_chars: bool=...) -> DataFormatT: ...
def bytearray_to_str(self: DataFormatT, encoding: str=..., errors: str=...) -> DataFormatT: ...
def str_to_list(self: DataFormatT) -> DataFormatT: ...
def to_list(self: DataFormatT) -> DataFormatT: ...
def str_to_dict(self: DataFormatT) -> DataFormatT: ...
def to_charcode(self: DataFormatT, join_by: str=..., base: int=...) -> DataFormatT: ...
def from_charcode(self: DataFormatT, delimiter: Union[str, None]=None, join_by: str=' ', base: int=10) -> DataFormatT: ...
Expand All @@ -68,6 +68,7 @@ class DataFormat(ChepyCore):
def trim(self: DataFormatT) -> DataFormatT: ...
def to_nato(self: DataFormatT, join_by:str=...) -> DataFormatT: ...
def from_nato(self: DataFormatT, delimiter: Union[str, None]=None, join_by: str=...) -> DataFormatT: ...
def swap_values(self: DataFormatT, indices1: Union[str, List[int]], indices2: Union[str, List[int]]) -> DataFormatT: ...
def swap_strings(self: DataFormatT, by:int) -> DataFormatT: ...
def to_string(self: DataFormatT) -> DataFormatT: ...
def stringify(self: DataFormatT, compact:bool=...) -> DataFormatT: ...
Expand Down
24 changes: 18 additions & 6 deletions chepy/modules/encryptionencoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,10 @@ def rot_8000(self):
def xor(
self,
key: str,
key_type: Literal["hex", "utf8", "base64", "decimal"] = "hex",
key_type: Literal["hex", "utf8", "base64", "decimal", "raw"] = "hex",
) -> EncryptionEncodingT:
"""XOR state with a key
"""XOR state with a key. In raw format, both the state and the key is converted
to a bytearray.
Args:
key (str): Required. The key to xor by
Expand All @@ -330,8 +331,18 @@ def xor(
"""

x = bytearray(b"")

if key_type == "raw":
_data_ba = bytearray()
_data = self._convert_to_str()
_data_ba.extend(map(ord, _data))
raw_key = bytearray()
raw_key.extend(map(ord, key))
for char, key_val in zip(_data_ba, itertools.cycle(raw_key)):
x.append(char ^ key_val)

# check if state is a list and keys are list
if isinstance(self.state, bytearray) and isinstance(key, bytearray):
elif isinstance(self.state, bytearray) and isinstance(key, bytearray):
for char, key_val in zip(self.state, itertools.cycle(key)):
x.append(char ^ key_val)

Expand Down Expand Up @@ -1216,17 +1227,18 @@ def atbash(self) -> EncryptionEncodingT:
>>> Chepy("secret").atbash_encode().o
"HVXIVG"
"""
key = 'ZYXWVUTSRQPONMLKJIHGFEDCBA'
# Reference https://github.com/jameslyons/pycipher/blob/master/pycipher/atbash.py
key = "ZYXWVUTSRQPONMLKJIHGFEDCBA"
data = self._convert_to_str()
ret = ''
ret = ""
arr = Ciphers.ATBASH
for c in data:
if c.isalpha():
if c.islower():
ret += key[arr[c.upper()]].lower()
else:
ret += key[arr[c]]
else:
else:
ret += c
self.state = ret
# self.state = pycipher.Atbash().encipher(self._convert_to_str(), keep_punct=True)
Expand Down
2 changes: 1 addition & 1 deletion chepy/modules/encryptionencoding.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class EncryptionEncoding(ChepyCore):
def rot_47(self: EncryptionEncodingT, amount: int=47) -> EncryptionEncodingT: ...
def rot_47_bruteforce(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def rot_8000(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def xor(self: EncryptionEncodingT, key: Union[str, bytearray], key_type: Literal['hex', 'utf8', 'base64', 'decimal']='hex') -> EncryptionEncodingT: ...
def xor(self: EncryptionEncodingT, key: Union[str, bytearray], key_type: Literal['hex', 'utf8', 'base64', 'decimal', 'raw']='hex') -> EncryptionEncodingT: ...
def xor_bruteforce(self: EncryptionEncodingT, length: int=..., crib: Union[str, bytes, None]=...) -> EncryptionEncodingT: ...
def jwt_decode(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
def jwt_verify(self: EncryptionEncodingT, secret: str, algorithm: list=...) -> EncryptionEncodingT: ...
Expand Down
3 changes: 2 additions & 1 deletion tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ def test_fork():
def test_save_buffer():
c = Chepy("A").save_buffer(0).str_to_hex().save_buffer(1)
assert c.buffers[0] == "A"
assert c.buffers[1] == b"41"
assert c.buffers[1] == "41"
assert Chepy('ffffd64c').from_hex().swap_endianness().str_to_hex('\\x').out_as_any == '\\x4c\\xd6\\xff\\xff'


def test_load_buffer():
Expand Down
23 changes: 16 additions & 7 deletions tests/test_dataformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ def test_base16_decode():
assert Chepy("74657374").from_base16().o == b"test"


def test_bytes_to_ascii():
assert Chepy([116, 101, 115, 116]).bytes_to_ascii().o == b"test"
def test_list_to_bytes():
assert Chepy([116, 101, 115, 116]).list_to_bytes().o == b"test"
assert Chepy([116, 101, 115, 116]).list_to_bytes(True).o == b"test"


def test_dict_to_json():
Expand Down Expand Up @@ -225,7 +226,7 @@ def test_from_url_encoding():
)


def test_to_list():
def test_str_list_to_list():
assert Chepy("[1,2,'lol', true]").str_list_to_list().o == [1, 2, "lol", True]


Expand All @@ -241,7 +242,7 @@ def test_join():

def test_to_int():
assert Chepy("1").to_int().o == 1
assert Chepy('AQAB').from_base64().to_int().o == 65537
assert Chepy("AQAB").from_base64().to_int().o == 65537


def test_normalize_hex():
Expand Down Expand Up @@ -270,8 +271,10 @@ def test_from_bytes():
)


def test_str_to_list():
assert Chepy("abc").str_to_list().o == ["a", "b", "c"]
def test_to_list():
assert Chepy("abc").to_list().o == ["a", "b", "c"]
assert Chepy(bytearray(b"abc")).to_list().o == [97, 98, 99]
assert Chepy(b"abc").to_list().o == [97, 98, 99]


def test_str_to_dict():
Expand Down Expand Up @@ -444,6 +447,12 @@ def test_nato_convert():
)


def test_swap_values():
assert Chepy("abcd").swap_values("0", "3").o == b"dbca"
assert Chepy("abcd").swap_values([0, 1], [3, 2]).o == b"dcba"
assert Chepy([1, 2, 3]).swap_values("0", "2").o == [3, 2, 1]


def test_swap_strings():
assert Chepy("oY u").swap_strings(2).o == b"You "

Expand All @@ -463,7 +472,7 @@ def test_stringify():
def test_select():
assert Chepy("abcd").select(0, 2).o == b"ab"
assert Chepy("abcd").select(2).o == b"cd"
assert Chepy("abcd").select('(b|c)').o == b"bcd"
assert Chepy("abcd").select("(b|c)").o == b"bcd"


def test_length():
Expand Down
5 changes: 5 additions & 0 deletions tests/test_encryptionencoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ def test_xor_hex():
assert Chepy("some data").xor("5544", "hex").o == b"&+8!u 404"


def test_xor_raw():
assert Chepy("ab\xffcd").xor("\x31\x03", "raw").o == b"Pa\xce`U"


def test_xor_binary():
assert (
Chepy("./tests/files/hello")
Expand Down Expand Up @@ -691,6 +695,7 @@ def test_affine_decode():

def test_atbash_encode():
assert Chepy("AbCd1.2").atbash().o == b"ZyXw1.2"
assert Chepy("zbCc;oo?|c;oAp9P%").atbash().atbash().o == b"zbCc;oo?|c;oAp9P%"


def test_to_morse_code():
Expand Down

0 comments on commit 326b83b

Please sign in to comment.