From d304015036dd0b45572d52a0a9acf3bf3f1e7e63 Mon Sep 17 00:00:00 2001 From: Neil Smith Date: Fri, 11 Oct 2013 23:38:55 +0100 Subject: [PATCH] Done one-based affine ciphers, and breaking them. --- __pycache__/norms.cpython-33.pyc | Bin 8560 -> 8560 bytes cipher.log | 5705 ++++++++++++++---------------- cipher.py | 79 +- 3 files changed, 2756 insertions(+), 3028 deletions(-) diff --git a/__pycache__/norms.cpython-33.pyc b/__pycache__/norms.cpython-33.pyc index 7d10c2f61bc74f9763ecdc1456169892160835f5..ea0f65768ab81d3f6bc838fd76f5be69c9d4c113 100644 GIT binary patch delta 16 Xcmez1^udXJ9uF_q)a;0j?3s!HH{1os delta 16 Xcmez1^udXJ9uF^>> ''.join([affine_encipher_letter(l, 3, 5, True) for l in string.ascii_uppercase]) + 'HKNQTWZCFILORUXADGJMPSVYBE' + >>> ''.join([affine_encipher_letter(l, 3, 5, False) for l in string.ascii_uppercase]) + 'FILORUXADGJMPSVYBEHKNQTWZC' + """ if letter in string.ascii_letters: if letter in string.ascii_uppercase: alphabet_start = ord('A') else: alphabet_start = ord('a') letter_number = ord(letter) - alphabet_start + if one_based: letter_number += 1 + raw_cipher_number = (letter_number * multiplier + adder) cipher_number = 0 - if multiply_then_add: - cipher_number = (letter_number * multiplier + adder) % 26 + if one_based: + cipher_number = (raw_cipher_number - 1) % 26 else: - cipher_number = ((letter_number + adder) * multiplier) % 26 + cipher_number = raw_cipher_number % 26 return chr(cipher_number + alphabet_start) else: return letter -def affine_decipher_letter(letter, multiplier, adder, multiply_then_add=True): +def affine_decipher_letter(letter, multiplier=1, adder=0, one_based=True): + """Encipher a letter, given a multiplier and adder + + >>> ''.join([affine_decipher_letter(l, 3, 5, True) for l in 'HKNQTWZCFILORUXADGJMPSVYBE']) + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + >>> ''.join([affine_decipher_letter(l, 3, 5, False) for l in 'FILORUXADGJMPSVYBEHKNQTWZC']) + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + """ if letter in string.ascii_letters: if letter in string.ascii_uppercase: alphabet_start = ord('A') else: alphabet_start = ord('a') cipher_number = ord(letter) - alphabet_start + if one_based: cipher_number += 1 plaintext_number = 0 - if multiply_then_add: - plaintext_number = modular_division_table[multiplier][(cipher_number - adder + 26) % 26] + if one_based: + plaintext_number = (modular_division_table_one_based[multiplier][(cipher_number - adder + 26) % 26] - 1) % 26 else: - plaintext_number = (modular_division_table[multiplier][cipher_number] - adder) % 26 + #plaintext_number = (modular_division_table[multiplier][cipher_number] - adder) % 26 + plaintext_number = modular_division_table[multiplier][(cipher_number - adder + 26) % 26] return chr(plaintext_number + alphabet_start) else: return letter -def affine_encipher(message, multiplier, adder, multiply_then_add=True): - enciphered = [affine_encipher_letter(l, multiplier, adder, multiply_then_add) for l in message] +def affine_encipher(message, multiplier=1, adder=0, one_based=True): + """Encipher a message + + >>> affine_encipher('hours passed during which jerico tried every trick he could think of', 15, 22, True) + 'lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls omytd jlaxe mh' + """ + + enciphered = [affine_encipher_letter(l, multiplier, adder, one_based) for l in message] return ''.join(enciphered) -def affine_decipher(message, multiplier, adder, multiply_then_add=True): - enciphered = [affine_decipher_letter(l, multiplier, adder, multiply_then_add) for l in message] +def affine_decipher(message, multiplier=1, adder=0, one_based=True): + """Decipher a message + + >>> affine_decipher('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls omytd jlaxe mh', 15, 22, True) + 'hours passed during which jerico tried every trick he could think of' + """ + enciphered = [affine_decipher_letter(l, multiplier, adder, one_based) for l in message] return ''.join(enciphered) @@ -187,26 +223,29 @@ def caesar_break(message, metric=norms.euclidean_distance, target_frequencies=no def affine_break(message, metric=norms.euclidean_distance, target_frequencies=normalised_english_counts, message_frequency_scaling=norms.normalise): """Breaks an affine cipher using frequency analysis + + >>> affine_break('lmyfu bkuusd dyfaxw claol psfaom jfasd snsfg jfaoe ls omytd jlaxe mh jm bfmibj umis hfsul axubafkjamx. ls kffkxwsd jls ofgbjmwfkiu olfmxmtmwaokttg jlsx ls kffkxwsd jlsi zg tsxwjl. jlsx ls umfjsd jlsi zg hfsqysxog. ls dmmdtsd mx jls bats mh bkbsf. ls bfmctsd kfmyxd jls lyj, mztanamyu xmc jm clm cku tmmeaxw kj lai kxd clm ckuxj.') + ((15, 22, True), 0.2357036181865554) """ sanitised_message = sanitise(message) best_multiplier = 0 best_adder = 0 - best_multiply_then_add = True + best_one_based = True best_fit = float("inf") - for multiply_then_add in [True, False]: + for one_based in [True, False]: for multiplier in range(1, 26, 2): for adder in range(26): - plaintext = affine_decipher(sanitised_message, multiplier, adder, multiply_then_add) + plaintext = affine_decipher(sanitised_message, multiplier, adder, one_based) frequencies = message_frequency_scaling(letter_frequencies(plaintext)) fit = metric(target_frequencies, frequencies) - logger.info('Affine break attempt using key {0}x+{1} ({2}) gives fit of {3} and decrypt starting: {4}'.format(multiplier, adder, multiply_then_add, fit, plaintext[:50])) + logger.info('Affine break attempt using key {0}x+{1} ({2}) gives fit of {3} and decrypt starting: {4}'.format(multiplier, adder, one_based, fit, plaintext[:50])) if fit < best_fit: best_fit = fit best_multiplier = multiplier best_adder = adder - best_multiply_then_add = multiply_then_add - logger.info('Affine break best fit with key {0}x+{1} ({2}) gives fit of {3} and decrypt starting: {4}'.format(best_multiplier, best_adder, best_multiply_then_add, best_fit, affine_decipher(sanitised_message, best_multiplier, best_adder, best_multiply_then_add)[:50])) - return (best_multiplier, best_adder, best_multiply_then_add), best_fit + best_one_based = one_based + logger.info('Affine break best fit with key {0}x+{1} ({2}) gives fit of {3} and decrypt starting: {4}'.format(best_multiplier, best_adder, best_one_based, best_fit, affine_decipher(sanitised_message, best_multiplier, best_adder, best_one_based)[:50])) + return (best_multiplier, best_adder, best_one_based), best_fit if __name__ == "__main__": -- 2.34.1