def decode(self, ctext: T) -> Optional[U]: """ UUEncode (Unix to Unix Encoding) is a symmetric encryption based on conversion of binary data (split into 6-bit blocks) into ASCII characters. This function decodes the input string 'ctext' if it has been encoded using 'uuencoder' It will return None otherwise """ logger.trace("Attempting UUencode") result = "" try: # UUencoded messages may begin with prefix "begin" and end with suffix "end" # In that case, we use the codecs module in Python ctext_strip = ctext.strip() if ctext_strip.startswith("begin") and ctext_strip.endswith("end"): result = decode(bytes(ctext, "utf-8"), "uu").decode() else: # 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") logger.debug(f"UUencode successful, returning '{result}'") return result except Exception: logger.trace("Failed to decode UUencode") return None
def check(self, ctext: T) -> Optional[str]: logging.debug("Trying PyWhat checker") returned_regexes = self.id.identify(ctext, api=True) if len(returned_regexes["Regexes"]) > 0: matched_regex = returned_regexes["Regexes"][0]["Regex Pattern"] ret = f'The plaintext is a [yellow]{matched_regex["Name"]}[/yellow]' human = f'\nI think the plaintext is a [yellow]{matched_regex["Name"]}[/yellow]' if "Description" in matched_regex and matched_regex["Description"]: s = matched_regex['Description'] # lowercases first letter so it doesn't look weird s = f", which is {s[0].lower() + s[1:]}\n" ret += s human += s # if URL is attached, include that too. if "URL" in matched_regex: link = matched_regex['URL'] + ctext.replace(' ', '') ret += f"\nClick here to view in browser [#CAE4F1][link={link}]{link}[/link][/#CAE4F1]\n" # If greppable mode is on, don't print this if self.config.verbosity > 0: # Print with full stop console.print(human) return ret return None
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. """ result = "" letters = list("abcdefghijklmnopqrstuvwxyz") atbash_dict = {letters[i]: letters[::-1][i] for i in range(26)} # Ensure that ciphertext is a string if type(ctext) == str: # Normalize the string to all-lowercase letters ctext = ctext.lower() else: return None for letter in ctext: if letter in atbash_dict.keys(): # Match every letter of the input to its atbash counterpoint result += atbash_dict[letter] else: # If the current character is not in the defined alphabet, # just accept it as-is (useful for numbers, punctuation,...) result += letter return result
def decode(self, ctext: T) -> Optional[U]: result = "" switch_to_digit_map = 0 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
def decode(self, ctext: T) -> Optional[U]: """ Performs Octal decoding """ str_converted = [] octal_seq = ctext.split(" ") if len(octal_seq) == 1: # Concatted octal must be formed of octal triplets if len(ctext) % 3 != 0: return None 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("Octal subseq too long") return None n = int(octal_char, 8) if ( n < 0 ): # n cannot be greater than 255, as we checked that with the earlier length check logger.trace(f"Non octal char {octal_char}") return None str_converted.append(n) return bytes(str_converted) # Catch bad octal chars except ValueError: return None
def decode(self, ctext: T) -> Optional[U]: try: ctext = re.sub(r"[^\S \n]", " ", ctext, flags=re.UNICODE) ctext = ctext.replace("\n", " ") 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 ctext = ctext.replace(" ", "") # Split into bytes, and test return self.try_split([ctext[i : i + 8] for i in range(0, len(ctext), 8)]) # Catch bad octal chars except ValueError: return None
def decode(self, ctext: T) -> Optional[U]: try: output = "" combinations = ctext.split(" ") for fragment in combinations: output += self.TABLE.get(fragment) return output except Exception as e: return None
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 ctext: i_priority = self.BOUNDARIES.get(i) if i_priority is None: if i in self.ALLOWED: continue logger.trace(f"Non-morse char '{i}' found") return None if i_priority <= char_priority or i == char_boundary or i == word_boundary: continue # Default to having a char boundary over a word boundary if (i_priority > word_priority and word_boundary is None and char_boundary is not None): word_priority = i_priority word_boundary = i continue char_priority = i_priority char_boundary = i logger.trace( f"Char boundary is unicode {ord(char_boundary)}, and word boundary is unicode {ord(word_boundary) if word_boundary is not None else None}" ) result = "" 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 try: m = self.MORSE_CODE_DICT_INV[char] except KeyError: logger.trace(f"Invalid codeword '{char}' found") return None result = result + m # after every word add a space result = result + " " if len(result) == 0: logger.trace("Morse code failed to match") return None # Remove trailing space result = result[:-1] logger.debug(f"Morse code successful, returning {result}") return result.strip().upper()
def decode(self, ctext: T) -> Optional[U]: """ Performs Tap code decoding """ try: result = "" combinations = ctext.split(" ") for fragment in combinations: result += self.TABLE.get(fragment) return result except Exception: return None
def check(self, text: T) -> Optional[str]: logger.trace(f"Trying json checker") # https://github.com/Ciphey/Ciphey/issues/389 if text.isdigit(): return None try: json.loads(text) return "" except ValueError: return None
def decode(self, ctext: T) -> Optional[U]: """Write the code that decodes here ctext -> the input to the function returns string """ ret = "" 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 else: return None
def decode(self, ctext: T) -> Optional[U]: """ Performs UTF-8 decoding """ logger.trace("Attempting UTF-8 decoder") result = "" try: 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
def decode(self, ctext: T) -> Optional[U]: """ Performs DNA decoding """ logger.trace("Attempting DNA decoder") ctext_decoded = "" ctext = re.sub(r"[,;:\-\s]", "", ctext) ctext = " ".join(ctext[i:i + 3] for i in range(0, len(ctext), 3)) ctext_split = ctext.split(" ") dna_keys = self.DNA_DICT.keys() for i in ctext_split: if i in dna_keys: ctext_decoded += self.DNA_DICT[i] else: return None logger.debug(f"DNA successful, returning '{ctext_decoded}'") return ctext_decoded
def decode(self, ctext: T) -> Optional[U]: """ Performs DTMF decoding """ logging.debug("Attempting DTMF decoder") ctext_decoded = "" ctext = re.sub(r"[,;:\-\/\s]", "", ctext) ctext = " ".join(ctext[i:i + 7] for i in range(0, len(ctext), 7)) ctext_split = ctext.split(" ") dtmf_keys = self.DTMF_DICT.keys() for i in ctext_split: if i in dtmf_keys: ctext_decoded += self.DTMF_DICT[i] else: return None logging.info(f"DTMF successful, returning '{ctext_decoded}'") return ctext_decoded
def decode(self, ctext: T) -> Optional[U]: """ 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") # To avoid complications, only move forward with the decoding if we can # reasonably assume that the input string is written in the galactic alphabet galactic_matches = 0 for symbol in self.GALACTIC_DICT.keys(): # These symbols are assumed to be frequent enough in regular # text to be skipped when counting the matches. All others are counted. if symbol in ctext and symbol not in ["!", "|"]: galactic_matches += 1 else: continue if galactic_matches == 0: logger.trace( "No matching galactic alphabet letters found. Skipping galactic decoder..." ) return None logger.trace(f"{galactic_matches} galactic alphabet letters found. ") result = "" ctext = (ctext.replace("||", "|").replace("/", "").replace("¡", "").replace( " ̣ ", "").replace(" ̇", " x")) logger.trace(f"Modified string is {ctext}") # Take out the problematic characters consisting of multiple symbols for letter in ctext: if letter in self.GALACTIC_DICT.keys(): # Match every letter of the input to its galactic counterpoint 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,...) result += letter result = result.replace("x ", "x") # Remove the trailing space (appearing as a leading space) # from the x that results from the diacritic replacement logger.trace(f"Decoded string is {result}") return result
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. """ result = "" atbash_dict = {self.ALPHABET[i]: self.ALPHABET[::-1][i] for i in range(26)} for letter in ctext.lower(): if letter in atbash_dict.keys(): # Match every letter of the input to its atbash counterpoint result += atbash_dict[letter] else: # If the current character is not in the defined alphabet, # just accept it as-is (useful for numbers, punctuation,...) result += letter return fix_case(result, ctext)
def decode(self, ctext: T) -> Optional[U]: """ Performs Braille decoding """ logger.trace("Attempting Braille") ctext_decoded = "" braille_matches = 0 for symbol in self.BRAILLE_DICT_INV.values(): if symbol in ctext: braille_matches += 1 else: continue if braille_matches == 0: logger.trace("Failed to decode Braille due to invalid characters") return None for pattern, value in self.BRAILLE_DICT.items(): ctext = re.sub(pattern, value, ctext) wordArr = [] for word in ctext.split(" "): # If two commas are in front of a word, uppercase the word and remove the comma if word[:2].find(",,") != -1: wordArr.append(word.replace(",,", "").upper()) else: wordArr.append(word) result = [] for word in wordArr: # If one comma is in front of a word, capitalize the word and remove the comma if word[0].find(",") != -1: result.append(word.replace(",", "").capitalize()) else: result.append(word) ctext_decoded = " ".join(result) logger.debug(f"Braille successful, returning '{ctext_decoded}'") return ctext_decoded
def check(self, ctext: T) -> Optional[str]: logging.debug("Trying Quadgrams checker") # Capitalize and remove everything that's not a letter ctext = re.sub("[^A-Z]", "", ctext.upper()) quadgrams = self.QUADGRAMS_DICT quadgrams_sum = sum(quadgrams.values()) score = 0 for key in quadgrams.keys(): quadgrams[key] = float(quadgrams[key]) / quadgrams_sum floor = log10(0.01 / quadgrams_sum) for i in range(len(ctext) - 4 + 1): # Get all quadgrams from ctext and check if they're in the dict # If yes then add the score of those quadgrams to the total score if ctext[i:i + 4] in quadgrams: score += quadgrams[ctext[i:i + 4]] else: score += floor if len(ctext) > 0: score = score / len(ctext) logging.info(f"Quadgrams is {score}") # The default threshold was found to work the best from lots of testing if score > self.threshold: return "" return None
def decode(self, ctext: T) -> Optional[U]: for src, dst in self.translate.items(): ctext = ctext.replace(src, dst) return ctext