Code cleanup (#510)

* Code cleanup

* Rename base58.py to base58_bitcoin.py

* Bump CipheyCore and CipheyDists

* Update __init__.py

* Fix import error

* Fix error

* Fix decoder error

* Fix import error

* Fix type error

* Fix bytes error

* Fix attribute error

* Fix XandY cracker

* Fix brandon test

* Fix leetspeak test

* Update and rename leet.py to leetspeak.py

* Update and rename morse.py to morse_code.py

* Update __init__.py
This commit is contained in:
SkeletalDemise 2020-10-26 12:11:18 -07:00 committed by GitHub
parent 3721f91ce0
commit 5544e945c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 1072 additions and 1108 deletions

View File

@ -1,7 +1,2 @@
from . import common
from . import iface
from . import basemods
from . import basemods, common, iface
from .ciphey import decrypt

View File

@ -1,3 +1 @@
from . import quorum, regex, brandon, format, human, any
from . import ezcheck
from . import any, brandon, ezcheck, format, human, quorum, regex

View File

@ -1,6 +1,6 @@
from typing import Optional, Dict
from typing import Dict, Optional
from ciphey.iface import registry, PolymorphicChecker, Config, ParamSpec
from ciphey.iface import Config, ParamSpec, PolymorphicChecker, registry
@registry.register

View File

@ -1,13 +1,13 @@
"""
© Brandon Skerritt
Github: brandonskerritt
Class to determine whether somethine is English or not.
Class to determine whether something is English or not.
1. Calculate the Chi Squared score of a sentence
2. If the score is significantly lower than the average score, it _might_ be English
2.1. If the score _might_ be English, then take the text and compare it to the sorted dictionary
@ -15,7 +15,7 @@ Class to determine whether somethine is English or not.
It creates a percentage of "How much of this text is in the dictionary?"
The dictionary contains:
* 20,000 most common US words
* 10,000 most common UK words (there's no repition between the two)
* 10,000 most common UK words (there's no repetition between the two)
* The top 10,000 passwords
If the word "Looks like" English (chi-squared) and if it contains English words, we can conclude it is
very likely English. The alternative is doing the dictionary thing but with an entire 479k word dictionary (slower)
@ -31,7 +31,7 @@ How to add a language:
* Download your desired dictionary. Try to make it the most popular words, for example. Place this file into this
folder with languagename.txt
As an example, this comes built in with english.txt
Find the statistical frequency of each letter in that language.
Find the statistical frequency of each letter in that language.
For English, we have:
self.languages = {
"English":
@ -45,23 +45,17 @@ self.languages = {
[0.0855, 0.0160, 0.0316, 0.0387, 0.1210,0.0218, 0.0209, 0.0496, 0.0733, 0.0022,0.0081, 0.0421, 0.0253, 0.0717,
0.0747,0.0207, 0.0010, 0.0633, 0.0673, 0.0894,0.0268, 0.0106, 0.0183, 0.0019, 0.0172,0.0011]
"German": [0.0973]
}
}
In alphabetical order
And you're.... Done! Make sure the name of the two match up
"""
from typing import Dict, Set, Optional, Any
import ciphey
from string import punctuation
from loguru import logger
import string
import os
import sys
from loguru import logger
from math import ceil
from typing import Dict, Optional
from ciphey.iface import T, registry
from loguru import logger
from ciphey.iface import Checker, Config, ParamSpec, T, registry
sys.path.append("..")
try:
@ -71,7 +65,7 @@ except ModuleNotFoundError:
@registry.register
class Brandon(ciphey.iface.Checker[str]):
class Brandon(Checker[str]):
"""
Class designed to confirm whether something is **language** based on how many words of **language** appears
Call confirmLanguage(text, language)
@ -104,7 +98,7 @@ class Brandon(ciphey.iface.Checker[str]):
"""
# makes the text unique words and readable
text = text.lower()
text = self.mh.strip_puncuation(text)
text = self.mh.strip_punctuation(text)
text = text.split(" ")
text = filter(lambda x: len(x) > 2, text)
text = set(text)
@ -112,11 +106,11 @@ class Brandon(ciphey.iface.Checker[str]):
x = []
for word in text:
# poor mans lemisation
# poor mans lemmatisation
# removes 's from the dict'
if word.endswith("'s"):
x.append(word[0:-2])
text = self.mh.strip_puncuation(x)
text = self.mh.strip_punctuation(x)
# turns it all into lowercase and as a set
complete = set([word.lower() for word in x])
@ -125,20 +119,20 @@ class Brandon(ciphey.iface.Checker[str]):
def checker(self, text: str, threshold: float, text_length: int, var: set) -> bool:
"""Given text determine if it passes checker
The checker uses the vairable passed to it. I.E. Stopwords list, 1k words, dictionary
The checker uses the variable passed to it. I.E. Stopwords list, 1k words, dictionary
Args:
text -> The text to check
threshold -> at what point do we return True? The percentage of text that is in var before we return True
text_length -> the length of the text
var -> the variable we are checking against. Stopwords list, 1k words list, dictionray list.
var -> the variable we are checking against. Stopwords list, 1k words list, dictionary list.
Returns:
boolean -> True for it passes the test, False for it fails the test."""
if text is None:
logger.trace(f"Checker's text is None, so returning False")
logger.trace("Checker's text is None, so returning False")
return False
if var is None:
logger.trace(f"Checker's input var is None, so returning False")
logger.trace("Checker's input var is None, so returning False")
return False
percent = ceil(text_length * threshold)
@ -179,7 +173,7 @@ class Brandon(ciphey.iface.Checker[str]):
)
return False
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
# Suppresses warning
super().__init__(config)
self.mh = mh.mathsHelper()
@ -216,7 +210,6 @@ class Brandon(ciphey.iface.Checker[str]):
length_text = len(text)
what_to_use = {}
# this code decides what checker / threshold to use
@ -262,7 +255,7 @@ class Brandon(ciphey.iface.Checker[str]):
If the length of the text is over the maximum sentence length, use the last checker / threshold
Otherwise, traverse the keys backwards until we find a key range that does not fit.
So we traverse backwards and see if the sentence length is between current - 1 and current
In this way, we find the absolute lowest checker / percentage threshold.
In this way, we find the absolute lowest checker / percentage threshold.
We traverse backwards because if the text is longer than the max sentence length, we already know.
In total, the keys are only 5 items long or so. It is not expensive to move backwards, nor is it expensive to move forwards.
@ -285,29 +278,29 @@ class Brandon(ciphey.iface.Checker[str]):
return what_to_use
@staticmethod
def getParams() -> Optional[Dict[str, ciphey.iface.ParamSpec]]:
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"top1000": ciphey.iface.ParamSpec(
"top1000": ParamSpec(
desc="A wordlist of the top 1000 words",
req=False,
default="cipheydists::list::english1000",
),
"wordlist": ciphey.iface.ParamSpec(
"wordlist": ParamSpec(
desc="A wordlist of all the words",
req=False,
default="cipheydists::list::english",
),
"stopwords": ciphey.iface.ParamSpec(
"stopwords": ParamSpec(
desc="A wordlist of StopWords",
req=False,
default="cipheydists::list::englishStopWords",
),
"threshold": ciphey.iface.ParamSpec(
"threshold": ParamSpec(
desc="The minimum proportion (between 0 and 1) that must be in the dictionary",
req=False,
default=0.45,
),
"phases": ciphey.iface.ParamSpec(
"phases": ParamSpec(
desc="Language-specific phase thresholds",
req=False,
default="cipheydists::brandon::english",

View File

@ -1,22 +1,19 @@
from typing import Optional, Dict, List
from typing import Dict, Optional
from loguru import logger
from . import brandon
from ciphey.iface import registry, Checker, ParamSpec, T, Config
import json
from ciphey.iface import Checker, Config, ParamSpec, T, registry
@registry.register
class Enttropy(Checker[str]):
class Entropy(Checker[str]):
"""
Uses entropy to determine plaintext
Uses entropy to determine plaintext
"""
def check(self, text: T) -> Optional[str]:
logger.trace(f"Trying entropy checker")
logger.trace("Trying entropy checker")
pass
def getExpectedRuntime(self, text: T) -> float:

View File

@ -1,29 +1,33 @@
from typing import Optional, Dict, List
from typing import Dict, List, Optional
from . import brandon
from ciphey.iface import registry, Checker, ParamSpec, T, Config
from ciphey.iface import Checker, Config, ParamSpec, T, registry
from .regex import RegexList
from .brandon import Brandon
from .format import JsonChecker
from .human import HumanChecker
from .regex import RegexList
@registry.register
class EzCheck(Checker[str]):
"""
This object is effectively a prebuilt quorum (with requirement 1) of common patterns, followed by a human check
This object is effectively a prebuilt quorum (with requirement 1) of common patterns, followed by a human check
"""
def check(self, text: str) -> Optional[str]:
for checker in self.checkers:
res = checker.check(text)
if res is not None and (self.decider is None or self.decider.check(text)) is not None:
if (
res is not None
and (self.decider is None or self.decider.check(text)) is not None
):
return res
return None
def getExpectedRuntime(self, text: T) -> float:
return sum(i.getExpectedRuntime(text) for i in self.checkers) + self.decider.getExpectedRuntime(text)
return sum(
i.getExpectedRuntime(text) for i in self.checkers
) + self.decider.getExpectedRuntime(text)
def __init__(self, config: Config):
super().__init__(config)

View File

@ -1,22 +1,20 @@
from typing import Optional, Dict, List
import json
from typing import Dict, Optional
from loguru import logger
from . import brandon
from ciphey.iface import registry, Checker, ParamSpec, T, Config
import json
from ciphey.iface import Checker, Config, ParamSpec, T, registry
@registry.register
class JsonChecker(Checker[str]):
"""
This object is effectively a prebuilt quroum (with requirement 1) of common patterns
This object is effectively a prebuilt quorum (with requirement 1) of common patterns
"""
def check(self, text: T) -> Optional[str]:
logger.trace(f"Trying json checker")
logger.trace("Trying json checker")
# https://github.com/Ciphey/Ciphey/issues/389
if text.isdigit():

View File

@ -1,11 +1,8 @@
from typing import Optional, Dict, List
from typing import Dict, Optional
from loguru import logger
from . import brandon
from ciphey.iface import registry, Checker, ParamSpec, T, Config
import json
from ciphey.iface import Checker, Config, ParamSpec, T, registry
@registry.register
@ -16,7 +13,7 @@ class GTestChecker(Checker[str]):
"""
def check(self, text: T) -> Optional[str]:
logger.trace(f"Trying entropy checker")
logger.trace("Trying entropy checker")
pass
def getExpectedRuntime(self, text: T) -> float:

View File

@ -1,6 +1,6 @@
from typing import Optional, Dict
from typing import Dict, Optional
from ciphey.iface import registry, Checker, Config, ParamSpec
from ciphey.iface import Checker, Config, ParamSpec, registry
@registry.register
@ -11,10 +11,10 @@ class HumanChecker(Checker[str]):
def check(self, text: str) -> Optional[str]:
with self._config().pause_spinner_handle():
response = input(f'Result {text.__repr__()} (y/N): ').lower()
response = input(f"Result {text.__repr__()} (y/N): ").lower()
if response == "y":
return ""
elif response == "n" or response == "":
elif response in ("n", ""):
return None
else:
return self.check(text)

View File

@ -1,11 +1,9 @@
from math import ceil
from typing import Optional, Dict, Generic
from typing import Dict, Generic, Optional
import ciphey
from ciphey.iface import ParamSpec, Config, T
from ciphey.iface import Checker, Config, ParamSpec, T, _registry
class Quorum(Generic[T], ciphey.iface.Checker[T]):
class Quorum(Generic[T], Checker[T]):
def check(self, text: T) -> Optional[str]:
left = self._params().k
results = []
@ -32,9 +30,7 @@ class Quorum(Generic[T], ciphey.iface.Checker[T]):
self.checkers = []
for i in self._params()["checker"]:
# This enforces type consistency
self.checkers.append(
ciphey.iface._registry.get_named(i, ciphey.iface.Checker[T])
)
self.checkers.append(_registry.get_named(i, Checker[T]))
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:

View File

@ -1,14 +1,13 @@
from typing import Optional, Dict
import ciphey
import re
from ciphey.iface import ParamSpec, T, Config, registry
from typing import Dict, Optional
from loguru import logger
from ciphey.iface import Checker, Config, ParamSpec, T, registry
@registry.register
class Regex(ciphey.iface.Checker[str]):
class Regex(Checker[str]):
def getExpectedRuntime(self, text: T) -> float:
return 1e-5 # TODO: actually calculate this
@ -37,7 +36,7 @@ class Regex(ciphey.iface.Checker[str]):
@registry.register
class RegexList(ciphey.iface.Checker[str]):
class RegexList(Checker[str]):
def getExpectedRuntime(self, text: T) -> float:
return 1e-5 # TODO: actually calculate this
@ -60,6 +59,8 @@ class RegexList(ciphey.iface.Checker[str]):
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"resource": ParamSpec(
req=True, desc="A list of regexes that could be matched", list=True,
req=True,
desc="A list of regexes that could be matched",
list=True,
)
}

View File

@ -1,114 +0,0 @@
# community
# by https://github.com/lukasgabriel
import re
from distutils import util
from typing import Optional, Dict, Union, Set, List
from loguru import logger
from ciphey.iface import ParamSpec, Cracker, CrackResult, CrackInfo, T, registry, Config
@registry.register
class XandY(Cracker[str]):
def getInfo(self, ctext: str) -> CrackInfo:
return CrackInfo(
success_likelihood=0.1, success_runtime=1e-5, failure_runtime=1e-5,
)
@staticmethod
def binary_to_ascii(variant):
# Convert the binary string to an integer with base 2
binary_int = int(variant, 2)
byte_number = binary_int.bit_length() + 7 // 8
# Convert the resulting int to a bytearray and then decode it to ascii text
binary_array = binary_int.to_bytes(byte_number, "big")
try:
ascii_text = binary_array.decode()
logger.trace(f"Found possible solution: {ascii_text[:32]}...")
return ascii_text
except UnicodeDecodeError as e:
logger.trace(f"X-Y Cracker ecountered UnicodeDecodeError when trying to crack ctext: {e}")
return ""
@staticmethod
def getTarget() -> str:
return "XandY"
def attemptCrack(self, ctext: str) -> List[CrackResult]:
"""
Checks an input if it only consists of two or three different letters.
If this is the case, it attempts to regard those letters as
0 and 1 (with the third characters as an optional delimiter) and then
converts it to ascii text.
"""
logger.trace("Attempting X-Y replacement.")
variants = []
candidates = []
result = []
# Convert the ctext to all-lowercase and regex-match & replace all whitespace
ctext = ctext.lower()
ctext = re.sub(r"\s+", "", ctext, flags=re.UNICODE)
# cset contains every unique value in the ctext
cset = list(set(list(ctext)))
cset_len = len(cset)
if not 1 < cset_len < 4:
# We only consider inputs with two or three unique values
logger.trace("String does not contain two or three unique values. Skipping X-Y...")
return None
else:
logger.trace(f"String contains {cset_len} unique values: {cset}")
# In case of three unique values, we regard the least frequent character as the delimiter
if cset_len == 3:
# Count each unique character in the set to determine the least frequent one
counting_list = []
for char in cset:
counting_list.append(ctext.count(char))
val, index = min(
(val, index) for (index, val) in enumerate(counting_list)
)
delimiter = cset[index]
logger.trace(
f"{delimiter} occurs {val} times and is least frequent character & probable delimiter."
)
# Remove the delimiter from the ctext and compute new cset
ctext = ctext.replace(delimiter, "")
cset = list(set(list(ctext)))
# Form both variants of the substitution
for i in range(2):
if i:
variants.append(ctext.replace(cset[0], "1").replace(cset[1], "0"))
else:
variants.append(ctext.replace(cset[0], "0").replace(cset[1], "1"))
# Apply function to both variants and strip stray NULL characters
for variant in variants:
candidates.append(self.binary_to_ascii(variant).strip("\x00"))
for i, candidate in enumerate(candidates):
if candidate != "":
keyinfo = f"{cset[0]} -> {i} & {cset[1]} -> {str(int(not i))}"
result.append(CrackResult(value=candidate, key_info=keyinfo))
logger.trace(f"X-Y cracker - Returning results: {result}")
return result
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"expected": ParamSpec(
desc="The expected distribution of the plaintext",
req=False,
config_ref=["default_dist"],
)
}
def __init__(self, config: Config):
super().__init__(config)
self.expected = config.get_resource(self._params()["expected"])
self.cache = config.cache

View File

@ -1,5 +1,5 @@
from . import (
XandY,
xandy,
affine,
ascii_shift,
caesar,

View File

@ -3,12 +3,12 @@
from typing import Dict, List, Optional
import ciphey
import cipheycore
from loguru import logger
from ciphey.common import fix_case
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, registry
from ciphey.mathsHelper import mathsHelper
from loguru import logger
@registry.register
@ -44,24 +44,24 @@ class Affine(Cracker[str]):
# a and b are coprime if gcd(a,b) is 1.
possible_a = [
a
for a in range(1, self.ALPHABET_LENGTH)
if mathsHelper.gcd(a, self.ALPHABET_LENGTH) == 1
for a in range(1, self.alphabet_length)
if mathsHelper.gcd(a, self.alphabet_length) == 1
]
logger.debug(
f"Trying Affine Cracker with {len(possible_a)} a-values and {self.ALPHABET_LENGTH} b-values"
f"Trying Affine Cracker with {len(possible_a)} a-values and {self.alphabet_length} b-values"
)
for a in possible_a:
a_inv = mathsHelper.mod_inv(a, self.ALPHABET_LENGTH)
a_inv = mathsHelper.mod_inv(a, self.alphabet_length)
# If there is no inverse, we cannot decrypt the text
if a_inv is None:
continue
for b in range(self.ALPHABET_LENGTH):
for b in range(self.alphabet_length):
# Pass in lowered text. This means that we expect alphabets to not contain both 'a' and 'A'.
translated = self.decrypt(ctext.lower(), a_inv, b, self.ALPHABET_LENGTH)
translated = self.decrypt(ctext.lower(), a_inv, b, self.alphabet_length)
candidate_probability = self.plaintext_probability(translated)
if candidate_probability > self.PLAINTEXT_PROB_THRESHOLD:
if candidate_probability > self.plaintext_prob_threshold:
candidates.append(
CrackResult(
value=fix_case(translated, ctext), key_info=f"a={a}, b={b}"
@ -108,7 +108,7 @@ class Affine(Cracker[str]):
req=False,
config_ref=["default_dist"],
),
"group": ciphey.iface.ParamSpec(
"group": ParamSpec(
desc="An ordered sequence of chars that make up the alphabet",
req=False,
default="abcdefghijklmnopqrstuvwxyz",
@ -119,6 +119,6 @@ class Affine(Cracker[str]):
super().__init__(config)
self.group = list(self._params()["group"])
self.expected = config.get_resource(self._params()["expected"])
self.ALPHABET_LENGTH = len(self.group)
self.alphabet_length = len(self.group)
self.cache = config.cache
self.PLAINTEXT_PROB_THRESHOLD = 0.01
self.plaintext_prob_threshold = 0.01

View File

@ -8,19 +8,16 @@
Github: brandonskerritt
"""
from typing import Optional, Dict, Union, Set, List, Tuple
from ciphey.iface import ParamSpec, CrackResult, T, CrackInfo, registry
from loguru import logger
import ciphey
from typing import Dict, List, Optional
import cipheycore
from loguru import logger
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, registry
@registry.register
class Ascii_shift(ciphey.iface.Cracker[str]):
class Ascii_shift(Cracker[str]):
def getInfo(self, ctext: str) -> CrackInfo:
analysis = self.cache.get_or_update(
ctext,
@ -77,17 +74,17 @@ class Ascii_shift(ciphey.iface.Cracker[str]):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"expected": ciphey.iface.ParamSpec(
"expected": ParamSpec(
desc="The expected distribution of the plaintext",
req=False,
config_ref=["default_dist"],
),
"group": ciphey.iface.ParamSpec(
"group": ParamSpec(
desc="An ordered sequence of chars that make up the ASCII shift cipher alphabet",
req=False,
default="""\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f""",
),
"p_value": ciphey.iface.ParamSpec(
"p_value": ParamSpec(
desc="The p-value to use for standard frequency analysis",
req=False,
default=0.01,
@ -95,7 +92,7 @@ class Ascii_shift(ciphey.iface.Cracker[str]):
# TODO: add "filter" param
}
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self.group = list(self._params()["group"])
self.expected = config.get_resource(self._params()["expected"])

View File

@ -7,20 +7,18 @@
© Brandon Skerritt
Github: brandonskerritt
"""
import sys
from distutils import util
from typing import Optional, Dict, Union, Set, List, Tuple
from typing import Dict, List, Optional, Union
from loguru import logger
import ciphey
import cipheycore
from loguru import logger
from ciphey.iface import ParamSpec, CrackResult, T, CrackInfo, registry
from ciphey.common import fix_case
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, registry
@registry.register
class Caesar(ciphey.iface.Cracker[str]):
class Caesar(Cracker[str]):
def getInfo(self, ctext: str) -> CrackInfo:
analysis = self.cache.get_or_update(
ctext,
@ -66,7 +64,7 @@ class Caesar(ciphey.iface.Cracker[str]):
logger.debug(f"Caesar returned {n_candidates} candidates")
if n_candidates == 0:
logger.trace(f"Filtering for better results")
logger.trace("Filtering for better results")
analysis = cipheycore.analyse_string(ctext, self.group)
possible_keys = cipheycore.caesar_crack(
analysis, self.expected, self.group, self.p_value
@ -77,29 +75,31 @@ class Caesar(ciphey.iface.Cracker[str]):
for candidate in possible_keys:
logger.trace(f"Candidate {candidate.key} has prob {candidate.p_value}")
translated = cipheycore.caesar_decrypt(message, candidate.key, self.group)
candidates.append(CrackResult(value=fix_case(translated, ctext), key_info=candidate.key))
candidates.append(
CrackResult(value=fix_case(translated, ctext), key_info=candidate.key)
)
return candidates
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"expected": ciphey.iface.ParamSpec(
"expected": ParamSpec(
desc="The expected distribution of the plaintext",
req=False,
config_ref=["default_dist"],
),
"group": ciphey.iface.ParamSpec(
"group": ParamSpec(
desc="An ordered sequence of chars that make up the caesar cipher alphabet",
req=False,
default="abcdefghijklmnopqrstuvwxyz",
),
"lower": ciphey.iface.ParamSpec(
"lower": ParamSpec(
desc="Whether or not the ciphertext should be converted to lowercase first",
req=False,
default=True,
),
"p_value": ciphey.iface.ParamSpec(
"p_value": ParamSpec(
desc="The p-value to use for standard frequency analysis",
req=False,
default=0.01,
@ -107,10 +107,10 @@ class Caesar(ciphey.iface.Cracker[str]):
# TODO: add "filter" param
}
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self.lower: Union[str, bool] = self._params()["lower"]
if type(self.lower) != bool:
if not isinstance(self.lower, bool):
self.lower = util.strtobool(self.lower)
self.group = list(self._params()["group"])
self.expected = config.get_resource(self._params()["expected"])

View File

@ -1,25 +1,18 @@
"""
his is Hashbuster but slightly modified to work with Ciphey
why reivent the wheel?
This is Hashbuster but slightly modified to work with Ciphey.
Why reinvent the wheel?
Changes (that I can remember)
* timeout set, as hashbuster took AGES before timeout was set.
https://github.com/s0md3v/Hash-Buster
"""
import re
import os
import argparse
from typing import Dict, List, Optional
import requests
import concurrent.futures
from loguru import logger
from typing import Optional, Dict, Union, Set, List, Any
from loguru import logger
import ciphey
from ciphey.iface import ParamSpec, CrackResult, T, CrackInfo, registry
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, T, registry
thread_count = 4
@ -38,8 +31,7 @@ def beta(ctext, hashtype):
match = re.search(r'/generate-hash/?text=.*?"', response)
if match:
return match.group(1)
else:
return None
return None
def gamma(ctext, hashtype):
@ -85,7 +77,7 @@ result = {}
def crack(ctext):
raise ("Error Crack is called")
raise "Error Crack is called"
def threaded(ctext):
@ -96,14 +88,14 @@ def threaded(ctext):
@registry.register
class HashBuster(ciphey.iface.Cracker[str]):
class HashBuster(Cracker[str]):
@staticmethod
def getTarget() -> str:
return "hash"
@staticmethod
def getParams() -> Optional[Dict[str, Dict[str, Any]]]:
pass
def getParams() -> Optional[Dict[str, ParamSpec]]:
return None
@staticmethod
def priority() -> float:
@ -111,10 +103,14 @@ class HashBuster(ciphey.iface.Cracker[str]):
def getInfo(self, ctext: T) -> CrackInfo:
# TODO calculate these properly
return CrackInfo(success_likelihood=0.5, success_runtime=5, failure_runtime=5,)
return CrackInfo(
success_likelihood=0.5,
success_runtime=5,
failure_runtime=5,
)
def attemptCrack(self, ctext: T) -> List[CrackResult]:
logger.debug(f"Starting to crack hashes")
logger.debug("Starting to crack hashes")
result = False
candidates = []
@ -154,5 +150,5 @@ class HashBuster(ciphey.iface.Cracker[str]):
# TODO add to 5.1 make this return multiple possible candidates
return [CrackResult(value=candidates[0][0], misc_info=candidates[1][1])]
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)

View File

@ -8,19 +8,16 @@
Github: brandonskerritt
"""
from typing import Optional, Dict, Union, Set, List, Tuple
from ciphey.iface import ParamSpec, CrackResult, T, CrackInfo, registry
from loguru import logger
import ciphey
from typing import Dict, List, Optional
import cipheycore
from loguru import logger
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, registry
@registry.register
class Rot47(ciphey.iface.Cracker[str]):
class Rot47(Cracker[str]):
def getInfo(self, ctext: str) -> CrackInfo:
analysis = self.cache.get_or_update(
ctext,
@ -77,17 +74,17 @@ class Rot47(ciphey.iface.Cracker[str]):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"expected": ciphey.iface.ParamSpec(
"expected": ParamSpec(
desc="The expected distribution of the plaintext",
req=False,
config_ref=["default_dist"],
),
"group": ciphey.iface.ParamSpec(
"group": ParamSpec(
desc="An ordered sequence of chars that make up the ROT47 cipher alphabet",
req=False,
default="""!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""",
default="""!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~""",
),
"p_value": ciphey.iface.ParamSpec(
"p_value": ParamSpec(
desc="The p-value to use for standard frequency analysis",
req=False,
default=0.01,
@ -95,7 +92,7 @@ class Rot47(ciphey.iface.Cracker[str]):
# TODO: add "filter" param
}
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self.group = list(self._params()["group"])
self.expected = config.get_resource(self._params()["expected"])

View File

@ -7,28 +7,26 @@
© Brandon Skerritt
Github: brandonskerritt
"""
from copy import copy
from distutils import util
from typing import Optional, Dict, Union, Set, List
from typing import Dict, List, Optional, Union
import re
from loguru import logger
import ciphey
import cipheycore
from loguru import logger
from ciphey.iface import ParamSpec, Cracker, CrackResult, T, CrackInfo, registry
from ciphey.common import fix_case
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, registry
@registry.register
class Vigenere(ciphey.iface.Cracker[str]):
class Vigenere(Cracker[str]):
def getInfo(self, ctext: str) -> CrackInfo:
if self.keysize is not None:
analysis = self.cache.get_or_update(
ctext,
f"vigenere::{self.keysize}",
lambda: cipheycore.analyse_string(ctext.lower(), self.keysize, self.group),
lambda: cipheycore.analyse_string(
ctext.lower(), self.keysize, self.group
),
)
val = cipheycore.vigenere_detect(analysis, self.expected)
@ -44,13 +42,14 @@ class Vigenere(ciphey.iface.Cracker[str]):
likely_lens = self.cache.get_or_update(
ctext,
f"vigenere::likely_lens",
lambda: cipheycore.vigenere_likely_key_lens(ctext.lower(), self.expected, self.group, self.detect_p_value),
"vigenere::likely_lens",
lambda: cipheycore.vigenere_likely_key_lens(
ctext.lower(), self.expected, self.group, self.detect_p_value
),
)
likely_lens_cpy = likely_lens
# Filter out the lens that make no sense
likely_lens = [i for i in likely_lens if i.len <= self.MAX_KEY_LENGTH]
likely_lens = [i for i in likely_lens if i.len <= self.max_key_length]
for keysize in likely_lens:
# Store the analysis
@ -65,7 +64,9 @@ class Vigenere(ciphey.iface.Cracker[str]):
failure_runtime=2e-2,
)
logger.debug(f"Vigenere has likelihood {likely_lens[0].p_value} with lens {[i.len for i in likely_lens]}")
logger.debug(
f"Vigenere has likelihood {likely_lens[0].p_value} with lens {[i.len for i in likely_lens]}"
)
return CrackInfo(
success_likelihood=likely_lens[0].p_value,
@ -85,15 +86,18 @@ class Vigenere(ciphey.iface.Cracker[str]):
analysis, self.expected, self.group, self.p_value
)
if len(possible_keys) > self.clamp:
possible_keys = possible_keys[:self.clamp]
possible_keys = possible_keys[: self.clamp]
logger.trace(
f"Vigenere crack got keys: {[[i for i in candidate.key] for candidate in possible_keys]}"
)
return [
CrackResult(
value=fix_case(cipheycore.vigenere_decrypt(ctext, candidate.key, self.group), real_ctext),
value=fix_case(
cipheycore.vigenere_decrypt(ctext, candidate.key, self.group),
real_ctext,
),
key_info="".join([self.group[i] for i in candidate.key]),
misc_info=f"p-value was {candidate.p_value}"
misc_info=f"p-value was {candidate.p_value}",
)
for candidate in possible_keys[: min(len(possible_keys), 10)]
]
@ -113,80 +117,84 @@ class Vigenere(ciphey.iface.Cracker[str]):
self.cache.get_or_update(
ctext,
f"vigenere::{self.keysize}",
lambda: cipheycore.analyse_string(message, self.keysize, self.group),
lambda: cipheycore.analyse_string(
message, self.keysize, self.group
),
),
ctext
)
else:
arrs = []
likely_lens = self.cache.get_or_update(
ctext,
f"vigenere::likely_lens",
lambda: cipheycore.vigenere_likely_key_lens(message, self.expected, self.group),
)
possible_lens = [i for i in likely_lens]
possible_lens.sort(key=lambda i: i.p_value)
logger.trace(f"Got possible lengths {[i.len for i in likely_lens]}")
# TODO: work out length
for i in possible_lens:
arrs.extend(
self.crackOne(
message,
self.cache.get_or_update(
ctext,
f"vigenere::{i.len}",
lambda: cipheycore.analyse_string(message, i.len, self.group),
),
ctext
)
)
logger.debug(f"Vigenere returned {len(arrs)} candidates")
return arrs
arrs = []
likely_lens = self.cache.get_or_update(
ctext,
"vigenere::likely_lens",
lambda: cipheycore.vigenere_likely_key_lens(
message, self.expected, self.group
),
)
possible_lens = [i for i in likely_lens]
possible_lens.sort(key=lambda i: i.p_value)
logger.trace(f"Got possible lengths {[i.len for i in likely_lens]}")
# TODO: work out length
for i in possible_lens:
arrs.extend(
self.crackOne(
message,
self.cache.get_or_update(
ctext,
f"vigenere::{i.len}",
lambda: cipheycore.analyse_string(message, i.len, self.group),
),
ctext,
)
)
logger.debug(f"Vigenere returned {len(arrs)} candidates")
return arrs
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"expected": ciphey.iface.ParamSpec(
"expected": ParamSpec(
desc="The expected distribution of the plaintext",
req=False,
config_ref=["default_dist"],
),
"group": ciphey.iface.ParamSpec(
"group": ParamSpec(
desc="An ordered sequence of chars that make up the caesar cipher alphabet",
req=False,
default="abcdefghijklmnopqrstuvwxyz",
),
"lower": ciphey.iface.ParamSpec(
"lower": ParamSpec(
desc="Whether or not the ciphertext should be converted to lowercase first",
req=False,
default=True,
),
"keysize": ciphey.iface.ParamSpec(
"keysize": ParamSpec(
desc="A key size that should be used. If not given, will attempt to work it out",
req=False,
),
"p_value": ciphey.iface.ParamSpec(
"p_value": ParamSpec(
desc="The p-value to use for windowed frequency analysis",
req=False,
default=0.5,
),
"detect_p_value": ciphey.iface.ParamSpec(
"detect_p_value": ParamSpec(
desc="The p-value to use for the detection of Vigenere length",
req=False,
default=0.01,
),
"clamp": ciphey.iface.ParamSpec(
"clamp": ParamSpec(
desc="The maximum number of candidates that can be returned per key len",
req=False,
default=10,
),
}
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self.lower: Union[str, bool] = self._params()["lower"]
if type(self.lower) != bool:
if not isinstance(self.lower, bool):
self.lower = util.strtobool(self.lower)
self.group = list(self._params()["group"])
self.expected = config.get_resource(self._params()["expected"])
@ -197,4 +205,4 @@ class Vigenere(ciphey.iface.Cracker[str]):
self.p_value = float(self._params()["p_value"])
self.detect_p_value = float(self._params()["detect_p_value"])
self.clamp = int(self._params()["clamp"])
self.MAX_KEY_LENGTH = 16
self.max_key_length = 16

View File

@ -0,0 +1,111 @@
import re
from typing import Dict, List, Optional
from loguru import logger
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, registry
@registry.register
class Xandy(Cracker[str]):
def getInfo(self, ctext: str) -> CrackInfo:
return CrackInfo(
success_likelihood=0.1,
success_runtime=1e-5,
failure_runtime=1e-5,
)
@staticmethod
def binary_to_ascii(variant):
# Convert the binary string to an integer with base 2
binary_int = int(variant, 2)
byte_number = binary_int.bit_length() + 7 // 8
# Convert the resulting int to a bytearray and then decode it to ASCII text
binary_array = binary_int.to_bytes(byte_number, "big")
try:
ascii_text = binary_array.decode()
logger.trace(f"Found possible solution: {ascii_text[:32]}")
return ascii_text
except UnicodeDecodeError as e:
logger.trace(f"Failed to crack X-Y due to a UnicodeDecodeError: {e}")
return ""
@staticmethod
def getTarget() -> str:
return "xandy"
def attemptCrack(self, ctext: str) -> List[CrackResult]:
"""
Checks an input if it only consists of two or three different letters.
If this is the case, it attempts to regard those letters as
0 and 1 (with the third characters as an optional delimiter) and then
converts it to ASCII text.
"""
logger.trace("Attempting X-Y replacement")
variants = []
candidates = []
result = []
# Convert the ctext to all-lowercase and regex-match & replace all whitespace
ctext = re.sub(r"\s+", "", ctext.lower(), flags=re.UNICODE)
# cset contains every unique value in the ctext
cset = list(set(list(ctext)))
cset_len = len(cset)
if not 1 < cset_len < 4:
# We only consider inputs with two or three unique values
logger.trace(
"Failed to crack X-Y due to not containing two or three unique values"
)
return None
logger.trace(f"String contains {cset_len} unique values: {cset}")
# In case of three unique values, we regard the least frequent character as the delimiter
if cset_len == 3:
# Count each unique character in the set to determine the least frequent one
counting_list = []
for char in cset:
counting_list.append(ctext.count(char))
val, index = min((val, index) for (index, val) in enumerate(counting_list))
delimiter = cset[index]
logger.trace(
f"{delimiter} occurs {val} times and is the probable delimiter"
)
# Remove the delimiter from the ctext and compute new cset
ctext = ctext.replace(delimiter, "")
cset = list(set(list(ctext)))
# Form both variants of the substitution
for i in range(2):
if i:
variants.append(ctext.replace(cset[0], "1").replace(cset[1], "0"))
else:
variants.append(ctext.replace(cset[0], "0").replace(cset[1], "1"))
# Apply function to both variants and strip stray NULL characters
for variant in variants:
candidates.append(self.binary_to_ascii(variant).strip("\x00"))
for i, candidate in enumerate(candidates):
if candidate != "":
keyinfo = f"{cset[0]} -> {i} & {cset[1]} -> {str(int(not i))}"
result.append(CrackResult(value=candidate, key_info=keyinfo))
logger.trace(f"X-Y cracker - Returning results: {result}")
return result
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"expected": ParamSpec(
desc="The expected distribution of the plaintext",
req=False,
config_ref=["default_dist"],
)
}
def __init__(self, config: Config):
super().__init__(config)
self.expected = config.get_resource(self._params()["expected"])
self.cache = config.cache

View File

@ -7,18 +7,16 @@
© Brandon Skerritt
Github: brandonskerritt
"""
import sys
from distutils import util
from typing import Optional, Dict, Union, Set, List, Tuple
from typing import Dict, List, Optional
from loguru import logger
import ciphey
import cipheycore
from loguru import logger
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, registry
from ciphey.iface import ParamSpec, CrackResult, T, CrackInfo, registry
@registry.register
class XorSingle(ciphey.iface.Cracker[bytes]):
class XorSingle(Cracker[bytes]):
def getInfo(self, ctext: str) -> CrackInfo:
analysis = self.cache.get_or_update(
ctext,
@ -72,12 +70,12 @@ class XorSingle(ciphey.iface.Cracker[bytes]):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"expected": ciphey.iface.ParamSpec(
"expected": ParamSpec(
desc="The expected distribution of the plaintext",
req=False,
config_ref=["default_dist"],
),
"p_value": ciphey.iface.ParamSpec(
"p_value": ParamSpec(
desc="The p-value to use for standard frequency analysis",
req=False,
default=0.01,
@ -86,10 +84,10 @@ class XorSingle(ciphey.iface.Cracker[bytes]):
}
@staticmethod
def scoreUtility() -> float:
def score_utility() -> float:
return 1.5
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self.expected = config.get_resource(self._params()["expected"])
self.cache = config.cache

View File

@ -7,22 +7,17 @@
© Brandon Skerritt
Github: brandonskerritt
"""
from copy import copy
from distutils import util
from typing import Optional, Dict, Union, Set, List
import re
import base64
from typing import Dict, List, Optional
from loguru import logger
import ciphey
import cipheycore
from loguru import logger
from ciphey.iface import ParamSpec, Cracker, CrackResult, T, CrackInfo, registry
from ciphey.iface import Config, Cracker, CrackInfo, CrackResult, ParamSpec, registry
@registry.register
class XorCrypt(ciphey.iface.Cracker[bytes]):
class XorCrypt(Cracker[bytes]):
def getInfo(self, ctext: bytes) -> CrackInfo:
if self.keysize is not None:
analysis = self.cache.get_or_update(
@ -40,7 +35,7 @@ class XorCrypt(ciphey.iface.Cracker[bytes]):
keysize = self.cache.get_or_update(
ctext,
f"xorcrypt::likely_lens",
"xorcrypt::likely_lens",
lambda: cipheycore.xorcrypt_guess_len(ctext),
)
@ -53,7 +48,7 @@ class XorCrypt(ciphey.iface.Cracker[bytes]):
)
return CrackInfo(
success_likelihood=0.9, # Dunno, but it's quite likely
success_likelihood=0.9, # Dunno, but it's quite likely
# TODO: actually calculate runtimes
success_runtime=2e-3,
failure_runtime=2e-2,
@ -66,17 +61,17 @@ class XorCrypt(ciphey.iface.Cracker[bytes]):
def crackOne(
self, ctext: bytes, analysis: cipheycore.windowed_analysis_res
) -> List[CrackResult]:
possible_keys = cipheycore.xorcrypt_crack(
analysis, self.expected, self.p_value
)
possible_keys = cipheycore.xorcrypt_crack(analysis, self.expected, self.p_value)
logger.trace(f"xorcrypt crack got keys: {[[i for i in candidate.key] for candidate in possible_keys]}")
logger.trace(
f"xorcrypt crack got keys: {[[i for i in candidate.key] for candidate in possible_keys]}"
)
return [
CrackResult(
value=cipheycore.xorcrypt_decrypt(ctext, candidate.key),
key_info="0x" + "".join(["{:02x}".format(i) for i in candidate.key]),
)
for candidate in possible_keys[:min(len(possible_keys), 10)]
for candidate in possible_keys[: min(len(possible_keys), 10)]
]
def attemptCrack(self, ctext: bytes) -> List[CrackResult]:
@ -92,52 +87,52 @@ class XorCrypt(ciphey.iface.Cracker[bytes]):
lambda: cipheycore.analyse_bytes(ctext, self.keysize),
),
)
else:
len = self.cache.get_or_update(
len = self.cache.get_or_update(
ctext,
"xorcrypt::likely_lens",
lambda: cipheycore.xorcrypt_guess_len(ctext),
)
logger.trace(f"Got possible length {len}")
if len < 2:
return []
ret = []
# Fuzz around
for i in range(min(len - 2, 2), len + 2):
ret += self.crackOne(
ctext,
f"xorcrypt::likely_lens",
lambda: cipheycore.xorcrypt_guess_len(ctext),
self.cache.get_or_update(
ctext,
f"xorcrypt::{len}",
lambda: cipheycore.analyse_bytes(ctext, len),
),
)
logger.trace(f"Got possible length {len}")
if len < 2:
return []
ret = []
# Fuzz around
for i in range(min(len - 2, 2), len + 2):
ret += self.crackOne(
ctext,
self.cache.get_or_update(
ctext,
f"xorcrypt::{len}",
lambda: cipheycore.analyse_bytes(ctext, len),
)
)
return ret
return ret
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"expected": ciphey.iface.ParamSpec(
"expected": ParamSpec(
desc="The expected distribution of the plaintext",
req=False,
config_ref=["default_dist"],
),
"keysize": ciphey.iface.ParamSpec(
"keysize": ParamSpec(
desc="A key size that should be used. If not given, will attempt to work it out",
req=False,
),
"p_value": ciphey.iface.ParamSpec(
"p_value": ParamSpec(
desc="The p-value to use for windowed frequency analysis",
req=False,
default=0.001,
),
}
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self.expected = config.get_resource(self._params()["expected"])
self.cache = config.cache
@ -145,4 +140,4 @@ class XorCrypt(ciphey.iface.Cracker[bytes]):
if self.keysize is not None:
self.keysize = int(self.keysize)
self.p_value = self._params()["p_value"]
self.MAX_KEY_LENGTH = 16
self.max_key_length = 16

View File

@ -1,24 +1,24 @@
from . import (
morse,
bases,
unicode,
reverse,
octal,
binary,
hexadecimal,
atbash,
galactic,
base62,
base58,
base58_bitcoin,
base58_ripple,
base91,
leet,
decimal,
base62,
base69,
base91,
bases,
baudot,
multi_tap,
url,
tap_code,
binary,
brainfuck,
decimal,
galactic,
hexadecimal,
leetspeak,
morse_code,
multi_tap,
octal,
reverse,
tap_code,
unicode,
url,
uuencode,
)

View File

@ -1,14 +1,13 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
import re
from typing import Dict, Optional
from loguru import logger
import re
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class A1z26(Decoder[str, str]):
class A1z26(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs A1Z26 decoding

View File

@ -1,21 +1,18 @@
# community
# by https://github.com/lukasgabriel
from typing import Dict, Optional
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry, WordList
from ciphey.common import fix_case
from ciphey.iface import Config, Decoder, ParamSpec, T, U, WordList, registry
@registry.register
class Atbash(Decoder[str, str]):
class Atbash(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Takes an encoded string and attempts to decode it according to the Atbash cipher.
The Atbash cipher is a very simple substitution cipher without a key.
It operates by replacing every letter in the input by its 'counterpoint'
in the alphabet. Example: A -> Z, B -> Y, ... , M -> N and vice versa.
The Atbash cipher is a very simple substitution cipher without a key.
It operates by replacing every letter in the input by its 'counterpoint'
in the alphabet. Example: A -> Z, B -> Y, ... , M -> N and vice versa.
"""
result = ""
@ -27,7 +24,7 @@ class Atbash(Decoder[str, str]):
result += atbash_dict[letter]
else:
# If the current character is not in the defined alphabet,
# just accept it as-is (useful for numbers, punctuation,...)
# just accept it as-is (useful for numbers, punctuation, etc.)
result += letter
return fix_case(result, ctext)

View File

@ -1,15 +1,15 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from typing import Dict, Optional
import base58
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Base58(Decoder[str, str]):
class Base58_bitcoin(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Base62 decoding
Performs Base58 (Bitcoin) decoding
"""
try:
return base58.b58decode(ctext).decode("utf-8")
@ -30,4 +30,4 @@ class Base58(Decoder[str, str]):
@staticmethod
def getTarget() -> str:
return "base58"
return "base58_bitcoin"

View File

@ -1,12 +1,12 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from typing import Dict, Optional
import base58
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Base58_flickr(Decoder[str, str]):
class Base58_flickr(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Base58 (Flickr) decoding

View File

@ -1,15 +1,15 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from typing import Dict, Optional
import base58
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Base58_ripple(Decoder[str, str]):
class Base58_ripple(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Base62 decoding
Performs Base58 (Ripple) decoding
"""
try:
return base58.b58decode(ctext, alphabet=base58.RIPPLE_ALPHABET).decode(

View File

@ -1,12 +1,12 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from typing import Dict, Optional
import base62
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Base62(Decoder[str, str]):
class Base62(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Base62 decoding

View File

@ -1,12 +1,11 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
import base64
from typing import Dict, Optional
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Base64_url(Decoder[str, str]):
class Base64_url(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Base64 URL decoding

View File

@ -1,12 +1,12 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from typing import Dict, Optional
import base65536
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Base65536(Decoder[str, str]):
class Base65536(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Base65536 decoding

View File

@ -1,17 +1,15 @@
# by https://github.com/lukasgabriel
# translated to Python and adapted for Ciphey from the JS original at https://github.com/pshihn/base69
# Translated to Python and adapted for Ciphey from the JS original at https://github.com/pshihn/base69
import re
from math import ceil
from typing import Dict, Optional
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry, WordList
from ciphey.iface import Config, Decoder, ParamSpec, T, U, WordList, registry
@registry.register
class Base69(Decoder[str, str]):
class Base69(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Base69 decoding
@ -39,7 +37,7 @@ class Base69(Decoder[str, str]):
for n, elem in enumerate(insert):
result[n + i * 7] = elem % 256
return bytearray(result).decode().strip("\x00")
except:
except Exception:
return None
def decode_chunk(self, s: str):

View File

@ -1,12 +1,12 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from typing import Dict, Optional
import base91
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Base91(Decoder[str, str]):
class Base91(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Base91 decoding

View File

@ -1,11 +1,12 @@
import base64
import types
import ciphey
from typing import Callable, Optional, Any, Dict
from typing import Any, Callable, Optional
from loguru import logger
from ciphey.common import id_lambda
from ciphey.iface import Decoder, registry
def _dispatch(self: Any, ctext: str, func: Callable[[str], bytes]) -> Optional[bytes]:
logger.trace(f"Attempting {self.getTarget()}")
@ -29,19 +30,19 @@ _bases = {
def gen_class(name, decoder, priority, ns):
ns["_get_func"] = ciphey.common.id_lambda(decoder)
ns["_get_func"] = id_lambda(decoder)
ns["decode"] = lambda self, ctext: _dispatch(self, ctext, self._get_func())
ns["getParams"] = ciphey.common.id_lambda(None)
ns["getTarget"] = ciphey.common.id_lambda(name)
ns["priority"] = ciphey.common.id_lambda(priority)
ns["getParams"] = id_lambda(None)
ns["getTarget"] = id_lambda(name)
ns["priority"] = id_lambda(priority)
ns["__init__"] = lambda self, config: super(type(self), self).__init__(config)
for name, (decoder, priority) in _bases.items():
t = types.new_class(
name,
(ciphey.iface.Decoder[str, bytes],),
(Decoder[str],),
exec_body=lambda x: gen_class(name, decoder, priority, x),
)
ciphey.iface.registry.register(t)
registry.register(t)

View File

@ -1,25 +1,25 @@
import re
from typing import Optional, Dict, List
from typing import Dict, Optional
from ciphey.iface import ParamSpec, Config, T, U, Decoder, registry, Translation
from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry
@registry.register_multi((str, str), (bytes, bytes))
class Baudot(Decoder[str, str]):
@registry.register
class Baudot(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
ret = ""
result = ""
switch_to_digit_map = 0
if type(ctext) == str:
if re.search("^[01]{5}$", ctext.split()[0]):
for i in ctext.split():
if i == "11011":
switch_to_digit_map = 1
if i == "11111":
switch_to_digit_map = 0
if switch_to_digit_map == 1:
ret += self.BAUDOT_DICT["+" + i]
if switch_to_digit_map == 0:
ret += self.BAUDOT_DICT[i]
return ret
if re.search("^[01]{5}$", ctext.split()[0]):
for i in ctext.split():
if i == "11011":
switch_to_digit_map = 1
if i == "11111":
switch_to_digit_map = 0
if switch_to_digit_map == 1:
result += self.BAUDOT_DICT["+" + i]
if switch_to_digit_map == 0:
result += self.BAUDOT_DICT[i]
return result
else:
return None

View File

@ -1,30 +1,28 @@
from typing import Optional, Dict, Any, List
import re
from typing import Dict, List, Optional
from loguru import logger
import ciphey
from ciphey.iface import registry
import re
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Binary(ciphey.iface.Decoder[str, bytes]):
def decode(self, text: str) -> Optional[bytes]:
class Binary(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
try:
text = re.sub(r"[^\S \n]", " ", text, flags=re.UNICODE)
text = text.replace("\n", " ")
ctext = re.sub(r"[^\S \n]", " ", ctext, flags=re.UNICODE)
ctext = ctext.replace("\n", " ")
existing_split = self.try_split(text.split(" "))
existing_split = self.try_split(ctext.split(" "))
if existing_split is not None:
return existing_split
# Now we try our own grouping
# Remove final bit of whitespace
text = text.replace(" ", "")
ctext = ctext.replace(" ", "")
# Split into bytes, and test
return self.try_split([text[i : i + 8] for i in range(0, len(text), 8)])
return self.try_split([ctext[i : i + 8] for i in range(0, len(ctext), 8)])
# Catch bad octal chars
except ValueError:
return None
@ -49,12 +47,12 @@ class Binary(ciphey.iface.Decoder[str, bytes]):
def priority() -> float:
return 0.3
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
@staticmethod
def getParams() -> Optional[Dict[str, Dict[str, Any]]]:
pass
def getParams() -> Optional[Dict[str, ParamSpec]]:
return None
@staticmethod
def getTarget() -> str:

View File

@ -1,16 +1,14 @@
from typing import Optional, Dict, List, Tuple
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry, WordList
import re
import time
from typing import Dict, Optional, Tuple
from loguru import logger
import re
import time
from ciphey.iface import Config, Decoder, ParamSpec, T, U, WordList, registry
@registry.register
class Brainfuck(Decoder[str, str]):
class Brainfuck(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Takes a ciphertext and treats it as a Brainfuck program,

View File

@ -1,14 +1,13 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
import re
from typing import Dict, Optional
from loguru import logger
import re
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Decimal(Decoder[str, str]):
class Decimal(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Decimal decoding

View File

@ -1,14 +1,13 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry, Translation
import re
from typing import Dict, Optional
from loguru import logger
import re
from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry
@registry.register
class Dna(Decoder[str, str]):
class Dna(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs DNA decoding

View File

@ -1,14 +1,13 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry, Translation
import re
from typing import Dict, Optional
from loguru import logger
import re
from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry
@registry.register
class Dtmf(Decoder[str, str]):
class Dtmf(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs DTMF decoding

View File

@ -1,20 +1,18 @@
# community
# by https://github.com/lukasgabriel
from typing import Dict, Optional
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry, Translation
from loguru import logger
from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry
@registry.register
class Galactic(Decoder[str, str]):
class Galactic(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Takes a string written in the 'Standard Galactic Alphabet'
Takes a string written in the 'Standard Galactic Alphabet'
(aka Minecraft Enchanting Table Symbols) and translates it to ASCII text.
"""
logger.trace("Attempting Standard Galactic Alphabet Decoder")
logger.trace("Attempting Standard Galactic Alphabet decoder")
# To avoid complications, only move forward with the decoding if we can
# reasonably assume that the input string is written in the galactic alphabet
@ -28,7 +26,7 @@ class Galactic(Decoder[str, str]):
continue
if galactic_matches == 0:
logger.trace(
"No matching galactic alphabet letters found. Skipping galactic decoder..."
"No matching galactic alphabet letters found. Skipping galactic decoder"
)
return None
logger.trace(f"{galactic_matches} galactic alphabet letters found. ")
@ -50,7 +48,7 @@ class Galactic(Decoder[str, str]):
result += self.GALACTIC_DICT[letter]
else:
# If the current character is not in the defined alphabet,
# just accept it as-is (useful for numbers, punctuation,...)
# just accept it as-is (useful for numbers, punctuation, etc.)
result += letter
# Remove the trailing space (appearing as a leading space)
# from the x that results from the diacritic replacement

View File

@ -1,34 +1,32 @@
from typing import Optional, Dict, Any
from typing import Dict, Optional
import ciphey
from ciphey.iface import registry
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Hex(ciphey.iface.Decoder[str, bytes]):
def decode(self, text: str) -> Optional[bytes]:
class Hexadecimal(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
It takes an octal string and return a string
:octal_str: octal str like "110 145 154"
Performs Hexadecimal decoding
"""
ctext_decoded = ""
try:
str_converted = bytearray.fromhex(text).decode()
return str_converted
# Catch bad octal chars
except ValueError:
ctext_decoded = bytearray.fromhex(ctext).decode("utf-8")
return ctext_decoded
except Exception:
return None
@staticmethod
def priority() -> float:
return 0.015
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
@staticmethod
def getParams() -> Optional[Dict[str, Dict[str, Any]]]:
pass
def getParams() -> Optional[Dict[str, ParamSpec]]:
return None
@staticmethod
def getTarget() -> str:
return "hex"
return "hexadecimal"

View File

@ -1,21 +1,20 @@
from typing import Optional, Dict, Any
from typing import Dict, Optional
import ciphey
from ciphey.iface import registry, Translation, ParamSpec
from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry
@registry.register
class Leet(ciphey.iface.Decoder[str, str]):
def decode(self, text: str) -> Optional[str]:
class Leetspeak(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
for src, dst in self.translate.items():
text = text.replace(src, dst)
return text
ctext = ctext.replace(src, dst)
return ctext
@staticmethod
def priority() -> float:
return 0.05
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self.translate = config.get_resource(self._params()["dict"], Translation)
@ -31,4 +30,4 @@ class Leet(ciphey.iface.Decoder[str, str]):
@staticmethod
def getTarget() -> str:
return "leet"
return "leetspeak"

View File

@ -1,11 +1,12 @@
from typing import Optional, Dict, Any, List
from typing import Dict, Optional
from loguru import logger
import ciphey
from ciphey.iface import registry
from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry
@registry.register
class MorseCode(ciphey.iface.Decoder[str, str]):
class Morse_code(Decoder[str]):
# A priority list for char/word boundaries
BOUNDARIES = {" ": 1, "/": 2, "\n": 3}
PURGE = {ord(c): None for c in BOUNDARIES.keys()}
@ -14,19 +15,19 @@ class MorseCode(ciphey.iface.Decoder[str, str]):
MORSE_CODE_DICT: Dict[str, str]
MORSE_CODE_DICT_INV: Dict[str, str]
def decode(self, text: str) -> Optional[str]:
logger.trace("Attempting morse code")
def decode(self, ctext: T) -> Optional[U]:
logger.trace("Attempting Morse code decoder")
char_boundary = word_boundary = None
char_boundary = word_boundary = None
char_priority = word_priority = 0
# Custom loop allows early break
for i in text:
for i in ctext:
i_priority = self.BOUNDARIES.get(i)
if i_priority is None:
if i in self.ALLOWED:
continue
continue
logger.trace(f"Non-morse char '{i}' found")
return None
@ -50,12 +51,12 @@ class MorseCode(ciphey.iface.Decoder[str, str]):
result = ""
for word in text.split(word_boundary) if word_boundary else [text]:
for word in ctext.split(word_boundary) if word_boundary else [ctext]:
logger.trace(f"Attempting to decode word {word}")
for char in word.split(char_boundary):
char = char.translate(self.PURGE)
if len(char) == 0:
continue
continue
try:
m = self.MORSE_CODE_DICT_INV[char]
except KeyError:
@ -65,7 +66,7 @@ class MorseCode(ciphey.iface.Decoder[str, str]):
# after every word add a space
result = result + " "
if len(result) == 0:
logger.trace(f"Morse code failed to match")
logger.trace("Morse code failed to match")
return None
# Remove trailing space
result = result[:-1]
@ -76,17 +77,15 @@ class MorseCode(ciphey.iface.Decoder[str, str]):
def priority() -> float:
return 0.05
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self.MORSE_CODE_DICT = config.get_resource(
self._params()["dict"], ciphey.iface.Translation
)
self.MORSE_CODE_DICT = config.get_resource(self._params()["dict"], Translation)
self.MORSE_CODE_DICT_INV = {v: k for k, v in self.MORSE_CODE_DICT.items()}
@staticmethod
def getParams() -> Optional[Dict[str, ciphey.iface.ParamSpec]]:
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"dict": ciphey.iface.ParamSpec(
"dict": ParamSpec(
desc="The morse code dictionary to use",
req=False,
default="cipheydists::translate::morse",
@ -95,4 +94,4 @@ class MorseCode(ciphey.iface.Decoder[str, str]):
@staticmethod
def getTarget() -> str:
return "morse"
return "morse_code"

View File

@ -1,21 +1,21 @@
from typing import Optional, Dict, List
from typing import Dict, Optional
from ciphey.iface import ParamSpec, Config, T, U, Decoder, registry
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class multiTap(Decoder[str, str]):
def decode(self, ctext: str) -> Optional[str]:
decode_text = ""
class Multi_tap(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
result = ""
for x in ctext.split():
if x == self.SPACE_DIGIT: # check if it space
decode_text += " "
elif not multiTap.valid_code_part(x):
if x == self.SPACE_DIGIT: # Check if it's a space
result += " "
elif not Multi_tap.valid_code_part(x):
return None
else:
decode_text += self.decode_num_to_char(x)
result += self.decode_num_to_char(x)
return decode_text
return result
@staticmethod
def valid_code_part(code: str) -> bool:
@ -23,7 +23,7 @@ class multiTap(Decoder[str, str]):
return False
# if not all the digits are the same
if not multiTap.is_all_dup(code):
if not Multi_tap.is_all_dup(code):
return False
if int(code[0]) not in range(2, 10):
@ -36,8 +36,8 @@ class multiTap(Decoder[str, str]):
@staticmethod
def decode_num_to_char(number: str) -> str:
index = multiTap.calculate_index(number)
return multiTap.number_index_to_char(index)
index = Multi_tap.calculate_index(number)
return Multi_tap.number_index_to_char(index)
@staticmethod
def is_all_dup(code):
@ -47,9 +47,9 @@ class multiTap(Decoder[str, str]):
def calculate_index(number: str) -> int:
first_number_as_int = int(number[0])
number_index = multiTap.get_index_from_first_digit(first_number_as_int)
number_index = Multi_tap.get_index_from_first_digit(first_number_as_int)
# add to index the number of the char : "22" -> index += 1
# Add to index the number of the char : "22" -> index += 1
num_rest_numbers = len(number) - 1
number_index += num_rest_numbers
@ -82,8 +82,8 @@ class multiTap(Decoder[str, str]):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
pass
return None
@staticmethod
def getTarget() -> str:
return "Multi-tap"
return "multi_tap"

View File

@ -1,30 +1,28 @@
from typing import Optional, Dict, Any
from typing import Dict, Optional
from loguru import logger
import ciphey
from ciphey.iface import registry
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Octal(ciphey.iface.Decoder[str, bytes]):
def decode(self, text: str) -> Optional[bytes]:
class Octal(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
It takes an octal string and return a string
:octal_str: octal str like "110 145 154"
Performs Octal decoding
"""
str_converted = []
octal_seq = text.split(" ")
octal_seq = ctext.split(" ")
if len(octal_seq) == 1:
# Concatted octal must be formed of octal triplets
if len(text) % 3 != 0:
if len(ctext) % 3 != 0:
return None
octal_seq = [text[i : i + 3] for i in range(0, len(text), 3)]
octal_seq = [ctext[i : i + 3] for i in range(0, len(ctext), 3)]
logger.trace(f"Trying chunked octal {octal_seq}")
try:
for octal_char in octal_seq:
if len(octal_char) > 3:
logger.trace(f"Octal subseq too long")
logger.trace("Octal subseq too long")
return None
n = int(octal_char, 8)
if (
@ -43,14 +41,13 @@ class Octal(ciphey.iface.Decoder[str, bytes]):
def priority() -> float:
return 0.025
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
@staticmethod
def getParams() -> Optional[Dict[str, Dict[str, Any]]]:
pass
def getParams() -> Optional[Dict[str, ParamSpec]]:
return None
@staticmethod
def getTarget() -> str:
return "octal"

View File

@ -1,10 +1,10 @@
from typing import Optional, Dict, List
from typing import Dict, Optional
from ciphey.iface import ParamSpec, Config, T, U, Decoder, registry
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register_multi((str, str), (bytes, bytes))
class Reverse(Decoder):
@registry.register
class Reverse(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
return ctext[::-1]
@ -17,7 +17,7 @@ class Reverse(Decoder):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
pass
return None
@staticmethod
def getTarget() -> str:

View File

@ -1,21 +1,23 @@
# by https://github.com/RustyDucky and https://github.com/lukasgabriel
from typing import Optional, Dict, List
from typing import Dict, Optional
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry, Translation
from ciphey.iface import Config, Decoder, ParamSpec, T, Translation, U, registry
@registry.register
class tap_code(Decoder[str, str]):
class Tap_code(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Tap code decoding
"""
try:
output = ""
result = ""
combinations = ctext.split(" ")
for fragment in combinations:
output += self.TABLE.get(fragment)
return output
except Exception as e:
result += self.TABLE.get(fragment)
return result
except Exception:
return None
@staticmethod

View File

@ -1,33 +1,38 @@
from typing import Optional, Dict, Any
from typing import Dict, Optional
from loguru import logger
import ciphey
from ciphey.iface import registry
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Utf8(ciphey.iface.Decoder[bytes, str]):
def decode(self, text: bytes) -> Optional[str]:
logger.trace("Attempting utf-8 decode")
class Utf8(Decoder[bytes]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs UTF-8 decoding
"""
logger.trace("Attempting UTF-8 decoder")
result = ""
try:
res = text.decode("utf8")
logger.debug(f"utf-8 decode gave '{res}'")
return res if len(res) != 0 else None
except UnicodeDecodeError:
logger.trace("utf-8 decode failed")
result = ctext.decode("utf-8")
if result != ctext:
logger.debug(f"UTF-8 successful, returning '{result}'")
return result
else:
return None
except Exception:
return None
@staticmethod
def priority() -> float:
return 0.9
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
@staticmethod
def getParams() -> Optional[Dict[str, Dict[str, Any]]]:
pass
def getParams() -> Optional[Dict[str, ParamSpec]]:
return None
@staticmethod
def getTarget() -> str:

View File

@ -1,23 +1,28 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from typing import Dict, Optional
from urllib.parse import unquote_plus
from loguru import logger
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Url(Decoder[str, str]):
class Url(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs URL decoding
"""
logger.trace("Attempting URL")
result = ""
try:
result = unquote_plus(ctext, errors="strict")
if result != ctext:
logger.debug(f"URL successful, returning '{result}'")
return result
else:
return None
except Exception:
logger.trace("Failed to decode URL")
return None
@staticmethod
@ -29,7 +34,7 @@ class Url(Decoder[str, str]):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
pass
return None
@staticmethod
def getTarget() -> str:

View File

@ -1,16 +1,14 @@
from typing import Optional, Dict
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from binascii import a2b_uu
from codecs import decode
from typing import Dict, Optional
from loguru import logger
from codecs import decode
from binascii import a2b_uu
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Uuencode(Decoder[str, str]):
class Uuencode(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
UUEncode (Unix to Unix Encoding) is a symmetric encryption
@ -31,8 +29,8 @@ class Uuencode(Decoder[str, str]):
# If there isn't a "being" prefix and "end" suffix, we use the binascii module instead
# It is possible that the ctext has multiple lines, so convert each line and append
ctext_split = list(filter(None, ctext.splitlines()))
for i in range(0, len(ctext_split)):
result += a2b_uu(ctext_split[i]).decode("utf-8")
for _, value in enumerate(ctext_split):
result += a2b_uu(value).decode("utf-8")
logger.debug(f"UUencode successful, returning '{result}'")
return result
except Exception:

View File

@ -1,14 +1,13 @@
from typing import Optional, Dict, List
from ciphey.iface import Config, ParamSpec, T, U, Decoder, registry
from typing import Dict, Optional
from loguru import logger
from zmq.utils import z85
from ciphey.iface import Config, Decoder, ParamSpec, T, U, registry
@registry.register
class Z85(Decoder[str, str]):
class Z85(Decoder[str]):
def decode(self, ctext: T) -> Optional[U]:
"""
Performs Z85 decoding

View File

@ -1,16 +1,22 @@
from typing import Optional, Dict, Any, Set
from functools import lru_cache
from typing import Any, Dict, Optional, Set
import cipheydists
import loguru
import ciphey
import cipheydists
from ciphey.iface import ParamSpec, Config, registry, WordList, Distribution, Translation
from ciphey.iface import (
Config,
Distribution,
ParamSpec,
ResourceLoader,
Translation,
WordList,
registry,
)
@registry.register_multi(WordList, Distribution, Translation)
class CipheyDists(ciphey.iface.ResourceLoader):
class CipheyDists(ResourceLoader):
# _wordlists: Set[str] = frozenset({"english", "english1000", "englishStopWords"})
# _brandons: Set[str] = frozenset({"english"})
# _dists: Set[str] = frozenset({"twist"})
@ -36,4 +42,4 @@ class CipheyDists(ciphey.iface.ResourceLoader):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
pass
return None

View File

@ -1,18 +1,22 @@
from abc import abstractmethod
from typing import Optional, Dict, Any, Set, Generic, Type
from functools import lru_cache
import ciphey
from ciphey.iface import T, ParamSpec, Config, get_args, registry
import json
import csv
import json
from functools import lru_cache
from typing import Dict, Generic, Optional, Set
from ciphey.iface import (
Config,
Distribution,
ParamSpec,
ResourceLoader,
T,
WordList,
registry,
)
# We can use a generic resource loader here, as we can instantiate it later
@registry.register_multi(ciphey.iface.WordList, ciphey.iface.Distribution)
class Json(ciphey.iface.ResourceLoader):
@registry.register_multi(WordList, Distribution)
class Json(ResourceLoader):
def whatResources(self) -> T:
return self._names
@ -28,18 +32,18 @@ class Json(ciphey.iface.ResourceLoader):
return "json"
@staticmethod
def getParams() -> Optional[Dict[str, ciphey.iface.ParamSpec]]:
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {"path": ParamSpec(req=True, desc="The path to a JSON file", list=True)}
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self._paths = self._params()["path"]
self._names = set(range(1, len(self._paths)))
# We can use a generic resource loader here, as we can instantiate it later
@registry.register_multi(ciphey.iface.WordList, ciphey.iface.Distribution)
class Csv(Generic[T], ciphey.iface.ResourceLoader[T]):
@registry.register_multi(WordList, Distribution)
class Csv(Generic[T], ResourceLoader[T]):
def whatResources(self) -> Set[str]:
return self._names
@ -56,10 +60,10 @@ class Csv(Generic[T], ciphey.iface.ResourceLoader[T]):
return "csv"
@staticmethod
def getParams() -> Optional[Dict[str, ciphey.iface.ParamSpec]]:
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {"path": ParamSpec(req=True, desc="The path to a CSV file", list=True)}
def __init__(self, config: ciphey.iface.Config):
def __init__(self, config: Config):
super().__init__(config)
self._paths = self._params()["path"]
self._names = set(range(1, len(self._paths)))

View File

@ -1,15 +1,18 @@
from collections import deque
import cipheycore
class Node:
"""
A node has a value assiocated with it
A node has a value associated with it
Calculated from the heuristic
"""
def __init__(
self, config, h: float = None, edges: (any, float) = None, ctext: str = None,
self,
config,
h: float = None,
edges: (any, float) = None,
ctext: str = None,
):
self.weight = h
# Edges is a list of other nodes it can connect to
@ -90,18 +93,18 @@ class Graph:
for v in open_list:
# TODO if v == decoder, run the decoder
print(f"The for loop node v is {v}")
if n == None or g[v] + self.h(v) < g[n] + self.h(n):
if n is None or g[v] + self.h(v) < g[n] + self.h(n):
n = v
print(f"The value of n is {n}")
if n == None:
if n is None:
print("Path does not exist!")
return None
# if the current node is the stop_node
# then we begin reconstructin the path from it to the start_node
# NOTE Uncomment this for an exit condition
# TODO Make it exit if decryptor returns True
# TODO Make it exit if decrypter returns True
# TODO We need to append the decryption methods to each node
# So when we reconstruct the path we can reconstruct the decryptions
# used
@ -179,5 +182,5 @@ graph1 = Graph(adjacency_list)
graph1.a_star_algorithm(A, D)
"""
Maybe after it
Maybe after it
"""

View File

@ -1,39 +1,32 @@
import bisect
import distutils
import math
import bisect
from copy import copy
from functools import lru_cache
from typing import (
Generic,
List,
Optional,
Dict,
Any,
Union,
TypeVar,
)
from ciphey.iface import (
T,
Cracker,
Config,
Searcher,
ParamSpec,
CrackInfo,
SearchLevel,
CrackResult,
SearchResult,
Decoder,
registry,
Checker,
)
from loguru import logger
import cipheycore
from dataclasses import dataclass
from functools import lru_cache
from typing import Any, Dict, Generic, List, Optional, TypeVar, Union
import cipheycore
from loguru import logger
from ciphey.iface import (
Checker,
Config,
Cracker,
CrackInfo,
CrackResult,
Decoder,
ParamSpec,
Searcher,
SearchLevel,
SearchResult,
T,
registry,
)
"""
We are using a tree structure here, because that makes searching and tracing back easier
As such, when we encounter another possible parent, we remove that edge
We are using a tree structure here, because that makes searching and tracing back easier
As such, when we encounter another possible parent, we remove that edge
"""
@ -131,9 +124,7 @@ class PriorityWorkQueue(Generic[PriorityType, T]):
_queues: Dict[Any, List[T]]
def add_work(self, priority: PriorityType, work: List[T]) -> None:
logger.trace(
f"""Adding work at depth {priority}"""
)
logger.trace(f"""Adding work at depth {priority}""")
idx = bisect.bisect_left(self._sorted_priorities, priority)
if (
@ -177,7 +168,7 @@ class AuSearch(Searcher):
@lru_cache() # To save extra sorting
def get_decoders_for(self, t: type):
ret = [j for i in registry[Decoder][t].values() for j in i]
ret = registry[Decoder[t]]
ret.sort(key=lambda x: x.priority(), reverse=True)
return ret
@ -194,7 +185,9 @@ class AuSearch(Searcher):
for i in self.get_crackers_for(type(res)):
inst = self._config()(i)
additional_work.append(Edge(source=node, route=inst, info=convert_edge_info(inst.getInfo(res))))
additional_work.append(
Edge(source=node, route=inst, info=convert_edge_info(inst.getInfo(res)))
)
priority = min(node.depth, self.priority_cap)
if self.invert_priority:
priority = -priority
@ -216,7 +209,7 @@ class AuSearch(Searcher):
except DuplicateNode:
continue
logger.trace(f"Nesting encodings")
logger.trace("Nesting encodings")
self.recursive_expand(new_node, False)
def recursive_expand(self, node: Node, nested: bool = True) -> None:
@ -299,9 +292,13 @@ class AuSearch(Searcher):
super().__init__(config)
self._checker: Checker = config.objs["checker"]
self.work = PriorityWorkQueue() # Has to be defined here because of sharing
self.invert_priority = bool(distutils.util.strtobool(self._params()["invert_priority"]))
self.invert_priority = bool(
distutils.util.strtobool(self._params()["invert_priority"])
)
self.priority_cap = int(self._params()["priority_cap"])
self.enable_nested = bool(distutils.util.strtobool(self._params()["enable_nested"]))
self.enable_nested = bool(
distutils.util.strtobool(self._params()["enable_nested"])
)
self.max_cipher_depth = int(self._params()["max_cipher_depth"])
if self.max_cipher_depth == 0:
self.max_cipher_depth = math.inf
@ -312,24 +309,32 @@ class AuSearch(Searcher):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
return {
"enable_nested": ParamSpec(req=False,
desc="Enables nested ciphers. "
"Incredibly slow, and not guaranteed to terminate",
default="False"),
"invert_priority": ParamSpec(req=False,
desc="Causes more complex encodings to be looked at first. "
"Good for deeply buried encodings.",
default="False"),
"max_cipher_depth": ParamSpec(req=False,
desc="The depth at which we stop trying to crack ciphers. "
"Set to 0 to disable",
default="0"),
"max_depth": ParamSpec(req=False,
desc="The depth at which we give up. "
"Set to 0 to disable",
default="0"),
"priority_cap": ParamSpec(req=False,
desc="Sets the maximum depth before we give up ordering items.",
default="2"),
"enable_nested": ParamSpec(
req=False,
desc="Enables nested ciphers. "
"Incredibly slow, and not guaranteed to terminate",
default="False",
),
"invert_priority": ParamSpec(
req=False,
desc="Causes more complex encodings to be looked at first. "
"Good for deeply buried encodings.",
default="False",
),
"max_cipher_depth": ParamSpec(
req=False,
desc="The depth at which we stop trying to crack ciphers. "
"Set to 0 to disable",
default="0",
),
"max_depth": ParamSpec(
req=False,
desc="The depth at which we give up. " "Set to 0 to disable",
default="0",
),
"priority_cap": ParamSpec(
req=False,
desc="Sets the maximum depth before we give up ordering items.",
default="2",
),
}

View File

@ -9,33 +9,33 @@ class Imperfection:
To calculate current, we push the entire graph to A*
And it calculates the next node to choose, as well as increasing the size
And it calculates the next node to choose, as well as increasing the size
of the graph with values
We're using a heap, meaing the element at [0] is always the smallest element
We're using a heap, meaning the element at [0] is always the smallest element
So we choose that and return it.
The current A* implemnentation has an end, we simply do not let it end as LC will make it
end far before itreaches Searcher again.
The current A* implementation has an end, we simply do not let it end as LC will make it
end far before it reaches Searcher again.
Current is the start position, so if we say we always start at the start of the graph it'll
go through the entire graph
graph = {
Node: [
{Node :
{Node :
{
node
}
}
]
}
}
For encodings we just do them straight out
The last value of parents from abstract
The last value of parents from abstract
"""
"""
@ -50,7 +50,7 @@ class Imperfection:
def __init__(self):
None
def findBestNode(nodes):
def findBestNode(self, nodes):
"""Finds the best decryption module"""
return next(iter(nodes))
@ -66,9 +66,9 @@ class Imperfection:
# Current appears to be the list of all new tiles we can reach from current location
# End is the end node, that won't actually run bc LC will make it return before it hits aSTar function
# so tbh I'll just make it infinitite unless something else forces a return
# so tbh I'll just make it infinite unless something else forces a return
# The graph is the actual data structure used. According to StackOvervlow, it looks like this:
# The graph is the actual data structure used. According to StackOverflow, it looks like this:
# graph = {'A': ['B', 'C'],
# 'B': ['C', 'D'],
@ -196,7 +196,7 @@ class Imperfection:
class Node:
"""
A node has a value assiocated with it
A node has a value associated with it
Calculated from the heuristic
"""

View File

@ -1,28 +1,15 @@
from abc import abstractmethod
from typing import Set, Any, Union, List, Optional, Dict, Tuple
from typing import Dict, Optional, Set
from loguru import logger
from ciphey.iface import Config, ParamSpec, registry
from .ausearch import Node, AuSearch
from ciphey.iface import (
SearchLevel,
Config,
registry,
CrackResult,
Searcher,
ParamSpec,
Decoder,
DecoderComparer,
)
import bisect
from .ausearch import AuSearch, Node
@registry.register
class Perfection(AuSearch):
@staticmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
pass
return None
def findBestNode(self, nodes: Set[Node]) -> Node:
return next(iter(nodes))

View File

@ -1,10 +1,10 @@
"""
https://github.com/ciphey
https://github.com/Ciphey
https://github.com/Ciphey/Ciphey/wiki
The cycle goes:
@ -13,24 +13,16 @@ one_level_of_decryption -> decrypt_normal
"""
import os
import warnings
import argparse
import sys
from typing import Optional, Dict, Any, List, Union
import bisect
from typing import Any, Optional, Union
from ciphey.iface import SearchLevel, registry
from . import iface
from rich import print
from loguru import logger
import click
import click_spinner
from appdirs import AppDirs
import yaspin
from appdirs import AppDirs
from loguru import logger
from rich import print
from yaspin.spinners import Spinners
from yaspin import yaspin
import time
from . import iface
warnings.filterwarnings("ignore")
@ -64,7 +56,10 @@ def print_help(ctx):
@click.command()
@click.option(
"-t", "--text", help="The ciphertext you want to decrypt.", type=str,
"-t",
"--text",
help="The ciphertext you want to decrypt.",
type=str,
)
@click.option(
"-q", "--quiet", help="Decrease verbosity", type=int, count=True, default=None
@ -86,13 +81,20 @@ def print_help(ctx):
)
@click.option("-w", "--wordlist", help="Uses the given wordlist")
@click.option(
"-p", "--param", help="Passes a parameter to the language checker", multiple=True,
"-p",
"--param",
help="Passes a parameter to the language checker",
multiple=True,
)
@click.option(
"-l", "--list-params", help="List the parameters of the selected module", type=bool,
"-l",
"--list-params",
help="List the parameters of the selected module",
type=bool,
)
@click.option(
"--searcher", help="Select the searching algorithm to use",
"--searcher",
help="Select the searching algorithm to use",
)
# HARLAN TODO XXX
# I switched this to a boolean flag system
@ -129,19 +131,19 @@ def print_help(ctx):
@click.argument("text_stdin", callback=get_name, required=False)
def main(**kwargs):
"""Ciphey - Automated Decryption Tool
Documentation:
Documentation:
https://github.com/Ciphey/Ciphey/wiki\n
Discord (support here, we're online most of the day):
https://discord.ciphey.online/\n
GitHub:
GitHub:
https://github.com/ciphey/ciphey\n
Ciphey is an automated decryption tool using smart artificial intelligence and natural language processing. Input encrypted text, get the decrypted text back.
Examples:\n
Basic Usage: ciphey -t "aGVsbG8gbXkgbmFtZSBpcyBiZWU="
Basic Usage: ciphey -t "aGVsbG8gbXkgbmFtZSBpcyBiZWU="
"""
"""Function to deal with arguments. Either calls with args or not. Makes Pytest work.
@ -152,7 +154,6 @@ def main(**kwargs):
we then update locals() with the new command line args and remove "withArgs"
This function then calls call_encryption(**result) which passes our dict of args
to the function as its own arguments using dict unpacking.
Returns:
The output of the decryption.
"""
@ -243,9 +244,9 @@ def main(**kwargs):
if issubclass(config.objs["format"], type(kwargs["text"])):
pass
elif config.objs["format"] == str and type(kwargs["text"]) is bytes:
elif config.objs["format"] == str and isinstance(kwargs["text"], bytes):
kwargs["text"] = kwargs["text"].decode("utf-8")
elif config.objs["format"] == bytes and type(kwargs["text"]) is str:
elif config.objs["format"] == bytes and isinstance(kwargs["text"], str):
kwargs["text"] = kwargs["text"].encode("utf-8")
else:
raise TypeError(f"Cannot load type {config.format} from {type(kwargs['text'])}")

View File

@ -1,18 +1,25 @@
"""Some useful adapters"""
from typing import Any, List
import inspect
import cipheycore
from typing import Any
def id_lambda(value: Any):
"""
A function used in dynamic class generation that abstracts away a constant return value (like in getName)
A function used in dynamic class generation that abstracts away a constant return value (like in getName)
"""
return lambda *args: value
def fix_case(target: str, base: str) -> str:
"""Returns the lower-case string target with the case of base"""
ret = ''.join([target[i].upper() if base[i].isupper() else target[i] for i in range(len(target))])
return ''.join([target[i].upper() if base[i].isupper() else target[i] for i in range(len(target))])
ret = "".join(
[
target[i].upper() if base[i].isupper() else target[i]
for i in range(len(target))
]
)
return "".join(
[
target[i].upper() if base[i].isupper() else target[i]
for i in range(len(target))
]
)

View File

@ -1,26 +1,25 @@
from ._config import Config
from ._modules import (
Checker,
Cracker,
CrackInfo,
CrackResult,
Decoder,
DecoderComparer,
Cracker,
CrackResult,
CrackInfo,
Checker,
Searcher,
SearchResult,
SearchLevel,
ResourceLoader,
ParamSpec,
WordList,
Distribution,
Translation,
ParamSpec,
PolymorphicChecker,
ResourceLoader,
Searcher,
SearchLevel,
SearchResult,
T,
Translation,
U,
WordList,
pretty_search_results,
PolymorphicChecker
)
from ._registry import get_args, get_origin
from ._fwd import registry

View File

@ -1,24 +1,14 @@
import datetime
import os
from typing import (
Any,
Dict,
Optional,
List,
Type,
Union,
Callable,
)
import pydoc
from typing import Any, Callable, Dict, List, Optional, Type, Union
import appdirs
import yaml
from loguru import logger
import datetime
import yaml
import appdirs
from . import _fwd
from ._modules import Checker, Searcher, ResourceLoader, PolymorphicChecker
from ._modules import PolymorphicChecker, ResourceLoader, Searcher
class Cache:
@ -28,7 +18,7 @@ class Cache:
self._cache: Dict[Any, Dict[str, Any]] = {}
def mark_ctext(self, ctext: Any) -> bool:
if (type(ctext) == str or type(ctext) == bytes) and len(ctext) < 4:
if (isinstance(ctext, str) or isinstance(ctext, bytes)) and len(ctext) < 4:
logger.trace(f"Candidate {ctext.__repr__()} too short!")
return False
@ -98,7 +88,7 @@ class Config:
def instantiate(self, t: type) -> Any:
"""
Used to enable caching of a instantiated type after the configuration has settled
Used to enable caching of a instantiated type after the configuration has settled
"""
# We cannot use set default as that would construct it again, and throw away the result
res = self._inst.get(t)
@ -138,7 +128,9 @@ class Config:
# Checkers do not depend on any other config object
logger.trace(f"Registry is {_fwd.registry._reg[PolymorphicChecker]}")
self.objs["checker"] = self(_fwd.registry.get_named(self.checker, PolymorphicChecker))
self.objs["checker"] = self(
_fwd.registry.get_named(self.checker, PolymorphicChecker)
)
# Searchers only depend on checkers
self.objs["searcher"] = self(_fwd.registry.get_named(self.searcher, Searcher))
@ -159,9 +151,10 @@ class Config:
else:
verbosity_name = quiet_list[min(len(quiet_list), -verbosity) - 1]
from loguru import logger
import sys
from loguru import logger
logger.remove()
if self.verbosity is None:
return

View File

@ -1,22 +1,5 @@
from abc import ABC, abstractmethod
from typing import (
Any,
Callable,
Dict,
Generic,
Optional,
List,
NamedTuple,
TypeVar,
Type,
Union,
Set,
)
import pydoc
from loguru import logger
import datetime
from typing import Any, Dict, Generic, List, NamedTuple, Optional, Set, Type, TypeVar
from ._fwd import config as Config
@ -48,14 +31,14 @@ class ConfigurableModule(ABC):
@abstractmethod
def getParams() -> Optional[Dict[str, ParamSpec]]:
"""
Returns a dictionary of `argument name: argument specification`
Returns a dictionary of `argument name: argument specification`
"""
pass
def _checkParams(self):
"""
Fills the given params dict with default values where arguments are not given,
using None as the default value for default values
Fills the given params dict with default values where arguments are not given,
using None as the default value for default values
"""
params = self._params()
@ -179,7 +162,7 @@ class Checker(Generic[T], ConfigurableModule):
# def __init__(self, config: Config): super().__init__(config)
class Decoder(Generic[T, U], ConfigurableModule, Targeted):
class Decoder(Generic[T], ConfigurableModule, Targeted):
"""Represents the undoing of some encoding into a different (or the same) type"""
@abstractmethod
@ -243,7 +226,7 @@ class Cracker(Generic[T], ConfigurableModule, Targeted):
@abstractmethod
def attemptCrack(self, ctext: T) -> List[CrackResult]:
"""
This should attempt to crack the cipher `target`, and return a list of candidate solutions
This should attempt to crack the cipher `target`, and return a list of candidate solutions
"""
# FIXME: Actually CrackResult[T], but python complains
pass
@ -260,21 +243,21 @@ class ResourceLoader(Generic[T], ConfigurableModule):
@abstractmethod
def whatResources(self) -> Optional[Set[str]]:
"""
Return a set of the names of instances T you can provide.
The names SHOULD be unique amongst ResourceLoaders of the same type
Return a set of the names of instances T you can provide.
The names SHOULD be unique amongst ResourceLoaders of the same type
These names will be exposed as f"{self.__name__}::{name}", use split_resource_name to recover this
These names will be exposed as f"{self.__name__}::{name}", use split_resource_name to recover this
If you cannot reasonably determine what resources you provide, return None instead
If you cannot reasonably determine what resources you provide, return None instead
"""
pass
@abstractmethod
def getResource(self, name: str) -> T:
"""
Returns the requested distribution
Returns the requested distribution
The behaviour is undefined if `name not in self.what_resources()`
The behavior is undefined if `name not in self.what_resources()`
"""
pass

View File

@ -1,30 +1,12 @@
from abc import ABC, abstractmethod
from collections import defaultdict
from typing import (
Any,
Callable,
Dict,
Generic,
Optional,
List,
NamedTuple,
TypeVar,
Type,
Union,
Set,
Tuple,
)
import pydoc
from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union
try:
from typing import get_origin, get_args
from typing import get_args, get_origin
except ImportError:
from typing_inspect import get_origin, get_args
from loguru import logger
from . import _fwd
from ._modules import *
import datetime
class Registry:
@ -91,9 +73,13 @@ class Registry:
# Replace input type with polymorphic checker if required
if issubclass(input_type, Checker):
if len(args) == 0:
arg = [get_args(i) for i in input_type.__orig_bases__ if get_origin(i) == Checker][0]
arg = [
get_args(i)
for i in input_type.__orig_bases__
if get_origin(i) == Checker
][0]
if len(arg) != 1:
raise TypeError(f"No argument for Checker")
raise TypeError("No argument for Checker")
input_type = input_type.convert({arg[0]})
else:
input_type = input_type.convert(set(args))

View File

@ -1,9 +1,9 @@
"""
© Brandon Skerritt
Github: brandonskerritt
@ -14,9 +14,10 @@ Class to provide helper functions for mathematics
from collections import OrderedDict
from string import punctuation
from loguru import logger
from typing import Optional
from loguru import logger
class mathsHelper:
"""Class to provide helper functions for mathematics and other small things"""
@ -189,17 +190,16 @@ class mathsHelper:
return bool(lambda s: len(s) == len(s.encode()))
@staticmethod
def strip_puncuation(text: str) -> str:
def strip_punctuation(text: str) -> str:
"""Strips punctuation from a given string.
Uses string.puncuation.
Uses string.punctuation.
Args:
text -> the text to strip puncuation from.
text -> the text to strip punctuation from.
Returns:
Returns string without puncuation.
Returns string without punctuation.
"""
text: str = (str(text).translate(str.maketrans("", "", punctuation))).strip(
"\n"

View File

@ -1,10 +1,10 @@
"""
The file for Nox
"""
import nox
from typing import Any
import nox
from nox.sessions import Session
import tempfile
locations = "ciphey/", "tests/", "docs/"
nox.options.sessions = "safety", "tests"

View File

@ -14,8 +14,8 @@ rich = ">=4,<10"
loguru = "^0.5.0"
pylint = "^2.5.2"
flake8 = "^3.8.2"
cipheydists = "^0.3.31"
cipheycore = "^0.3.1"
cipheydists = "^0.3.33"
cipheycore = "^0.3.2"
appdirs = "^1.4.4"
typing_inspect = { version = "^0.6.0", python = "~3.7" }
base58 = "^2.0.1"

View File

@ -13,7 +13,7 @@ If you have more tests, let me know and I can factor them in.
In your first reply:
https://github.com/Ciphey/Ciphey/issues/90#issuecomment-645046918
Point 3:
> Be aware that the stuff passed to the checker will most likely be complete gibberish (with a similar freq dist) OR the correct result. A user will not care about an extra second spent on the final correct result, but really will care that every false candidate takes an extra second. The current suggestion seems to be pessimal for the gibberish inputs: maybe add some sanity checks (have I failed to match any word, have I failed to lemmatise any word, etc.)
> Be aware that the stuff passed to the checker will most likely be complete gibberish (with a similar freq dist) OR the correct result. A user will not care about an extra second spent on the final correct result, but really will care that every false candidate takes an extra second. The current suggestion seems to be pessimal for the gibberish inputs: maybe add some sanity checks (have I failed to match any word, have I failed to lemmatize any word, etc.)
I decided to test how well `lem` worked as phase 1. To do this, I created this program:
```python
@ -36,7 +36,6 @@ import spacy
import random
import time
from statistics import mean
import ciphey
import enciphey
from alive_progress import alive_bar
@ -75,10 +74,10 @@ def perform():
total = 0
true_returns = 0
# calculate aveager time
# calculate average time
time_list = []
# average sentance size
# average sentence size
sent_size_list = []
items = range(20000)
with alive_bar(len(items)) as bar:
@ -117,7 +116,7 @@ perform()
```
The results were fascinating, to say the least.
With a 50/50 chance of the text being gibberish (ciphertext from enCiphey) or sentences from Hansard.txt, we had these results for using lemmization as phase 1:
With a 50/50 chance of the text being gibberish (ciphertext from enCiphey) or sentences from Hansard.txt, we had these results for using lemmatization as phase 1:
```
The accuracy is 49.63%
@ -138,7 +137,7 @@ I will create a table of my results:
| Name | Speed | Accuracy | String Size Average Chars | Epochs | Max Sentence Size |
| -------------------------- | ---------------------------- | -------- | ------------------------- | ------ | ----------------- |
| Lemmization (lem) | 0.02 seconds | 50% | 1580 | 20,000 | 50 |
| Lemmatization (lem) | 0.02 seconds | 50% | 1580 | 20,000 | 50 |
| Stop word removal | 3.05465052884756e-05 seconds | 96% | 1596 | 20,000 | 50 |
| Check1000Words | 0.0005 seconds | 96% | 1597 | 20,000 | 50 |
| Word endings | 0.0009 seconds | 95% | 1597 | 20,000 | 50 |
@ -147,7 +146,7 @@ I will create a table of my results:
| Name | Speed | Accuracy | String Size Average Chars | Epochs | Max Sentence Size |
| -------------------------- | ------------------------------ | -------- | ------------------------- | ------ | ----------------- |
| Lemmization (lem) |
| Lemmatization (lem) |
| Stop word removal | 1.1574924453998391e-05 seconds | 93% | 569 | 20,000 | 5 |
| Check1000Words | 0.0006 seconds | 95% | 586 | 20,000 | 5 |
| Word endings | 0.0003 seconds | 92% | 482 | 20,000 | 5 |
@ -155,14 +154,14 @@ I will create a table of my results:
| Name | Speed | Accuracy | Threshold | String Size Average Chars | Epochs | Max Sentence Size |
| -------------------------- | ------------------------------- | -------- | ------ |------------------------- | ------ | ----------------- |
| Lemmization (lem) |
| Lemmatization (lem) |
| Stop word removal | 1.2532061150591289e-05. seconds | 50% | 481 | 20,000 | 1 |
| Check1000Words | 0.0006 seconds | 95% | 586 | 20,000 | 5 |
| Word endings | 0.0002 seconds | 86% | 15| 482 | 20,000 | 1 |
## Confusion Matrices & Notes
### Lemization
### Lemmatization
```
Positive Negative
@ -297,7 +296,7 @@ However, the accuracy for smaller sentence sizes tanked.
The highest accuracy we had was with the original one. Words <= 2 chars and no limit on threshold.
If possible, we want to combine the high accuracy on smaller texts while maintaining the generalisation found in the latter checker results.
If possible, we want to combine the high accuracy on smaller texts while maintaining the generalization found in the latter checker results.
The reason we want a smaller threshold is that due to the chunking procedure, it will be much faster on larger texts. The lower the sentence length the higher the threshold is allowed to be.

View File

@ -1,9 +1,11 @@
import unittest
from loguru import logger
from ciphey.basemods.Checkers.brandon import Brandon
config = dict()
lc = config["checker"](config)
import unittest
from loguru import logger
logger.remove()

View File

@ -1,18 +1,15 @@
import random
import nltk
from nltk.tokenize.treebank import TreebankWordDetokenizer
import random
import base64
import binascii
import cipheydists
import random
import re
import string
import cipheycore
import cipheydists
import base58
import base62
import re
import lukas
import cipheycore
import cipheydists
import nltk
from nltk.tokenize.treebank import TreebankWordDetokenizer
class encipher:
@ -48,11 +45,11 @@ class encipher_crypto: # pragma: no cover
"""Holds the encryption functions
can randomly select an encryption function use on text
returns:
{"text": t, "plaintext": c, "cipher": p, "suceeds": False}
{"text": t, "plaintext": c, "cipher": p, "succeeds": False}
where suceeds is whether or not the text is really encrypted or falsely decrypted
where succeeds is whether or not the text is really encrypted or falsely decrypted
Uses Cyclic3's module generate psuedo random text"""
Uses Cyclic3's module generate pseudo random text"""
def __init__(self): # pragma: no cover
self.methods = [
@ -93,13 +90,13 @@ class encipher_crypto: # pragma: no cover
return {"PlainText": text, "EncryptedText": encryptedText, "CipherUsed": name}
def Base64(self, text: str) -> str: # pragma: no cover
"""Turns text in base64 using Python libray
"""Turns text into Base64 using Python library
args:
text -> text convert
args:
text -> text convert
returns:
text -> as base 64"""
returns:
text -> as Base64"""
return base64.b64encode(bytes(text, "utf-8")).decode("utf-8")
def Caesar(self, s, k): # pragma: no cover
@ -118,23 +115,23 @@ class encipher_crypto: # pragma: no cover
return c
def Base32(self, text: str) -> str: # pragma: no cover
"""Turns text in base64 using Python libray
"""Turns text in Base32 using Python library
args:
text -> text convert
args:
text -> text convert
returns:
text -> as base 64"""
returns:
text -> as Base32"""
return base64.b32encode(bytes(text, "utf-8")).decode("utf-8")
def Base16(self, text: str) -> str: # pragma: no cover
"""Turns text in base64 using Python libray
"""Turns text in Base16 using Python library
args:
text -> text convert
args:
text -> text convert
returns:
text -> as base 64"""
returns:
text -> as Base16"""
return base64.b16encode(bytes(text, "utf-8")).decode("utf-8")
def Binary(self, text: str) -> str: # pragma: no cover
@ -152,7 +149,7 @@ class encipher_crypto: # pragma: no cover
morse = []
for i in text:
m = self.morse_dict.get(i.upper())
if m == None:
if m is None:
m = ""
morse.append(m)
@ -189,5 +186,3 @@ class encipher_crypto: # pragma: no cover
def b62(self, text: str):
return base62.decode(str(re.sub(r"[^A-Za-z1-9]+", "", text)))

View File

@ -18,17 +18,17 @@ for i in range(1, 20000):
grabCipher = grabCipher()
# this returns a random cipher, encrypted text and plaintext combo
toAppend ='''
def test_{cipher}_{suceeds}_{plaintext[0:10]}(textToTest):
def test_{cipher}_{succeeds}_{plaintext[0:10]}(textToTest):
cipheyObj = ciphey(text)
output = cipheyObj.decrypt()
assert(output, {plaintext})
'''
file.append()
"""
import uuid
import enciphey
import string
import random
import string
import enciphey
from rich.progress import track
@ -69,7 +69,7 @@ def test_{cipher['Encrypted Texts']['CipherUsed']}_{id}():
cfg["debug"] = "TRACE"
result = main(cfg)
assert result["IsPlaintext?"] == True
assert result["IsPlaintext?"] == True
"""
def randomString(self, stringLength):

View File

@ -1,7 +1,9 @@
from ciphey.LanguageChecker import LanguageChecker
import unittest
from loguru import logger
from ciphey.LanguageChecker import LanguageChecker
logger.remove()
@ -24,7 +26,7 @@ class testIntegration(unittest.TestCase):
def test_basics_quickbrownfox(self):
"""
This returns true becaue by default chi squared returns true so long as it's less than 10 items it's processed
This returns true because by default chi squared returns true so long as it's less than 10 items it's processed
"""
lc = LanguageChecker.Checker()
result = lc.check("The quick brown fox jumped over the lazy dog")
@ -32,7 +34,7 @@ class testIntegration(unittest.TestCase):
def test_basics_quickbrownfox(self):
"""
This returns true becaue by default chi squared returns true so long as it's less than 10 items it's processed
This returns true because by default chi squared returns true so long as it's less than 10 items it's processed
"""
lc = LanguageChecker.Checker()
result = lc.check("The quick brown fox jumped over the lazy dog")
@ -109,7 +111,7 @@ class testIntegration(unittest.TestCase):
def test_integration_addition(self):
"""
Makes sure you can add 2 lanuggae objecs together
Makes sure you can add 2 language objects together
"""
lc = LanguageChecker.Checker()
result = lc.check("hello my darling")
@ -128,7 +130,7 @@ class testIntegration(unittest.TestCase):
"""
I had a bug with this exact string
Bug is that chi squared does not score this as True
"""
"""
text = """Charles Babbage, FRS (26 December 1791 - 18 October 1871) was an English mathematician, philosopher, inventor and mechanical engineer who originated the concept of a programmable computer. Considered a "father of the computer", Babbage is credited with inventing the first mechanical computer that eventually led to more complex designs. Parts of his uncompleted mechanisms are on display in the London Science Museum. In 1991, a perfectly functioning difference engine was constructed from Babbage's original plans. Built to tolerances achievable in the 19th century, the success of the finished engine indicated that Babbage's machine would have worked. Nine years later, the Science Museum completed the printer Babbage had designed for the difference engine."""
lc = LanguageChecker.Checker()
result = lc.check(text)

View File

@ -1,6 +1,7 @@
import cipheydists
import random
import cipheydists
class galactic_encode:
"""
@ -50,7 +51,7 @@ class XY_encrypt:
"""
Encrypts an input string using binary substitution (called XandY in Ciphey) in which
first, the input string is converted to its binary representation and then the 0s and 1s
of the binary string are replaced with any two characters.
of the binary string are replaced with any two characters.
- flip: Which of the two possible rotations of the substitute characters is used?
- randomize: If True, random spaces are inserted into the cstring, which Ciphey can handle.
- key: Which two characters are used to represent the 0s and 1s?
@ -91,6 +92,6 @@ class XY_encrypt:
self.ctext = self.ctext.replace(str(int(self.flip)), one).replace(
str(int(not self.flip)), two
)
self.ctext = self.randomizer() if self.randomize == True else self.ctext
self.ctext = self.randomizer() if self.randomize is True else self.ctext
return self.ctext

View File

@ -1,81 +1,25 @@
import pytest
from ciphey import decrypt
from ciphey.iface import Config
import pytest
answer_str = "Hello my name is bee and I like dog and apple and tree"
def test_plaintext():
res = decrypt(Config.library_default().complete_config(), answer_str)
print(res)
assert res == answer_str
def test_base64():
def test_affine():
res = decrypt(
Config().library_default().complete_config(),
"SGVsbG8gbXkgbmFtZSBpcyBiZWUgYW5kIEkgbGlrZSBkb2cgYW5kIGFwcGxlIGFuZCB0cmVl",
"Ihsst bf kxbh rd ghh xky R srjh ytz xky xccsh xky muhh",
)
assert res == answer_str
def test_caesar():
def test_ascii_shift():
res = decrypt(
Config().library_default().complete_config(),
"Uryyb zl anzr vf orr naq V yvxr qbt naq nccyr naq gerr",
'"?FFIzGSzH;G?zCMz<??z;H>z#zFCE?z>IAz;H>z;JJF?z;H>zNL??',
)
assert res == answer_str
def test_binary_base64_caesar():
res = decrypt(
Config().library_default().complete_config(),
"01010110 01011000 01001010 00110101 01100101 01010111 01001001 01100111 01100101 01101101 01110111 "
"01100111 01011001 01010111 00110101 00110110 01100011 01101001 01000010 00110010 01011010 01101001 "
"01000010 01110110 01100011 01101110 01001001 01100111 01100010 01101101 01000110 01111000 01001001 "
"01000110 01011001 01100111 01100101 01011000 01011010 00110100 01100011 01101001 01000010 01111000 "
"01011001 01101110 01010001 01100111 01100010 01101101 01000110 01111000 01001001 01000111 00110101 "
"01101010 01011001 00110011 01101100 01111001 01001001 01000111 00110101 01101000 01100011 01010011 "
"01000010 01101110 01011010 01011000 01001010 01111001 00001010",
)
assert res == answer_str
def test_vigenere():
res = decrypt(
Config().library_default().complete_config(),
"Rijvs ki rywi gc fco eln M jsoc nse krb ktnvi yxh rbic",
)
assert res == answer_str
def test_binary():
res = decrypt(
Config().library_default().complete_config(),
"01001000 01100101 01101100 01101100 01101111 00100000 01101101 01111001 00100000 01101110 01100001 "
"01101101 01100101 00100000 01101001 01110011 00100000 01100010 01100101 01100101 00100000 01100001 "
"01101110 01100100 00100000 01001001 00100000 01101100 01101001 01101011 01100101 00100000 01100100 "
"01101111 01100111 00100000 01100001 01101110 01100100 00100000 01100001 01110000 01110000 01101100 "
"01100101 00100000 01100001 01101110 01100100 00100000 01110100 01110010 01100101 01100101",
)
assert res == answer_str
def test_hex():
res = decrypt(
Config().library_default().complete_config(),
"48656c6c6f206d79206e616d652069732062656520616e642049206c696b6520646f6720616e64206170706c6520616e6420"
"74726565",
)
assert res == answer_str
assert res.lower() == answer_str.lower()
def test_atbash():
@ -86,55 +30,15 @@ def test_atbash():
assert res == answer_str
def test_galactic():
def test_base32():
res = decrypt(
Config().library_default().complete_config(),
"⍑ᒷꖎꖎ𝙹 ᒲ|| リᔑᒲᒷ ╎ᓭ ʖᒷᒷ ᔑリ↸ i ꖎ╎ꖌᒷ ↸𝙹⊣ ᔑリ↸ ᔑ!¡!¡ꖎᒷ ᔑリ↸ ℸ ̣ ∷ᒷᒷ",
"JBSWY3DPEBWXSIDOMFWWKIDJOMQGEZLFEBQW4ZBAJEQGY2LLMUQGI33HEBQW4ZBAMFYHA3DFEBQW4ZBAORZGKZI=",
)
assert res.lower() == answer_str.lower()
def test_galactic_Xproblem():
res = decrypt(
Config().library_default().complete_config(),
"⍑ᔑꖎ╎⎓ᔑ ̇/, ̇/||ꖎ𝙹!¡⍑𝙹リᒷ, ᔑ ̇/ ᔑꖎ𝙹リᒷ ᔑリ↸ ̇/ᒷ∷𝙹 ̇/ ⎓∷𝙹ᒲ 𝙹 ̇/⎓𝙹∷↸"
)
assert res.lower() == "halifax, xylophone, a x alone and xerox from oxford"
def test_XandY():
res = decrypt(
Config().library_default().complete_config(),
"xDDxDxxx xDDxxDxD xDDxDDxx xDDxDDxx xDDxDDDD xxDxxxxx xDDxDDxD xDDDDxxD xxDxxxxx xDDxDDDx xDDxxxxD xDDxDDxD xDDxxDxD xxDxxxxx xDDxDxxD xDDDxxDD xxDxxxxx xDDxxxDx xDDxxDxD xDDxxDxD xxDxxxxx xDDxxxxD xDDxDDDx xDDxxDxx xxDxxxxx xDxxDxxD xxDxxxxx xDDxDDxx xDDxDxxD xDDxDxDD xDDxxDxD xxDxxxxx xDDxxDxx xDDxDDDD xDDxxDDD xxDxxxxx xDDxxxxD xDDxDDDx xDDxxDxx xxDxxxxx xDDxxxxD xDDDxxxx xDDDxxxx xDDxDDxx xDDxxDxD xxDxxxxx xDDxxxxD xDDxDDDx xDDxxDxx xxDxxxxx xDDDxDxx xDDDxxDx xDDxxDxD xDDxxDxD",
)
assert res.lower() == answer_str.lower()
def leet():
res = decrypt(
Config().library_default().complete_config(),
"|-|3770 my nam3 is 833 and 1 lIke D06 AND 4|>|>13 4 7R33",
)
assert res.lower() == answer_str
def test_new_line_strip_and_return():
# Language Checker should return True by stripping new line
# but the new line should be returned to the user as new lines are important
res = decrypt(Config().library_default().complete_config(), "pass\n")
assert res.lower() == "pass\n"
def test_new_line_at_start_returns():
# Language Checker should return True by stripping new line
# but the new line should be returned to the user as new lines are important
res = decrypt(Config().library_default().complete_config(), "\npass\n")
assert res.lower() == "\npass\n"
def test_base58_normal():
def test_base58_bitcoin():
res = decrypt(
Config().library_default().complete_config(),
"6qYhNwsP46Mn4gy6gyANfsMm2icAxGFA6gnFjVm9phYHeby7PZm3vthiXxSU77teQgTFGbHETn",
@ -158,20 +62,13 @@ def test_base62():
assert res.lower() == answer_str.lower()
def test_base91():
def test_base64():
res = decrypt(
Config().library_default().complete_config(),
">OwJh>=/fV@$x88j9ZNKB*ge$yV%lE%ZKi,<d,TX2$0t,,cjPD@JY<UCHRWznuWoQPD",
"SGVsbG8gbXkgbmFtZSBpcyBiZWUgYW5kIEkgbGlrZSBkb2cgYW5kIGFwcGxlIGFuZCB0cmVl",
)
assert res.lower() == answer_str.lower()
def test_decimal():
res = decrypt(
Config().library_default().complete_config(),
"72 101 108 108 111 32 109 121 32 110 97 109 101 32 105 115 32 98 101 101 32 97 110 100 32 73 32 108 105 107 101 32 100 111 103 32 97 110 100 32 97 112 112 108 101 32 97 110 100 32 116 114 101 101",
)
assert res.lower() == answer_str.lower()
assert res == answer_str
def test_base69():
@ -182,7 +79,107 @@ def test_base69():
assert res == answer_str
def test_base69():
def test_base85():
res = decrypt(
Config().library_default().complete_config(),
"87cURD]inB+DtV)AKY].+C\\nn+CT.u+A!\\lBkq9&A8c*'@;]Tu@;p1%AKYE!A0>u7ARt",
)
assert res.lower() == answer_str.lower()
def test_base91():
res = decrypt(
Config().library_default().complete_config(),
">OwJh>=/fV@$x88j9ZNKB*ge$yV%lE%ZKi,<d,TX2$0t,,cjPD@JY<UCHRWznuWoQPD",
)
assert res.lower() == answer_str.lower()
def test_baudot():
res = decrypt(
Config().library_default().complete_config(),
"10100 00001 10010 10010 11000 00100 11100 10101 00100 01100 00011 11100 00001 00100 00110 00101 00100 11001 00001 00001 00100 00011 01100 01001 00100 00110 00100 10010 00110 01111 00001 00100 01001 11000 11010 00100 00011 01100 01001 00100 00011 10110 10110 10010 00001 00100 00011 01100 01001 00100 10000 01010 00001 00001",
)
assert res == answer_str.upper()
def test_binary():
res = decrypt(
Config().library_default().complete_config(),
"01001000 01100101 01101100 01101100 01101111 00100000 01101101 01111001 00100000 01101110 01100001 01101101 01100101 00100000 01101001 01110011 00100000 01100010 01100101 01100101 00100000 01100001 01101110 01100100 00100000 01001001 00100000 01101100 01101001 01101011 01100101 00100000 01100100 01101111 01100111 00100000 01100001 01101110 01100100 00100000 01100001 01110000 01110000 01101100 01100101 00100000 01100001 01101110 01100100 00100000 01110100 01110010 01100101 01100101",
)
assert res == answer_str
def test_binary_base64_caesar():
res = decrypt(
Config().library_default().complete_config(),
"01010110 01011000 01001010 00110101 01100101 01010111 01001001 01100111 01100101 01101101 01110111 01100111 01011001 01010111 00110101 00110110 01100011 01101001 01000010 00110010 01011010 01101001 01000010 01110110 01100011 01101110 01001001 01100111 01100010 01101101 01000110 01111000 01001001 01000110 01011001 01100111 01100101 01011000 01011010 00110100 01100011 01101001 01000010 01111000 01011001 01101110 01010001 01100111 01100010 01101101 01000110 01111000 01001001 01000111 00110101 01101010 01011001 00110011 01101100 01111001 01001001 01000111 00110101 01101000 01100011 01010011 01000010 01101110 01011010 01011000 01001010 01111001 00001010",
)
assert res == answer_str
def test_brainfuck():
res = decrypt(
Config().library_default().complete_config(),
"+[+++++++>+<]>-.-[+>-----<]>++.+++++++..+++.+[+>++<]>.[++>+<]>---.--[+++>-<]>.-[+>++++<]>.[++>+<]>--.-[+++>++<]>-.+[-->---<]>.--------.[+++++>+<]>+.-[+++>--<]>-.++++++++++.---[+>++<]>.[+++>-<]>++.+++..[+++++>+<]>+.[+++>-<]>+.+[-->---<]>+.----------.-[+++>-<]>-.-[+++>+<]>--.-[+>----<]>.++[+++>--<]>.---.++.------.[+++++>+<]>+.+[+>---<]>+.+++++++++++.--------.-[+++>-<]>--.[+++>-<]>+.+[-->---<]>+.----------.-[+++>-<]>-.[+++>-<]>+.-[-->---<]>..----.-------.[+++++>+<]>+.[+++>-<]>+.+[-->---<]>+.----------.-[+++>-<]>-.[++>+<]>++++.--.-------------..",
)
assert res == answer_str
def test_brandon():
res = decrypt(
Config().library_default().complete_config(),
"R hvv blf tzgsvi yvuliv nv...sfmtib...gviirurvw... Xofgxsrmt blfi yzyvh gl blfi yivzhg. Vnkvili Vnsbi srh nzixsvw srh ovtrlmh rmgl lfi ozmwh... Ozrw hrvtv gl vevib uligivhh uiln sviv gl gsv Yofv Nlfmgzrmh. Izyrw zmw izevmlfh, sv yrgvh zmw yrgvh zdzb. Nvm lu gsv Mligs, blf hgzmw zg gsv kivxrkrxv. Blfi prmth szev uzrovw blf, hl mld blf gfim gl gsv tlwh! Zmw bvg blf wl mlg kovzw? Blf wl mlg pmvvo gl wfhg blfi svzwh drgs zhs? Rmhgvzw blf dzro, Dsb szev gsv tlwh ulihzpvm fh? Dv nfhg ollp rmgl gsv girzoh dv uzrovw olmt ztl! Rm z grnv kzhhvw, lfi dliow rmgvigdrmvw drgs zmlgsvi gsilfts zm fksvzezo hxslozih xzoo gsv Xlmqfmxgrlm lu gsv Hksvivh... Gsv tlwh zooldvw fmslob ulixvh gl hork rmgl lfi wlnzrm. Gsv luuhkirmt lu gszg xzgzxobhn dzh gsv mvuvirlfh ulixv xzoovw nztrx... Bvg dv wrw mlg yzmrhs rg, rmhgvzw hgfwbrmt gsv erov zixzmv uli lfi kldvi zmw dvzogs! Zmw gsv nlmhgvih zg lfi wlli...gsv fmslob ivorxgh lu gsrh Xlmqfmxgrlm? ...gsv gilooh...gsv xlikhv vzgvih...gsv dvivdloevh? Wrw dv izrhv lfi hdliwh ztzrmhg gsvn? Li szev dv ozrw gsrh yfiwvm lm lgsvih? Lm hl-xzoovw drgxsvih? Hgizb xsrowivm gzftsg gsv dzbh lu ulfo hlixvib, gsvri ylwrvh nfgzgvw gsilfts yozhksvnlfh irgfzo. Hvmg gl urtsg nlmhgvih gslfts gsvb xlfow mlg wrhgrmtfrhs tllw uiln vero. Gsv uorxpvi lu sfnzmrgb olmt vcgrmtfrhsvw drgsrm gsvn. Bvh, gsvri mfnyvih szev wdrmwovw gsilfts gsv bvzih. Yfg z uvd hgroo ilzn lfi ozmwh, luuvirmt gsvri yollwb dlip uli xlrm. Gl gsrh wzb gsvb hsznv fh drgs gsvri evib vcrhgvmxv! Gsv Mligs yovvwh, uolttvw yb dzi. Gsv yzggovh ziv gsv tlwh' dsrk, xszhgrhvnvmg uli lfi hrmh! Zmw ovg fh mlg ulitvg gsv gviilih, gsv hxlfitvh uiln yvblmw lfi dliow! Gsv Drow Sfmg irwvh gsv hpb drgs vevib ufoo nllm! Gsv wzip izrwvih zywfxg lfi xsrowivm rmgl ozmwh fmpmldm! Hlnv hzb gsvb svizow z hvxlmw Xlmqfmxgrlm! Xzm dv xszig z xlfihv yzxp rmgl gsv ortsg? Droo dv urmw gsv hgivmtgs gl yzmrhs gsv nztvh uiln lfi prmtwlnh? Fmrgv zilfmw gsv dzings lu gsv Vgvimzo Uriv? Mrts rh gsv Grnv lu gsv Hdliw zmw gsv Zcv! Mlmv droo urtsg gsrh dzi rm lfi hgvzw! Mrts rh gsv Grnv lu Nzwmvhh zmw Wrhwzrm!",
)
assert bool(res) is True
def test_caesar():
res = decrypt(
Config().library_default().complete_config(),
"Uryyb zl anzr vf orr naq V yvxr qbt naq nccyr naq gerr",
)
assert res == answer_str
def test_decimal():
res = decrypt(
Config().library_default().complete_config(),
"72 101 108 108 111 32 109 121 32 110 97 109 101 32 105 115 32 98 101 101 32 97 110 100 32 73 32 108 105 107 101 32 100 111 103 32 97 110 100 32 97 112 112 108 101 32 97 110 100 32 116 114 101 101",
)
assert res.lower() == answer_str.lower()
def test_galactic():
res = decrypt(
Config().library_default().complete_config(),
"⍑ᒷꖎꖎ𝙹 ᒲ|| リᔑᒲᒷ ╎ᓭ ʖᒷᒷ ᔑリ↸ i ꖎ╎ꖌᒷ ↸𝙹⊣ ᔑリ↸ ᔑ!¡!¡ꖎᒷ ᔑリ↸ ℸ ̣ ∷ᒷᒷ",
)
assert res.lower() == answer_str.lower()
def test_galactic_Xproblem():
res = decrypt(
Config().library_default().complete_config(),
"⍑ᔑꖎ╎⎓ᔑ ̇/, ̇/||ꖎ𝙹!¡⍑𝙹リᒷ, ᔑ ̇/ ᔑꖎ𝙹リᒷ ᔑリ↸ ̇/ᒷ∷𝙹 ̇/ ⎓∷𝙹ᒲ 𝙹 ̇/⎓𝙹∷↸",
)
assert res.lower() == "halifax, xylophone, a x alone and xerox from oxford"
def test_hexadecimal():
res = decrypt(
Config().library_default().complete_config(),
"48 65 6c 6c 6f 20 6d 79 20 6e 61 6d 65 20 69 73 20 62 65 65 20 61 6e 64 20 49 20 6c 69 6b 65 20 64 6f 67 20 61 6e 64 20 61 70 70 6c 65 20 61 6e 64 20 74 72 65 65",
)
assert res.lower() == answer_str.lower()
def test_json_problem():
res = decrypt(
Config().library_default().complete_config(),
"0110100001100101011011000110110001101111",
@ -190,10 +187,18 @@ def test_base69():
assert res != "0110100001100101011011000110110001101111"
def test_baudot():
def test_leetspeak():
res = decrypt(
Config().library_default().complete_config(),
"10100 00001 10010 10010 11000 00100 11100 10101 00100 01100 00011 11100 00001 00100 00110 00101 00100 11001 00001 00001 00100 00011 01100 01001 00100 00110 00100 10010 00110 01111 00001 00100 01001 11000 11010 00100 00011 01100 01001 00100 00011 10110 10110 10010 00001 00100 00011 01100 01001 00100 10000 01010 00001 00001",
"|-|3ll0 my n4m3 1s 833 4nd 1 l1k3 D06 4ND 4ppl3 4nd 7R33",
)
assert res.lower() == answer_str.lower()
def test_morse_code():
res = decrypt(
Config().library_default().complete_config(),
".... . .-.. .-.. ---/-- -.--/-. .- -- ./.. .../-... . ./.- -. -../../.-.. .. -.- ./-.. --- --./.- -. -../.- .--. .--. .-.. ./.- -. -../- .-. . .",
)
assert res == answer_str.upper()
@ -206,14 +211,59 @@ def test_multi_tap():
assert res == answer_str.upper()
def test_url():
def test_new_line_at_start_returns():
# Language Checker should return True by stripping new line
# but the new line should be returned to the user as new lines are important
res = decrypt(Config().library_default().complete_config(), "\npass\n")
assert res.lower() == "\npass\n"
def test_new_line_strip_and_return():
# Language Checker should return True by stripping new line
# but the new line should be returned to the user as new lines are important
res = decrypt(Config().library_default().complete_config(), "pass\n")
assert res.lower() == "pass\n"
def test_octal():
res = decrypt(
Config().library_default().complete_config(),
"Hello%20my%20name%20is%20bee%20and%20I%20like%20dog%20and%20apple%20and%20tree",
"110 145 154 154 157 40 155 171 40 156 141 155 145 40 151 163 40 142 145 145 40 141 156 144 40 111 40 154 151 153 145 40 144 157 147 40 141 156 144 40 141 160 160 154 145 40 141 156 144 40 164 162 145 145",
)
assert res.lower() == answer_str.lower()
def test_plaintext():
res = decrypt(Config().library_default().complete_config(), answer_str)
assert res == answer_str
def test_reversed_text():
res = decrypt(
Config().library_default().complete_config(),
"eert dna elppa dna god ekil I dna eeb si eman ym olleH",
)
assert res.lower() == answer_str.lower()
def test_rot47():
res = decrypt(
Config().library_default().complete_config(),
"$A9:?I @7 3=24< BF2CEK[ ;F586 >J G@H",
)
assert res == "Sphinx of black quartz, judge my vow"
def test_soundex():
res = decrypt(
Config().library_default().complete_config(),
"H236 I200 I500 T000 P230",
)
assert res.lower() == "history is in the past"
def test_tap_code():
res = decrypt(
Config().library_default().complete_config(),
@ -222,34 +272,10 @@ def test_tap_code():
assert res == "test one two three".upper()
def test_brandon():
def test_url():
res = decrypt(
Config().library_default().complete_config(),
"R hvv blf tzgsvi yvuliv nv...sfmtib...gviirurvw... Xofgxsrmt blfi yzyvh gl blfi yivzhg. Vnkvili Vnsbi srh nzixsvw srh ovtrlmh rmgl lfi ozmwh... Ozrw hrvtv gl vevib uligivhh uiln sviv gl gsv Yofv Nlfmgzrmh. Izyrw zmw izevmlfh, sv yrgvh zmw yrgvh zdzb. Nvm lu gsv Mligs, blf hgzmw zg gsv kivxrkrxv. Blfi prmth szev uzrovw blf, hl mld blf gfim gl gsv tlwh! Zmw bvg blf wl mlg kovzw? Blf wl mlg pmvvo gl wfhg blfi svzwh drgs zhs? Rmhgvzw blf dzro, Dsb szev gsv tlwh ulihzpvm fh? Dv nfhg ollp rmgl gsv girzoh dv uzrovw olmt ztl! Rm z grnv kzhhvw, lfi dliow rmgvigdrmvw drgs zmlgsvi gsilfts zm fksvzezo hxslozih xzoo gsv Xlmqfmxgrlm lu gsv Hksvivh... Gsv tlwh zooldvw fmslob ulixvh gl hork rmgl lfi wlnzrm. Gsv luuhkirmt lu gszg xzgzxobhn dzh gsv mvuvirlfh ulixv xzoovw nztrx... Bvg dv wrw mlg yzmrhs rg, rmhgvzw hgfwbrmt gsv erov zixzmv uli lfi kldvi zmw dvzogs! Zmw gsv nlmhgvih zg lfi wlli...gsv fmslob ivorxgh lu gsrh Xlmqfmxgrlm? ...gsv gilooh...gsv xlikhv vzgvih...gsv dvivdloevh? Wrw dv izrhv lfi hdliwh ztzrmhg gsvn? Li szev dv ozrw gsrh yfiwvm lm lgsvih? Lm hl-xzoovw drgxsvih? Hgizb xsrowivm gzftsg gsv dzbh lu ulfo hlixvib, gsvri ylwrvh nfgzgvw gsilfts yozhksvnlfh irgfzo. Hvmg gl urtsg nlmhgvih gslfts gsvb xlfow mlg wrhgrmtfrhs tllw uiln vero. Gsv uorxpvi lu sfnzmrgb olmt vcgrmtfrhsvw drgsrm gsvn. Bvh, gsvri mfnyvih szev wdrmwovw gsilfts gsv bvzih. Yfg z uvd hgroo ilzn lfi ozmwh, luuvirmt gsvri yollwb dlip uli xlrm. Gl gsrh wzb gsvb hsznv fh drgs gsvri evib vcrhgvmxv! Gsv Mligs yovvwh, uolttvw yb dzi. Gsv yzggovh ziv gsv tlwh' dsrk, xszhgrhvnvmg uli lfi hrmh! Zmw ovg fh mlg ulitvg gsv gviilih, gsv hxlfitvh uiln yvblmw lfi dliow! Gsv Drow Sfmg irwvh gsv hpb drgs vevib ufoo nllm! Gsv wzip izrwvih zywfxg lfi xsrowivm rmgl ozmwh fmpmldm! Hlnv hzb gsvb svizow z hvxlmw Xlmqfmxgrlm! Xzm dv xszig z xlfihv yzxp rmgl gsv ortsg? Droo dv urmw gsv hgivmtgs gl yzmrhs gsv nztvh uiln lfi prmtwlnh? Fmrgv zilfmw gsv dzings lu gsv Vgvimzo Uriv? Mrts rh gsv Grnv lu gsv Hdliw zmw gsv Zcv! Mlmv droo urtsg gsrh dzi rm lfi hgvzw! Mrts rh gsv Grnv lu Nzwmvhh zmw Wrhwzrm!",
)
assert True
def test_affine():
res = decrypt(
Config().library_default().complete_config(),
"Ihsst bf kxbh rd ghh xky R srjh ytz xky xccsh xky muhh",
)
assert res == answer_str
def test_brainfuck():
res = decrypt(
Config().library_default().complete_config(),
"+[+++++++>+<]>-.-[+>-----<]>++.+++++++..+++.+[+>++<]>.[++>+<]>---.--[+++>-<]>.-[+>++++<]>.[++>+<]>--.-[+++>++<]>-.+[-->---<]>.--------.[+++++>+<]>+.-[+++>--<]>-.++++++++++.---[+>++<]>.[+++>-<]>++.+++..[+++++>+<]>+.[+++>-<]>+.+[-->---<]>+.----------.-[+++>-<]>-.-[+++>+<]>--.-[+>----<]>.++[+++>--<]>.---.++.------.[+++++>+<]>+.+[+>---<]>+.+++++++++++.--------.-[+++>-<]>--.[+++>-<]>+.+[-->---<]>+.----------.-[+++>-<]>-.[+++>-<]>+.-[-->---<]>..----.-------.[+++++>+<]>+.[+++>-<]>+.+[-->---<]>+.----------.-[+++>-<]>-.[++>+<]>++++.--.-------------..",
)
assert res == answer_str
def test_ascii_shift():
res = decrypt(
Config().library_default().complete_config(),
"\"?FFIzGSzH;G?zCMz<??z;H>z#zFCE?z>IAz;H>z;JJF?z;H>zNL??",
"Hello%20my%20name%20is%20bee%20and%20I%20like%20dog%20and%20apple%20and%20tree",
)
assert res.lower() == answer_str.lower()
@ -257,18 +283,28 @@ def test_ascii_shift():
def test_uuencode():
res = decrypt(
Config().library_default().complete_config(),
"begin 644 /dev/stdout\nM2&5L;&\@;7D@;F%M92!I<R!B964@86YD($D@;&EK92!D;V<@86YD(&%P<&QE\n)(&%N9\"!T<F5E\n`\nend\n"
'begin 644 /dev/stdout\nM2&5L;&\\@;7D@;F%M92!I<R!B964@86YD($D@;&EK92!D;V<@86YD(&%P<&QE\n)(&%N9"!T<F5E\n`\nend\n',
)
assert res == answer_str
res = decrypt(
Config().library_default().complete_config(),
"M2&5L;&\@;7D@;F%M92!I<R!B964@86YD($D@;&EK92!D;V<@86YD(&%P<&QE\n)(&%N9\"!T<F5E\n"
'M2&5L;&\\@;7D@;F%M92!I<R!B964@86YD($D@;&EK92!D;V<@86YD(&%P<&QE\n)(&%N9"!T<F5E\n',
)
assert res == answer_str
def test_soundex():
def test_vigenere():
res = decrypt(
Config().library_default().complete_config(),
"H236 I200 I500 T000 P230",
"Rijvs ki rywi gc fco eln M jsoc nse krb ktnvi yxh rbic",
)
assert "history is in the past" == res
assert res == answer_str
def test_xandy():
res = decrypt(
Config().library_default().complete_config(),
"xDDxDxxx xDDxxDxD xDDxDDxx xDDxDDxx xDDxDDDD xxDxxxxx xDDxDDxD xDDDDxxD xxDxxxxx xDDxDDDx xDDxxxxD xDDxDDxD xDDxxDxD xxDxxxxx xDDxDxxD xDDDxxDD xxDxxxxx xDDxxxDx xDDxxDxD xDDxxDxD xxDxxxxx xDDxxxxD xDDxDDDx xDDxxDxx xxDxxxxx xDxxDxxD xxDxxxxx xDDxDDxx xDDxDxxD xDDxDxDD xDDxxDxD xxDxxxxx xDDxxDxx xDDxDDDD xDDxxDDD xxDxxxxx xDDxxxxD xDDxDDDx xDDxxDxx xxDxxxxx xDDxxxxD xDDDxxxx xDDDxxxx xDDxDDxx xDDxxDxD xxDxxxxx xDDxxxxD xDDxDDDx xDDxxDxx xxDxxxxx xDDDxDxx xDDDxxDx xDDxxDxD xDDxxDxD",
)
assert res.lower() == answer_str.lower()

View File

@ -1,5 +1,6 @@
import json
import sys
import cipheycore
data = sys.stdin.read()