def decryptWithSubstitution(ciph): """Decrypt a general polyalphabetic substitution cipher using mulitple simultaneous hill-climbing algorithms.""" ciph = Format.keepOnly(ciph.lower(), ALPH) length = len(ciph) subs = [] for x in range(0, 7): substring = ciph[x::7] key = [y[0] for y in FreqAnalysis.getFrequencies(substring).most_common() if y[0] in ALPH] for char in letterProbs: if char not in key: key.append(char) subs.append((substring, key)) i = 0 bestScore = 0 result = [] for sub in subs: keyMap = dict(zip(sub[1], letterProbs)) result.append(Substitution.sub(sub[0], keyMap)) result = "".join("".join(b) for b in zip_longest(*result, fillvalue="")) bestScore = DetectEnglish.detect(result, length=length) while i < 10000: x = random.randint(0, len(subs) - 1) y = random.randint(1, len(subs[x][1]) - 1) z = random.randint(1, len(subs[x][1]) - 1) subs[x][1][y], subs[x][1][z] = subs[x][1][z], subs[x][1][y] result = [] for sub in subs: keyMap = dict(zip(sub[1], letterProbs)) result.append(Substitution.sub(sub[0], keyMap)) result = "".join("".join(b) for b in zip_longest(*result, fillvalue="")) score = DetectEnglish.detect(result, length=length) if score > bestScore: bestScore = score i = 0 else: subs[x][1][y], subs[x][1][z] = subs[x][1][z], subs[x][1][y] i += 1 result = [] for sub in subs: keyMap = dict(zip(sub[1], letterProbs)) key = "" for x in ALPH: for k, v in keyMap.items(): if k == x: key += v result.append(Substitution.sub(sub[0], keyMap)) result = "".join("".join(b) for b in zip_longest(*result, fillvalue="")) return result
def getBest(possKeys, ciph): """Find best mapping in given possibilities.""" results = [] for key in possKeys: keyMap = dict(zip(ALPH, key)) result = sub(ciph, keyMap) results.append((key, DetectEnglish.detect(result), -DetectEnglish.chiSquared(result))) best = max(results) bestMap = dict(zip(ALPH, best[0])) result = sub(ciph, bestMap) return result, best[1], bestMap
def reverseText(): args = {"title": "Reverse Text", "ciphText": "", "result": "", "score": 0} if request.method == "POST": args["ciphText"] = ciph = request.form["ciphInput"] args["result"] = plain = ciph[::-1] args["score"] = DetectEnglish.detectWord(SpaceAdd.addLongest(plain)) * 100 return render_template(f"tools/reversetext.html", **args)
def freqAnalysis(): args = {"title": "Frequency Analysis", "ciphText": "", "score": 0} if request.method == "POST": args["ciphText"] = ciph = request.form["ciphInput"] args["score"] = DetectEnglish.detectWord(SpaceAdd.addLongest(ciph)) * 100 plotFreq(args["ciphText"]) return render_template(f"tools/freqanalysis.html", **args)
def decrypt(ciph): """Use a hill-climbing algorithm to decipher a substituted alphabet.""" ciph = Format.keepOnly(ciph.lower(), ALPH) if not ciph: return ciph, {x: "" for x in ALPH} key = [ x[0] for x in FreqAnalysis.getFrequencies(ciph).most_common() if x[0] in ALPH ] keyMap = dict(zip(key, letterProbs)) bestKey = [] bestScore = 0 i = 0 while i < 1000: result = sub(ciph, keyMap) score = DetectEnglish.detect(result) if score > bestScore: bestScore = score bestKey = list(key) i = 0 x = random.randint(1, len(key) - 1) y = random.randint(1, len(key) - 1) key = list(bestKey) key[x], key[y] = bestKey[y], bestKey[x] keyMap = dict(zip(key, letterProbs)) i += 1 bestMap = dict(zip(key, letterProbs)) result = sub(ciph, bestMap) return result, bestMap
def _decryptLongKey(text, keylen): """Decrypt long keys (length >= 9) using a hill-climbing algorithm.""" key = list(range(keylen)) random.shuffle(key) bestKey = [] bestScore = 0 i = 0 while i < 10000: if random.uniform(0, 10) < 1: random.shuffle(key) result = recreate(shuffle(text, key)) score = DetectEnglish.detect(result) if score > bestScore: bestScore = score bestKey = list(key) i = 0 x = random.randint(0, len(key) - 1) y = random.randint(0, len(key) - 1) key = list(bestKey) key[x], key[y] = bestKey[y], bestKey[x] i += 1 result = recreate(shuffle(text, bestKey)) return result, list(map(str, key))
def addSpaces(): if request.method == "POST": plainText = Format.remove(request.json["plain"], SPACE) plainText = SpaceAdd.addLongest(plainText) score = DetectEnglish.detectWord(plainText) * 100 return json.dumps({"plain": plainText, "score": f"{score}% certainty"}) return "error"
def addForwards(text): """Insert spacing into text.""" with open("static/txt/wordlist.txt", encoding="utf-8") as f: wordset = set(f.read().split("\n")) string = Format.keepOnly(text, ALPH) result = [] maxLen = DetectEnglish.getLongest() x = maxLen while True: word = string[:x] if word in wordset: result.append(word) string = string[x::] x = maxLen else: x -= 1 if x == 0 and string: result.append(string[0]) string = string[1::] x = maxLen elif not string: break return " ".join(result)
def decrypt(ciph, keylen=0, key=""): """ Attempt decryption of the transposition-enciphered text. One of keylen or key is required to function. """ if not (key or keylen): return "", "" ciph = Format.keepOnly(ciph.lower(), ALPH, NUMS) text = _process(ciph, keylen=keylen, key=key) if key: return _decryptWithKey(text, key.split(",")) if keylen < 9: bestResult, bestKey = _decryptShortKey(text) else: bestResult, bestKey = _decryptLongKey(text, keylen) bestScore = DetectEnglish.detectWord(SpaceAdd.addLongest(bestResult)) text = _process(ciph, keylen=len(ciph) // keylen, key=key) text = "".join(text) text = _process(text, keylen=keylen, key=key) if keylen < 9: result, key = _decryptShortKey(text) else: result, key = _decryptLongKey(text, keylen) score = DetectEnglish.detectWord(SpaceAdd.addLongest(result)) if score > bestScore: bestResult = result bestKey = key overflow = len(ciph) % keylen if overflow != 0: bestScore = 0 lastset = bestResult[-overflow:] overflow = len(lastset) for perm in itertools.permutations(lastset, overflow): result = bestResult[:-overflow] + "".join(perm) score = DetectEnglish.detectWord(SpaceAdd.addLongest(result)) if score > bestScore: bestScore = score bestResult = result return bestResult, bestKey, bestScore
def caesar(): args = {"title": "Caesar", "ciphText": "", "result": "", "score": 0} if request.method == "POST": from Ciphers import Caesar ciphText = request.form["ciphInput"].lower() result, _ = Caesar.decrypt(ciphText) score = DetectEnglish.detectWord(SpaceAdd.addLongest(result)) * 100 args = {"title": "Caesar", "ciphText": ciphText, "result": result, "score": score} return render_template(f"ciphers/caesar.html", **args)
def _decryptShortKey(text): """Decrypt ciphertext if the key is short (length < 9) using a brute-force attack.""" bestKey = [] bestScore = 0 for key in itertools.permutations(range(len(text))): result = recreate(shuffle(text, key)) score = DetectEnglish.detect(result) if score > bestScore: bestScore = score bestKey = list(key) result = recreate(shuffle(text, bestKey)) return result, list(map(str, bestKey))
def substitution(): args = {"title": "Substitution", "ciphText": "", "result": "", "score": 0, "vals": {}} if request.method == "POST": from Ciphers import Substitution ciphText = Format.remove(request.form["ciphInput"], PUNC).lower() if request.form.get("useSpace"): result, vals = Substitution.decryptWithSpaces(ciphText) else: result, vals = Substitution.decrypt(ciphText) score = DetectEnglish.detectWord(SpaceAdd.addLongest(result)) * 100 args = {"title": "Substitution", "ciphText": ciphText, "result": result, "score": score, "vals": vals} return render_template(f"ciphers/substitution.html", **args)
def transposition(): args = {"title": "Transposition", "ciphText": "", "result": "", "score": 0, "keylen": "", "key": ""} if request.method == "POST": from Ciphers import Transposition ciphText = Format.remove(request.form["ciphInput"], PUNC).lower() keylen = int(request.form["keylenInput"] or 0) key = request.form["keyInput"] result, key = Transposition.decrypt(ciphText, key=key, keylen=keylen) key = ",".join(key) score = DetectEnglish.detectWord(SpaceAdd.addLongest(result)) * 100 args = {"title": "Transposition", "ciphText": ciphText, "result": result, "score": score, "keylen": keylen, "key": key} return render_template(f"ciphers/transposition.html", **args)
def decrypt(ciph): """Try every possible shift and choose the best result.""" bestScore = 9e99 bestKey = "" bestResult = "" for i in range(26): result = shift(ciph, i) score = DetectEnglish.chiSquared(result) if score < bestScore: bestScore = score bestResult = result bestKey = ALPH[i] return bestResult, bestKey
def decryptWithKeylen(ciph, keylen): """Decrypt each 'column' of ciphertext as separate Caesar ciphers.""" sub = [] for i in range(keylen): sub.append(ciph[i::keylen]) results = [] key = [] for x in sub: result, shift = Caesar.decrypt(x) results.append(result) key.append(shift) result = Transposition.recreate(results) score = DetectEnglish.detect(result) return result, ",".join(key), score
def vigenere(): args = {"title": "Vigenere", "ciphText": "", "result": "", "score": 0, "keylen": "", "key": ""} if request.method == "POST": from Ciphers import Vigenere ciphText = request.form["ciphInput"] keylen = request.form["keylenInput"] key = request.form["keyInput"] if request.form.get("oddAlph"): result = Vigenere.decryptWithSubstitution(ciphText) else: result, key, _ = Vigenere.decrypt(ciphText, key=key, keylen=keylen) score = DetectEnglish.detectWord(SpaceAdd.addLongest(result)) * 100 args = {"title": "Vigenere", "ciphText": ciphText, "result": result, "score": score, "keylen": keylen, "key": key} return render_template(f"ciphers/vigenere.html", **args)
def decryptWithKey(ciph, key): """Use given key to decrypt text.""" key = key.split(",") keylen = len(key) sub = [] for i in range(keylen): sub.append(ciph[i::keylen]) results = [] for i, x in enumerate(sub): result = Caesar.shift(x, key[i]) results.append(result) result = Transposition.recreate(results) score = DetectEnglish.detect(result) return result, ",".join(key), score
def subInputs(): if request.method == "POST": changed = request.json["name"][0] newval = request.json["val"].lower() if newval == "": newval = "_" ciphText = Format.keepOnly(request.json["ciph"].lower(), ALPH) plainText = Format.remove(request.json["plain"], SPACE).lower() if plainText == "": new = ''.join([newval if x in changed else "_" for x in ciphText]) else: plainText = [x for x in plainText] for i, letter in enumerate(ciphText): if letter == changed: plainText[i] = newval new = "".join(plainText) score = DetectEnglish.detectWord(SpaceAdd.add(new)) * 100 return json.dumps({"plain": new, "score": f"{score}% certainty"}) return "error"
def addLongest(text): """Insert spacing into text. Longest identified words inserted first.""" with open("static/txt/wordlist.txt", encoding="utf-8") as f: wordset = set(f.read().split("\n")) string = Format.keepOnly(text, ALPH) result = [""] * len(string) maxLen = DetectEnglish.getLongest() for chunkSize in range(maxLen, 0, -1): for i in range(0, len(string) - chunkSize + 1): if string[i:i + chunkSize] in wordset: result[i] = string[i:i + chunkSize] string = string.replace(string[i:i + chunkSize], "." * chunkSize) result = filter(lambda x: x != "", result) return " ".join(result)
def decrypt(ciph, key="", keylen=0): """Automatically decrypt a vigenere cipher using the Index of Coincidence to find possible key lengths.""" ciph = Format.keepOnly(ciph.lower(), ALPH) if key: return decryptWithKey(ciph, key) if keylen: return decryptWithKeylen(ciph, int(keylen)) sub = {} for i in range(2, 26): sub[i] = [] for j in range(i): sub[i].append(ciph[j::i]) ic = {} for i in sub: avgic = sum(map(DetectEnglish.indexOfCoincidence, sub[i])) / i if avgic > 0.06: ic[i] = avgic bestKey = "" bestScore = 0 bestResult = "" for i in ic: results = [] key = [] for x in sub[i]: result, shift = Caesar.decrypt(x) results.append(result) key.append(shift) result = Transposition.recreate(results) score = DetectEnglish.detect(result) if score > bestScore: bestScore = score bestKey = "".join(key) bestResult = result return bestResult, ",".join(bestKey), bestScore