• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

pyaes/H20-Sep-2017-933490

tests/H20-Sep-2017-341213

PKG-INFOH A D20-Sep-2017665 1615

README.mdH A D20-Sep-201711.3 KiB364242

setup.pyH A D20-Sep-2017850 2419

README.md

1pyaes
2=====
3
4A pure-Python implementation of the AES block cipher algorithm and the common modes of operation (CBC, CFB, CTR, ECB and OFB).
5
6
7Features
8--------
9
10* Supports all AES key sizes
11* Supports all AES common modes
12* Pure-Python (no external dependencies)
13* BlockFeeder API allows streams to easily be encrypted and decrypted
14* Python 2.x and 3.x support (make sure you pass in bytes(), not strings for Python 3)
15
16
17API
18---
19
20All keys may be 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes) long.
21
22To generate a random key use:
23```python
24import os
25
26# 128 bit, 192 bit and 256 bit keys
27key_128 = os.urandom(16)
28key_192 = os.urandom(24)
29key_256 = os.urandom(32)
30```
31
32To generate keys from simple-to-remember passwords, consider using a _password-based key-derivation function_ such as [scrypt](https://github.com/ricmoo/pyscrypt).
33
34
35### Common Modes of Operation
36
37There are many modes of operations, each with various pros and cons. In general though, the **CBC** and **CTR** modes are recommended. The **ECB is NOT recommended.**, and is included primarily for completeness.
38
39Each of the following examples assumes the following key:
40```python
41import pyaes
42
43# A 256 bit (32 byte) key
44key = "This_key_for_demo_purposes_only!"
45
46# For some modes of operation we need a random initialization vector
47# of 16 bytes
48iv = "InitializationVe"
49```
50
51
52#### Counter Mode of Operation (recommended)
53
54```python
55aes = pyaes.AESModeOfOperationCTR(key)
56plaintext = "Text may be any length you wish, no padding is required"
57ciphertext = aes.encrypt(plaintext)
58
59# '''\xb6\x99\x10=\xa4\x96\x88\xd1\x89\x1co\xe6\x1d\xef;\x11\x03\xe3\xee
60#    \xa9V?wY\xbfe\xcdO\xe3\xdf\x9dV\x19\xe5\x8dk\x9fh\xb87>\xdb\xa3\xd6
61#    \x86\xf4\xbd\xb0\x97\xf1\t\x02\xe9 \xed'''
62print repr(ciphertext)
63
64# The counter mode of operation maintains state, so decryption requires
65# a new instance be created
66aes = pyaes.AESModeOfOperationCTR(key)
67decrypted = aes.decrypt(ciphertext)
68
69# True
70print decrypted == plaintext
71
72# To use a custom initial value
73counter = pyaes.Counter(initial_value = 100)
74aes = pyaes.AESModeOfOperationCTR(key, counter = counter)
75ciphertext = aes.encrypt(plaintext)
76
77# '''WZ\x844\x02\xbfoY\x1f\x12\xa6\xce\x03\x82Ei)\xf6\x97mX\x86\xe3\x9d
78#    _1\xdd\xbd\x87\xb5\xccEM_4\x01$\xa6\x81\x0b\xd5\x04\xd7Al\x07\xe5
79#    \xb2\x0e\\\x0f\x00\x13,\x07'''
80print repr(ciphertext)
81```
82
83
84#### Cipher-Block Chaining (recommended)
85
86```python
87aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
88plaintext = "TextMustBe16Byte"
89ciphertext = aes.encrypt(plaintext)
90
91# '\xd6:\x18\xe6\xb1\xb3\xc3\xdc\x87\xdf\xa7|\x08{k\xb6'
92print repr(ciphertext)
93
94
95# The cipher-block chaining mode of operation maintains state, so
96# decryption requires a new instance be created
97aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
98decrypted = aes.decrypt(ciphertext)
99
100# True
101print decrypted == plaintext
102```
103
104
105#### Cipher Feedback
106
107```python
108# Each block into the mode of operation must be a multiple of the segment
109# size. For this example we choose 8 bytes.
110aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
111plaintext =  "TextMustBeAMultipleOfSegmentSize"
112ciphertext = aes.encrypt(plaintext)
113
114# '''v\xa9\xc1w"\x8aL\x93\xcb\xdf\xa0/\xf8Y\x0b\x8d\x88i\xcb\x85rmp
115#    \x85\xfe\xafM\x0c)\xd5\xeb\xaf'''
116print repr(ciphertext)
117
118
119# The cipher-block chaining mode of operation maintains state, so
120# decryption requires a new instance be created
121aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
122decrypted = aes.decrypt(ciphertext)
123
124# True
125print decrypted == plaintext
126```
127
128
129#### Output Feedback Mode of Operation
130
131```python
132aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
133plaintext = "Text may be any length you wish, no padding is required"
134ciphertext = aes.encrypt(plaintext)
135
136# '''v\xa9\xc1wO\x92^\x9e\rR\x1e\xf7\xb1\xa2\x9d"l1\xc7\xe7\x9d\x87(\xc26s
137#    \xdd8\xc8@\xb6\xd9!\xf5\x0cM\xaa\x9b\xc4\xedLD\xe4\xb9\xd8\xdf\x9e\xac
138#    \xa1\xb8\xea\x0f\x8ev\xb5'''
139print repr(ciphertext)
140
141# The counter mode of operation maintains state, so decryption requires
142# a new instance be created
143aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
144decrypted = aes.decrypt(ciphertext)
145
146# True
147print decrypted == plaintext
148```
149
150
151#### Electronic Codebook (NOT recommended)
152
153```python
154aes = pyaes.AESModeOfOperationECB(key)
155plaintext = "TextMustBe16Byte"
156ciphertext = aes.encrypt(plaintext)
157
158# 'L6\x95\x85\xe4\xd9\xf1\x8a\xfb\xe5\x94X\x80|\x19\xc3'
159print repr(ciphertext)
160
161# Since there is no state stored in this mode of operation, it
162# is not necessary to create a new aes object for decryption.
163#aes = pyaes.AESModeOfOperationECB(key)
164decrypted = aes.decrypt(ciphertext)
165
166# True
167print decrypted == plaintext
168```
169
170
171### BlockFeeder
172
173Since most of the modes of operations require data in specific block-sized or segment-sized blocks, it can be difficult when working with large arbitrary streams or strings of data.
174
175The BlockFeeder class is meant to make life easier for you, by buffering bytes across multiple calls and returning bytes as they are available, as well as padding or stripping the output when finished, if necessary.
176
177```python
178import pyaes
179
180# Any mode of operation can be used; for this example CBC
181key = "This_key_for_demo_purposes_only!"
182iv = "InitializationVe"
183
184ciphertext = ''
185
186# We can encrypt one line at a time, regardles of length
187encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv))
188for line in file('/etc/passwd'):
189    ciphertext += encrypter.feed(line)
190
191# Make a final call to flush any remaining bytes and add paddin
192ciphertext += encrypter.feed()
193
194# We can decrypt the cipher text in chunks (here we split it in half)
195decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv))
196decrypted = decrypter.feed(ciphertext[:len(ciphertext) / 2])
197decrypted += decrypter.feed(ciphertext[len(ciphertext) / 2:])
198
199# Again, make a final call to flush any remaining bytes and strip padding
200decrypted += decrypter.feed()
201
202print file('/etc/passwd').read() == decrypted
203```
204
205### Stream Feeder
206
207This is meant to make it even easier to encrypt and decrypt streams and large files.
208
209```python
210import pyaes
211
212# Any mode of operation can be used; for this example CTR
213key = "This_key_for_demo_purposes_only!"
214
215# Create the mode of operation to encrypt with
216mode = pyaes.AESModeOfOperationCTR(key)
217
218# The input and output files
219file_in = file('/etc/passwd')
220file_out = file('/tmp/encrypted.bin', 'wb')
221
222# Encrypt the data as a stream, the file is read in 8kb chunks, be default
223pyaes.encrypt_stream(mode, file_in, file_out)
224
225# Close the files
226file_in.close()
227file_out.close()
228```
229
230Decrypting is identical, except you would use `pyaes.decrypt_stream`, and the encrypted file would be the `file_in` and target for decryption the `file_out`.
231
232### AES block cipher
233
234Generally you should use one of the modes of operation above. This may however be useful for experimenting with a custom mode of operation or dealing with encrypted blocks.
235
236The block cipher requires exactly one block of data to encrypt or decrypt, and each block should be an array with each element an integer representation of a byte.
237
238```python
239import pyaes
240
241# 16 byte block of plain text
242plaintext = "Hello World!!!!!"
243plaintext_bytes = [ ord(c) for c in plaintext ]
244
245# 32 byte key (256 bit)
246key = "This_key_for_demo_purposes_only!"
247
248# Our AES instance
249aes = pyaes.AES(key)
250
251# Encrypt!
252ciphertext = aes.encrypt(plaintext_bytes)
253
254# [55, 250, 182, 25, 185, 208, 186, 95, 206, 115, 50, 115, 108, 58, 174, 115]
255print repr(ciphertext)
256
257# Decrypt!
258decrypted = aes.decrypt(ciphertext)
259
260# True
261print decrypted == plaintext_bytes
262```
263
264What is a key?
265--------------
266
267This seems to be a point of confusion for many people new to using encryption. You can think of the key as the *"password"*. However, these algorithms require the *"password"* to be a specific length.
268
269With AES, there are three possible key lengths, 16-bytes, 24-bytes or 32-bytes. When you create an AES object, the key size is automatically detected, so it is important to pass in a key of the correct length.
270
271Often, you wish to provide a password of arbitrary length, for example, something easy to remember or write down. In these cases, you must come up with a way to transform the password into a key, of a specific length. A **Password-Based Key Derivation Function** (PBKDF) is an algorithm designed for this exact purpose.
272
273Here is an example, using the popular (possibly obsolete?) *crypt* PBKDF:
274
275```
276# See: https://www.dlitz.net/software/python-pbkdf2/
277import pbkdf2
278
279password = "HelloWorld"
280
281# The crypt PBKDF returns a 48-byte string
282key = pbkdf2.crypt(password)
283
284# A 16-byte, 24-byte and 32-byte key, respectively
285key_16 = key[:16]
286key_24 = key[:24]
287key_32 = key[:32]
288```
289
290The [scrypt](https://github.com/ricmoo/pyscrypt) PBKDF is intentionally slow, to make it more difficult to brute-force guess a password:
291
292```
293# See: https://github.com/ricmoo/pyscrypt
294import pyscrypt
295
296password = "HelloWorld"
297
298# Salt is required, and prevents Rainbow Table attacks
299salt = "SeaSalt"
300
301# N, r, and p are parameters to specify how difficult it should be to
302# generate a key; bigger numbers take longer and more memory
303N = 1024
304r = 1
305p = 1
306
307# A 16-byte, 24-byte and 32-byte key, respectively; the scrypt algorithm takes
308# a 6-th parameter, indicating key length
309key_16 = pyscrypt.hash(password, salt, N, r, p, 16)
310key_24 = pyscrypt.hash(password, salt, N, r, p, 24)
311key_32 = pyscrypt.hash(password, salt, N, r, p, 32)
312```
313
314Another possibility, is to use a hashing function, such as SHA256 to hash the password, but this method may be vulnerable to [Rainbow Attacks](http://en.wikipedia.org/wiki/Rainbow_table), unless you use a [salt](http://en.wikipedia.org/wiki/Salt_(cryptography)).
315
316```python
317import hashlib
318
319password = "HelloWorld"
320
321# The SHA256 hash algorithm returns a 32-byte string
322hashed = hashlib.sha256(password).digest()
323
324# A 16-byte, 24-byte and 32-byte key, respectively
325key_16 = hashed[:16]
326key_24 = hashed[:24]
327key_32 = hashed
328```
329
330
331
332
333Performance
334-----------
335
336There is a test case provided in _/tests/test-aes.py_ which does some basic performance testing (its primary purpose is moreso as a regression test).
337
338Based on that test, in **CPython**, this library is about 30x slower than [PyCrypto](https://www.dlitz.net/software/pycrypto/) for CBC, ECB and OFB; about 80x slower for CFB; and 300x slower for CTR.
339
340Based on that same test, in **Pypy**, this library is about 4x slower than [PyCrypto](https://www.dlitz.net/software/pycrypto/) for CBC, ECB and OFB; about 12x slower for CFB; and 19x slower for CTR.
341
342The PyCrypto documentation makes reference to the counter call being responsible for the speed problems of the counter (CTR) mode of operation, which is why they use a specially optimized counter. I will investigate this problem further in the future.
343
344
345FAQ
346---
347
348#### Why do this?
349
350The short answer, *why not?*
351
352The longer answer, is for my [pyscrypt](https://github.com/ricmoo/pyscrypt) library. I required a pure-Python AES implementation that supported 256-bit keys with the counter (CTR) mode of operation. After searching, I found several implementations, but all were missing CTR or only supported 128 bit keys. After all the work of learning AES inside and out to implement the library, it was only a marginal amount of extra work to library-ify a more general solution. So, *why not?*
353
354#### How do I get a question I have added?
355
356E-mail me at pyaes@ricmoo.com with any questions, suggestions, comments, et cetera.
357
358
359#### Can I give you my money?
360
361Umm... Ok? :-)
362
363_Bitcoin_  - `18UDs4qV1shu2CgTS2tKojhCtM69kpnWg9`
364