Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 160 lines (125 sloc) 6.541 kb
f16b361b »
2012-08-19 Initial commit.
1 # RSA Cipher
2 # http://inventwithpython.com/codebreaker (BSD Licensed)
3
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
4 import sys
5
6 # IMPORTANT: The block size MUST be less or equal than the key size!
24fc561d »
2012-08-31 Changes from unit testing.
7 # (Note: The block size is in bytes, the key size is in bits. There
8 # are 8 bits in 1 byte.)
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
9 DEFAULT_BLOCK_SIZE = 128
10 BYTE_SIZE = 256 # One byte has 256 different values.
11
f16b361b »
2012-08-19 Initial commit.
12 def main():
13 # Runs a test that encrypts a message to a file or decrypts a message
14 # from a file.
15 filename = 'encrypted_file.txt'
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
16 mode = 'encrypt' # set to 'encrypt' or 'decrypt'
f16b361b »
2012-08-19 Initial commit.
17
18 if mode == 'encrypt':
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
19 message = '''"Journalists belong in the gutter because that is where the ruling classes throw their guilty secrets." -Gerald Priestland "The Founding Fathers gave the free press the protection it must have to bare the secrets of government and inform the people." -Hugo Black'''
f16b361b »
2012-08-19 Initial commit.
20 privKeyFilename = 'al_sweigart_privkey.txt'
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
21 print('Encrypting and writing to %s...' % (filename))
22 encryptedText = encryptAndWriteToFile(filename, privKeyFilename, message, 128)
f16b361b »
2012-08-19 Initial commit.
23
24 print('Encrypted text:')
25 print(encryptedText)
26
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
27 if mode == 'decrypt':
f16b361b »
2012-08-19 Initial commit.
28 pubKeyFilename = 'al_sweigart_pubkey.txt'
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
29 print('Reading from %s and decrypting...' % (filename))
f16b361b »
2012-08-19 Initial commit.
30 decryptedText = readFromFileAndDecrypt(filename, pubKeyFilename)
31
32 print('Decrypted text:')
33 print(decryptedText)
34
35
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
36 def getBlocksFromText(message, blockSize=DEFAULT_BLOCK_SIZE):
f16b361b »
2012-08-19 Initial commit.
37 # Converts a string message to a list of block integers. Each integer
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
38 # represents 512 (or whatever blockSize is set to) string characters.
f16b361b »
2012-08-19 Initial commit.
39
40 messageBytes = message.encode('ascii') # convert the string to bytes
41 blockInts = []
42 for blockStart in range(0, len(messageBytes), blockSize):
43 # Calculate the block integer for this block of text
44 blockInt = 0
45 for i in range(blockStart, min(blockStart + blockSize, len(messageBytes))):
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
46 blockInt += int(messageBytes[i]) * (BYTE_SIZE ** (i % blockSize))
f16b361b »
2012-08-19 Initial commit.
47 blockInts.append(blockInt)
48 return blockInts
49
50
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
51 def getTextFromBlocks(blockInts, messageLength, blockSize=DEFAULT_BLOCK_SIZE):
24fc561d »
2012-08-31 Changes from unit testing.
52 # Converts a list of block integers to the original message string.
53 # The original message length is needed to properly convert the last
54 # block integer.
f16b361b »
2012-08-19 Initial commit.
55 message = []
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
56
f16b361b »
2012-08-19 Initial commit.
57 for blockInt in blockInts:
58 blockMessage = []
59 for i in range(blockSize-1, -1, -1):
c4fd6ab2 »
2012-08-25 Pylint-suggested fixes.
60 if len(message) + i < messageLength:
24fc561d »
2012-08-31 Changes from unit testing.
61 # Decode the message string for the 512 (or whatever
62 # blockSize is set to) characters from this block integer.
c4fd6ab2 »
2012-08-25 Pylint-suggested fixes.
63 charNumber = blockInt // (BYTE_SIZE ** i)
64 blockInt = blockInt % (BYTE_SIZE ** i)
65 blockMessage.insert(0, bytes([charNumber]).decode('ascii'))
f16b361b »
2012-08-19 Initial commit.
66 message.extend(blockMessage)
67 return ''.join(message)
68
69
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
70 def encryptMessage(message, key, blockSize=DEFAULT_BLOCK_SIZE):
f16b361b »
2012-08-19 Initial commit.
71 # Converts the message string into a list of block integers, and then
24fc561d »
2012-08-31 Changes from unit testing.
72 # encrypt each block integer. Pass the PUBLIC key to encrypt.
f16b361b »
2012-08-19 Initial commit.
73 encryptedBlocks = []
74 n, e = key
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
75
76 for block in getBlocksFromText(message, blockSize):
f16b361b »
2012-08-19 Initial commit.
77 # ciphertext = plaintext ^ e mod n
78 encryptedBlocks.append(pow(block, e, n))
79 return encryptedBlocks
80
81
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
82 def decryptMessage(encryptedBlocks, messageLength, key, blockSize=DEFAULT_BLOCK_SIZE):
f16b361b »
2012-08-19 Initial commit.
83 # Decrypts a list of encrypted block ints into the original message
84 # string. The original message length is required to properly decrypt
85 # the last block. Be sure to pass the PRIVATE key to decrypt.
86 decryptedBlocks = []
87 n, d = key
88 for block in encryptedBlocks:
89 # plaintext = ciphertext ^ d mod n
90 decryptedBlocks.append(pow(block, d, n))
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
91 return getTextFromBlocks(decryptedBlocks, messageLength, blockSize)
f16b361b »
2012-08-19 Initial commit.
92
93
94 def readKeyFile(keyFilename):
95 # Given the filename of a file that contains a public or private key,
96 # return the key as a (n,e) or (n,d) tuple value.
97 fp = open(keyFilename)
98 content = fp.read()
99 fp.close()
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
100 keySize, N, EorD = content.split(',')
101 return (int(keySize), int(N), int(EorD))
f16b361b »
2012-08-19 Initial commit.
102
103
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
104 def encryptAndWriteToFile(messageFilename, keyFilename, message, blockSize=DEFAULT_BLOCK_SIZE):
f16b361b »
2012-08-19 Initial commit.
105 # Using a key from a key file, encrypt the message and save it to a
106 # file. Returns the encrypted message string.
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
107 keySize, N, EorD = readKeyFile(keyFilename)
108
109 # Check that key size is greater than block size.
110 if keySize < blockSize * 8: # * 8 to convert bytes to bits
111 sys.exit('ERROR: Block size is %s and key size is %s. The RSA cipher requires the block size to be less than the key size. Either decrease the block size or use different keys.' % (blockSize, keySize))
112 key = (N, EorD)
f16b361b »
2012-08-19 Initial commit.
113
114 # Encrypt the message
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
115 encryptedBlocks = encryptMessage(message, key, blockSize)
f16b361b »
2012-08-19 Initial commit.
116
117 # Convert the large int values to one string value.
118 for i in range(len(encryptedBlocks)):
119 encryptedBlocks[i] = str(encryptedBlocks[i])
120 encryptedContent = ','.join(encryptedBlocks)
121
122 # Write out the encrypted string to the output file.
123 fp = open(messageFilename, 'w')
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
124 fp.write('%s_%s_%s' % (len(message), blockSize, encryptedContent))
f16b361b »
2012-08-19 Initial commit.
125 fp.close()
126
127 # Also return the encrypted string.
128 return encryptedContent
129
130
131 def readFromFileAndDecrypt(messageFilename, keyFilename):
132 # Using a key from a key file, read an encrypted message from a file
133 # and then decrypt it. Returns the decrypted message string.
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
134 keySize, N, EorD = readKeyFile(keyFilename)
135 key = (N, EorD)
f16b361b »
2012-08-19 Initial commit.
136
137 # Read in the message length and the encrypted message from the file.
138 fp = open(messageFilename)
139 content = fp.read()
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
140 messageLength, blockSize, message = content.split('_')
141 messageLength = int(messageLength)
142 blockSize = int(blockSize)
143
144 # Check that key size is greater than block size.
145 if keySize < blockSize * 8: # * 8 to convert bytes to bits
146 sys.exit('ERROR: Block size is %s and key size is %s. The RSA cipher requires the block size to be less than the key size. Did you specify the correct key file and encrypted file?' % (blockSize, keySize))
f16b361b »
2012-08-19 Initial commit.
147
148 # Convert the encrypted message into large int values.
149 encryptedBlocks = []
150 for block in message.split(','):
151 encryptedBlocks.append(int(block))
152
153 # Decrypt the large int values.
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
154 return decryptMessage(encryptedBlocks, messageLength, key, blockSize)
f16b361b »
2012-08-19 Initial commit.
155
156
91f8abbb »
2012-08-24 First round of personal fixes. This is an unhelpful log message.
157 # If rsaCipher.py is run (instead of imported as a module) call
158 # the main() function.
f16b361b »
2012-08-19 Initial commit.
159 if __name__ == '__main__':
160 main()
Something went wrong with that request. Please try again.