Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 85 lines (65 sloc) 2.827 kB
f16b361 @asweigart Initial commit.
authored
1 # Affine Cipher
2 # http://inventwithpython.com/codebreaker (BSD Licensed)
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
3
f16b361 @asweigart Initial commit.
authored
4 import sys, random, pyperclip
5
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
6 LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
f16b361 @asweigart Initial commit.
authored
7
8
9 def main():
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
10 myMessage = 'A computer would deserve to be called intelligent if it could deceive a human into believing that it was human. -Alan Turing'
11 myKeyA, myKeyB = 5, 7
12 myMode = 'encrypt' # set to 'encrypt' or 'decrypt'
f16b361 @asweigart Initial commit.
authored
13
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
14 myMessage = myMessage.upper()
f16b361 @asweigart Initial commit.
authored
15
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
16 if myMode == 'encrypt':
17 translated = encryptMessage(myKeyA, myKeyB, myMessage)
18 elif myMode == 'decrypt':
19 translated = decryptMessage(myKeyA, myKeyB, myMessage)
f16b361 @asweigart Initial commit.
authored
20
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
21 print('%sed text:' % (myMode.title()))
f16b361 @asweigart Initial commit.
authored
22 print(translated)
23 pyperclip.copy(translated)
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
24 print('Full %sed text copied to clipboard.' % (myMode))
f16b361 @asweigart Initial commit.
authored
25
26
27 def gcd(a, b):
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
28 # Return the Greatest Common Divisor of a and b using Euclid's Algorithm
f16b361 @asweigart Initial commit.
authored
29 while a != 0:
30 a, b = b % a, a
31 return b
32
33
34 def findModInverse(a, m):
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
35 # Return the modular inverse of a mod m.
36 # Uses the naive bruteforce approach to finding mod inverses.
f16b361 @asweigart Initial commit.
authored
37 for b in range(m):
38 if (a * b) % m == 1:
39 return b
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
40 return None # no mod inverse exists because a & m aren't relatively prime
f16b361 @asweigart Initial commit.
authored
41
42
43 def encryptMessage(keyA, keyB, message):
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
44 # key strength and validity checks
45 if keyA == 1:
46 sys.exit('The affine cipher becomes incredibly weak when keyA is set to 1. Choose a different key.')
47 if keyB == 0:
48 sys.exit('The affine cipher becomes incredibly weak when keyB is set to 0. Choose a different key.')
49 if gcd(keyA, len(LETTERS)) != 1:
50 sys.exit('The key (%s) and the size of the alphabet (%s) are not relatively prime. Choose a different key.' % (keyA, len(LETTERS)))
51
f16b361 @asweigart Initial commit.
authored
52 ciphertext = ''
53 for symbol in message:
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
54 if symbol in LETTERS:
f16b361 @asweigart Initial commit.
authored
55 # encrypt this symbol
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
56 symIndex = LETTERS.find(symbol)
57 ciphertext += LETTERS[(symIndex * keyA + keyB) % len(LETTERS)]
f16b361 @asweigart Initial commit.
authored
58 else:
59 # just append this symbol unencrypted
60 ciphertext += symbol
61 return ciphertext
62
63
64 def decryptMessage(keyA, keyB, message):
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
65 if gcd(keyA, len(LETTERS)) != 1:
66 sys.exit('The key (%s) and the size of the alphabet (%s) are not relatively prime. Choose a different key.' % (keyA, len(LETTERS)))
67
f16b361 @asweigart Initial commit.
authored
68 plaintext = ''
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
69 modInverseOfKeyA = findModInverse(keyA, len(LETTERS))
f16b361 @asweigart Initial commit.
authored
70
71 for symbol in message:
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
72 if symbol in LETTERS:
f16b361 @asweigart Initial commit.
authored
73 # decrypt this symbol
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
74 symIndex = LETTERS.find(symbol)
75 plaintext += LETTERS[(symIndex - keyB) * modInverseOfKeyA % len(LETTERS)]
f16b361 @asweigart Initial commit.
authored
76 else:
77 # just append this symbol unencrypted
78 plaintext += symbol
79 return plaintext
80
81
91f8abb @asweigart First round of personal fixes. This is an unhelpful log message.
authored
82 # If affineCipher.py is run (instead of imported as a module) call
83 # the main() function.
f16b361 @asweigart Initial commit.
authored
84 if __name__ == '__main__':
85 main()
Something went wrong with that request. Please try again.