Module szyfrow.affine
+Enciphering and deciphering using the affine cipher. +Also attempts to break messages that use an affine cipher.
+The affine cipher operates one letter at a time. It converts each letter to a +number, then enciphers that number using a multiplier and a number. The result +is taken mod 26 and converted back into a letter.
+For a multiplier m and adder a, a letter converted to number x is +enciphered as E(x) = (m x + a) mod 26. Deciphering uses the modular +inverse of m, mâ»Â¹, so that D(x) = mâ»Â¹ (x - a) mod 26.
+If one_based
is True
, the conversion between letters and numbers maps
+'a' â 1, 'b' â 2, ⦠'z'â 26 and the mod
function is adjusted to keep
+numbers in this range during enciphering and deciphering. If one_based
is
+False
, the conversion maps 'a' â 0, 'b' â 1, ⦠'z'â 25 and mod
behaves
+normally.
+Expand source code +
+"""Enciphering and deciphering using the [affine cipher](https://en.wikipedia.org/wiki/Affine_cipher).
+Also attempts to break messages that use an affine cipher.
+
+The affine cipher operates one letter at a time. It converts each letter to a
+number, then enciphers that number using a multiplier and a number. The result
+is taken mod 26 and converted back into a letter.
+
+For a multiplier _m_ and adder _a_, a letter converted to number _x_ is
+enciphered as _E(x)_ = (_m_ _x_ + _a_) mod 26. Deciphering uses the modular
+inverse of _m_, _m_â»Â¹, so that _D(x)_ = _m_â»Â¹ (_x_ - _a_) mod 26.
+
+If `one_based` is `True`, the conversion between letters and numbers maps
+'a' â 1, 'b' â 2, ⦠'z'â 26 and the `mod` function is adjusted to keep
+numbers in this range during enciphering and deciphering. If `one_based` is
+`False`, the conversion maps 'a' â 0, 'b' â 1, ⦠'z'â 25 and `mod` behaves
+normally.
+"""
+
+from szyfrow.support.utilities import *
+from szyfrow.support.language_models import *
+
+modular_division_table = {
+ (multiplier, (multiplier * plaintext) % 26): plaintext
+ for plaintext in range(26)
+ for multiplier in range(26)
+ }
+
+
+def affine_encipher_letter(accented_letter, multiplier=1, adder=0, one_based=True):
+ """Encipher a letter, given a multiplier and adder.
+
+ Accented version of latin letters (such as é and ö) are converted to their
+ non-accented versions before encryption.
+
+ >>> cat(affine_encipher_letter(l, 3, 5, True) \
+ for l in string.ascii_letters)
+ 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE'
+ >>> cat(affine_encipher_letter(l, 3, 5, False) \
+ for l in string.ascii_letters)
+ 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC'
+ """
+ letter = unaccent(accented_letter)
+ if letter in string.ascii_letters:
+ letter_number = pos(letter)
+ if one_based: letter_number += 1
+ cipher_number = (letter_number * multiplier + adder) % 26
+ if one_based: cipher_number -= 1
+ if letter in string.ascii_uppercase:
+ return unpos(cipher_number).upper()
+ else:
+ return unpos(cipher_number)
+ else:
+ return letter
+
+def affine_decipher_letter(letter, multiplier=1, adder=0, one_based=True):
+ """Encipher a letter, given a multiplier and adder
+
+ >>> cat(affine_decipher_letter(l, 3, 5, True) \
+ for l in 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE')
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ >>> cat(affine_decipher_letter(l, 3, 5, False) \
+ for l in 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC')
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ """
+ if letter in string.ascii_letters:
+ cipher_number = pos(letter)
+ if one_based: cipher_number += 1
+ plaintext_number = (
+ modular_division_table[multiplier, (cipher_number - adder) % 26]
+ )
+ if one_based: plaintext_number -= 1
+ if letter in string.ascii_uppercase:
+ return unpos(plaintext_number).upper()
+ else:
+ return unpos(plaintext_number)
+ else:
+ return letter
+
+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 cat(enciphered)
+
+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 cat(enciphered)
+
+
+
+def affine_break(message, fitness=Pletters):
+ """Breaks an affine cipher using frequency analysis.
+
+ It tries all possible combinations of multiplier, adder, and one_based,
+ scores the fitness of the text decipherd with each combination, and returns
+ the key that produces the most fit deciphered text.
+
+ >>> 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.') # doctest: +ELLIPSIS
+ ((15, 22, True), -340.601181913...)
+ """
+ sanitised_message = sanitise(message)
+ best_multiplier = 0
+ best_adder = 0
+ best_one_based = True
+ best_fit = float("-inf")
+ for one_based in [True, False]:
+ for multiplier in [x for x in range(1, 26, 2) if x != 13]:
+ for adder in range(26):
+ plaintext = affine_decipher(sanitised_message,
+ multiplier, adder, one_based)
+ fit = fitness(plaintext)
+ if fit > best_fit:
+ best_fit = fit
+ best_multiplier = multiplier
+ best_adder = adder
+ best_one_based = one_based
+
+ return (best_multiplier, best_adder, best_one_based), best_fit
+Functions
+-
+
+def affine_break(message, fitness=<function Pletters>) +
+-
++
Breaks an affine cipher using frequency analysis.
+It tries all possible combinations of multiplier, adder, and one_based, +scores the fitness of the text decipherd with each combination, and returns +the key that produces the most fit deciphered text.
+>>> 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.') # doctest: +ELLIPSIS +((15, 22, True), -340.601181913...) +
+++Expand source code +
+
+def affine_break(message, fitness=Pletters): + """Breaks an affine cipher using frequency analysis. + + It tries all possible combinations of multiplier, adder, and one_based, + scores the fitness of the text decipherd with each combination, and returns + the key that produces the most fit deciphered text. + + >>> 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.') # doctest: +ELLIPSIS + ((15, 22, True), -340.601181913...) + """ + sanitised_message = sanitise(message) + best_multiplier = 0 + best_adder = 0 + best_one_based = True + best_fit = float("-inf") + for one_based in [True, False]: + for multiplier in [x for x in range(1, 26, 2) if x != 13]: + for adder in range(26): + plaintext = affine_decipher(sanitised_message, + multiplier, adder, one_based) + fit = fitness(plaintext) + if fit > best_fit: + best_fit = fit + best_multiplier = multiplier + best_adder = adder + best_one_based = one_based + + return (best_multiplier, best_adder, best_one_based), best_fit
+ +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' +
+++Expand source code +
+
+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 cat(enciphered)
+ +def affine_decipher_letter(letter, multiplier=1, adder=0, one_based=True) +
+-
++
Encipher a letter, given a multiplier and adder
+>>> cat(affine_decipher_letter(l, 3, 5, True) for l in 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE') +'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +>>> cat(affine_decipher_letter(l, 3, 5, False) for l in 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC') +'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' +
+++Expand source code +
+
+def affine_decipher_letter(letter, multiplier=1, adder=0, one_based=True): + """Encipher a letter, given a multiplier and adder + + >>> cat(affine_decipher_letter(l, 3, 5, True) \ + for l in 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE') + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + >>> cat(affine_decipher_letter(l, 3, 5, False) \ + for l in 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC') + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + """ + if letter in string.ascii_letters: + cipher_number = pos(letter) + if one_based: cipher_number += 1 + plaintext_number = ( + modular_division_table[multiplier, (cipher_number - adder) % 26] + ) + if one_based: plaintext_number -= 1 + if letter in string.ascii_uppercase: + return unpos(plaintext_number).upper() + else: + return unpos(plaintext_number) + else: + return letter
+ +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' +
+++Expand source code +
+
+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 cat(enciphered)
+ +def affine_encipher_letter(accented_letter, multiplier=1, adder=0, one_based=True) +
+-
++
Encipher a letter, given a multiplier and adder.
+Accented version of latin letters (such as é and ö) are converted to their +non-accented versions before encryption.
+>>> cat(affine_encipher_letter(l, 3, 5, True) for l in string.ascii_letters) +'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE' +>>> cat(affine_encipher_letter(l, 3, 5, False) for l in string.ascii_letters) +'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC' +
+++Expand source code +
+
+def affine_encipher_letter(accented_letter, multiplier=1, adder=0, one_based=True): + """Encipher a letter, given a multiplier and adder. + + Accented version of latin letters (such as é and ö) are converted to their + non-accented versions before encryption. + + >>> cat(affine_encipher_letter(l, 3, 5, True) \ + for l in string.ascii_letters) + 'hknqtwzcfiloruxadgjmpsvybeHKNQTWZCFILORUXADGJMPSVYBE' + >>> cat(affine_encipher_letter(l, 3, 5, False) \ + for l in string.ascii_letters) + 'filoruxadgjmpsvybehknqtwzcFILORUXADGJMPSVYBEHKNQTWZC' + """ + letter = unaccent(accented_letter) + if letter in string.ascii_letters: + letter_number = pos(letter) + if one_based: letter_number += 1 + cipher_number = (letter_number * multiplier + adder) % 26 + if one_based: cipher_number -= 1 + if letter in string.ascii_uppercase: + return unpos(cipher_number).upper() + else: + return unpos(cipher_number) + else: + return letter
+ +def cat(iterable, /) +
+-
++
Concatenate any number of strings.
+The string whose method is called is inserted in between each given string. +The result is returned as a new string.
+Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
+ +def lcat(iterable, /) +
+-
++
Concatenate any number of strings.
+The string whose method is called is inserted in between each given string. +The result is returned as a new string.
+Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
+ +def wcat(iterable, /) +
+-
++
Concatenate any number of strings.
+The string whose method is called is inserted in between each given string. +The result is returned as a new string.
+Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
+