[Python-modules-commits] [pyaes] 01/02: Import pyaes_1.6.0.orig.tar.gz

Tristan Seligmann mithrandi at moszumanska.debian.org
Sat Jul 29 16:25:11 UTC 2017


This is an automated email from the git hooks/post-receive script.

mithrandi pushed a commit to branch debian/master
in repository pyaes.

commit ffae25e07294e8be01544854a2e4630be06cc03d
Author: Tristan Seligmann <mithrandi at mithrandi.net>
Date:   Sat Jul 29 18:12:26 2017 +0200

    Import pyaes_1.6.0.orig.tar.gz
---
 LICENSE.txt               |  22 ++
 PKG-INFO                  |  15 ++
 README.md                 | 363 ++++++++++++++++++++++++++++
 pyaes/__init__.py         |  53 +++++
 pyaes/aes.py              | 589 ++++++++++++++++++++++++++++++++++++++++++++++
 pyaes/blockfeeder.py      | 227 ++++++++++++++++++
 pyaes/util.py             |  60 +++++
 setup.py                  |  23 ++
 tests/test-aes.py         | 158 +++++++++++++
 tests/test-blockfeeder.py | 148 ++++++++++++
 10 files changed, 1658 insertions(+)

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..0417a6c
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Richard Moore
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..fa926d2
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,15 @@
+Metadata-Version: 1.1
+Name: pyaes
+Version: 1.6.0
+Summary: Pure-Python Implementation of the AES block-cipher and common modes of operation
+Home-page: https://github.com/ricmoo/pyaes
+Author: Richard Moore
+Author-email: pyaes at ricmoo.com
+License: License :: OSI Approved :: MIT License
+Description: A pure-Python implementation of the AES (FIPS-197)
+        block-cipher algorithm and common modes of operation (CBC, CFB, CTR, ECB,
+        OFB) with no dependencies beyond standard Python libraries. See README.md
+        for API reference and details.
+Platform: UNKNOWN
+Classifier: Topic :: Security :: Cryptography
+Classifier: License :: OSI Approved :: MIT License
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..05b39f3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,363 @@
+pyaes
+=====
+
+A pure-Python implmentation of the AES block cipher algorithm and the common modes of operation (CBC, CFB, CTR, ECB and OFB).
+
+
+Features
+--------
+
+* Supports all AES key sizes
+* Supports all AES common modes
+* Pure-Python (no external dependancies)
+* BlockFeeder API allows streams to easily be encrypted and decrypted
+* Python 2.x and 3.x support (make sure you pass in bytes(), not strings for Python 3)
+
+
+API
+---
+
+All keys may be 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes) long.
+
+To generate a random key use:
+```python
+import os
+
+# 128 bit, 192 bit and 256 bit keys
+key_128 = os.urandom(16)
+key_192 = os.urandom(24)
+key_256 = os.urandom(32)
+```
+
+To generate keys from simple-to-remember passwords, consider using a _password-based key-derivation function_ such as [scrypt](https://github.com/ricmoo/pyscrypt).
+
+
+### Common Modes of Operation
+
+There 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 primarilty for completeness.
+
+Each of the following examples assumes the following key:
+```python
+import pyaes
+
+# A 256 bit (32 byte) key
+key = "This_key_for_demo_purposes_only!"
+
+# For some modes of operation we need a random initialization vector
+# of 16 bytes
+iv = "InitializationVe"
+```
+
+
+#### Counter Mode of Operation (recommended)
+
+```python
+aes = pyaes.AESModeOfOperationCTR(key)
+plaintext = "Text may be any length you wish, no padding is required"
+ciphertext = aes.encrypt(plaintext)
+
+# '''\xb6\x99\x10=\xa4\x96\x88\xd1\x89\x1co\xe6\x1d\xef;\x11\x03\xe3\xee
+#    \xa9V?wY\xbfe\xcdO\xe3\xdf\x9dV\x19\xe5\x8dk\x9fh\xb87>\xdb\xa3\xd6
+#    \x86\xf4\xbd\xb0\x97\xf1\t\x02\xe9 \xed'''
+print repr(ciphertext)
+
+# The counter mode of operation maintains state, so decryption requires
+# a new instance be created
+aes = pyaes.AESModeOfOperationCTR(key)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+
+# To use a custom initial value
+counter = pyaes.Counter(initial_value = 100)
+aes = pyaes.AESModeOfOperationCTR(key, counter = counter)
+ciphertext = aes.encrypt(plaintext)
+
+# '''WZ\x844\x02\xbfoY\x1f\x12\xa6\xce\x03\x82Ei)\xf6\x97mX\x86\xe3\x9d
+#    _1\xdd\xbd\x87\xb5\xccEM_4\x01$\xa6\x81\x0b\xd5\x04\xd7Al\x07\xe5
+#    \xb2\x0e\\\x0f\x00\x13,\x07'''
+print repr(ciphertext)
+```
+
+
+#### Cipher-Block Chaining (recommended)
+
+```python
+aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
+plaintext = "TextMustBe16Byte"
+ciphertext = aes.encrypt(plaintext)
+
+# '\xd6:\x18\xe6\xb1\xb3\xc3\xdc\x87\xdf\xa7|\x08{k\xb6'
+print repr(ciphertext)
+
+
+# The cipher-block chaining mode of operation maintains state, so 
+# decryption requires a new instance be created
+aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Cipher Feedback
+
+```python
+# Each block into the mode of operation must be a multiple of the segment
+# size. For this example we choose 8 bytes.
+aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
+plaintext =  "TextMustBeAMultipleOfSegmentSize"
+ciphertext = aes.encrypt(plaintext)
+
+# '''v\xa9\xc1w"\x8aL\x93\xcb\xdf\xa0/\xf8Y\x0b\x8d\x88i\xcb\x85rmp
+#    \x85\xfe\xafM\x0c)\xd5\xeb\xaf'''
+print repr(ciphertext)
+
+
+# The cipher-block chaining mode of operation maintains state, so 
+# decryption requires a new instance be created
+aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Output Feedback Mode of Operation
+
+```python
+aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
+plaintext = "Text may be any length you wish, no padding is required"
+ciphertext = aes.encrypt(plaintext)
+
+# '''v\xa9\xc1wO\x92^\x9e\rR\x1e\xf7\xb1\xa2\x9d"l1\xc7\xe7\x9d\x87(\xc26s
+#    \xdd8\xc8@\xb6\xd9!\xf5\x0cM\xaa\x9b\xc4\xedLD\xe4\xb9\xd8\xdf\x9e\xac
+#    \xa1\xb8\xea\x0f\x8ev\xb5'''
+print repr(ciphertext)
+
+# The counter mode of operation maintains state, so decryption requires
+# a new instance be created
+aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Electronic Codebook (NOT recommended)
+
+```python
+aes = pyaes.AESModeOfOperationECB(key)
+plaintext = "TextMustBe16Byte"
+ciphertext = aes.encrypt(plaintext)
+
+# 'L6\x95\x85\xe4\xd9\xf1\x8a\xfb\xe5\x94X\x80|\x19\xc3'
+print repr(ciphertext)
+
+# Since there is no state stored in this mode of operation, it
+# is not necessary to create a new aes object for decryption.
+#aes = pyaes.AESModeOfOperationECB(key)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+### BlockFeeder
+
+Since 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.
+
+The 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.
+
+```python
+import pyaes
+
+# Any mode of operation can be used; for this example CBC
+key = "This_key_for_demo_purposes_only!"
+iv = "InitializationVe"
+
+ciphertext = ''
+
+# We can encrypt one line at a time, regardles of length
+encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv))
+for line in file('/etc/passwd'):
+    ciphertext += encrypter.feed(line)
+
+# Make a final call to flush any remaining bytes and add paddin
+ciphertext += encrypter.feed()
+
+# We can decrypt the cipher text in chunks (here we split it in half)
+decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv))
+decrypted = decrypter.feed(ciphertext[:len(ciphertext) / 2])
+decrypted += decrypter.feed(ciphertext[len(ciphertext) / 2:])
+
+# Again, make a final call to flush any remaining bytes and strip padding
+decrypted += decrypter.feed()
+
+print file('/etc/passwd').read() == decrypted
+```
+
+### Stream Feeder
+
+This is meant to make it even easier to encrypt and decrypt streams and large files.
+
+```python
+import pyaes
+
+# Any mode of operation can be used; for this example CTR
+key = "This_key_for_demo_purposes_only!"
+
+# Create the mode of operation to encrypt with
+mode = pyaes.AESModeOfOperationCTR(key)
+
+# The input and output files
+file_in = file('/etc/passwd')
+file_out = file('/tmp/encrypted.bin', 'wb')
+
+# Encrypt the data as a stream, the file is read in 8kb chunks, be default
+pyaes.encrypt_stream(mode, file_in, file_out)
+
+# Close the files
+file_in.close()
+file_out.close()
+```
+
+Decrypting 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`.
+
+### AES block cipher
+
+Generally 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.
+
+The 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.
+
+```python
+import pyaes
+
+# 16 byte block of plain text
+plaintext = "Hello World!!!!!"
+plaintext_bytes = [ ord(c) for c in plaintext ]
+
+# 32 byte key (256 bit)
+key = "This_key_for_demo_purposes_only!"
+
+# Our AES instance
+aes = pyaes.AES(key)
+
+# Encrypt!
+ciphertext = aes.encrypt(plaintext_bytes)
+
+# [55, 250, 182, 25, 185, 208, 186, 95, 206, 115, 50, 115, 108, 58, 174, 115]
+print repr(ciphertext)
+
+# Decrypt!
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext_bytes
+```
+
+What is a key?
+--------------
+
+This 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.
+
+With 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.
+
+Often, 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.
+
+Here is an example, using the popular (possibly obsolete?) *crypt* PBKDF:
+
+```
+# See: https://www.dlitz.net/software/python-pbkdf2/
+import pbkdf2
+
+password = "HelloWorld"
+
+# The crypt PBKDF returns a 48-byte string
+key = pbkdf2.crypt(password)
+
+# A 16-byte, 24-byte and 32-byte key, respectively
+key_16 = key[:16]
+key_24 = key[:24]
+key_32 = key[:32]
+```
+
+The [scrypt](https://github.com/ricmoo/pyscrypt) PBKDF is intentionally slow, to make it more difficult to brute-force guess a password:
+
+```
+# See: https://github.com/ricmoo/pyscrypt
+import pyscrypt
+
+password = "HelloWorld"
+
+# Salt is required, and prevents Rainbow Table attacks
+salt = "SeaSalt"
+
+# N, r, and p are parameters to specify how difficult it should be to
+# generate a key; bigger numbers take longer and more memory
+N = 1024
+r = 1
+p = 1
+
+# A 16-byte, 24-byte and 32-byte key, respectively; the scrypt algorithm takes
+# a 6-th parameter, indicating key length
+key_16 = pyscrypt.hash(password, salt, N, r, p, 16)
+key_24 = pyscrypt.hash(password, salt, N, r, p, 24)
+key_32 = pyscrypt.hash(password, salt, N, r, p, 32)
+```
+
+Another 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)).
+
+```python
+import hashlib
+
+password = "HelloWorld"
+
+# The SHA256 hash algorithm returns a 32-byte string
+hashed = hashlib.sha256(password).digest()
+
+# A 16-byte, 24-byte and 32-byte key, respectively
+key_16 = hashed[:16]
+key_24 = hashed[:24]
+key_32 = hashed
+```
+
+
+
+
+Performance
+-----------
+
+There 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).
+
+Based 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.
+
+Based 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.
+
+The 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.
+
+
+FAQ
+---
+
+#### Why do this?
+
+The short answer, *why not?*
+
+The 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?*
+
+#### How do I get a question I have added?
+
+E-mail me at pyaes at ricmoo.com with any questions, suggestions, comments, et cetera.
+
+
+#### Can I give you my money?
+
+Umm... Ok? :-)
+
+_Bitcoin_  - `18UDs4qV1shu2CgTS2tKojhCtM69kpnWg9`
diff --git a/pyaes/__init__.py b/pyaes/__init__.py
new file mode 100644
index 0000000..5712f79
--- /dev/null
+++ b/pyaes/__init__.py
@@ -0,0 +1,53 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+
+# Supported key sizes:
+#   128-bit
+#   192-bit
+#   256-bit
+
+
+# Supported modes of operation:
+#   ECB - Electronic Codebook
+#   CBC - Cipher-Block Chaining
+#   CFB - Cipher Feedback
+#   OFB - Output Feedback
+#   CTR - Counter
+
+# See the README.md for API details and general information.
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+VERSION = [1, 3, 0]
+
+from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter
+from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter
+from .blockfeeder import PADDING_NONE, PADDING_DEFAULT
diff --git a/pyaes/aes.py b/pyaes/aes.py
new file mode 100644
index 0000000..c6e8bc0
--- /dev/null
+++ b/pyaes/aes.py
@@ -0,0 +1,589 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+
+# Honestly, the best description of the modes of operations are the wonderful
+# diagrams on Wikipedia. They explain in moments what my words could never
+# achieve. Hence the inline documentation here is sparer than I'd prefer.
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+# Supported key sizes:
+#   128-bit
+#   192-bit
+#   256-bit
+
+
+# Supported modes of operation:
+#   ECB - Electronic Codebook
+#   CBC - Cipher-Block Chaining
+#   CFB - Cipher Feedback
+#   OFB - Output Feedback
+#   CTR - Counter
+
+
+# See the README.md for API details and general information.
+
+
+import copy
+import struct
+
+__all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB",
+           "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"]
+
+
+def _compact_word(word):
+    return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]
+
+def _string_to_bytes(text):
+    return list(ord(c) for c in text)
+
+def _bytes_to_string(binary):
+    return "".join(chr(b) for b in binary)
+
+def _concat_list(a, b):
+    return a + b
+
+
+# Python 3 compatibility
+try:
+    xrange
+except Exception:
+    xrange = range
+
+    # Python 3 supports bytes, which is already an array of integers
+    def _string_to_bytes(text):
+        if isinstance(text, bytes):
+            return text
+        return [ord(c) for c in text]
+
+    # In Python 3, we return bytes
+    def _bytes_to_string(binary):
+        return bytes(binary)
+
+    # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first
+    def _concat_list(a, b):
+        return a + bytes(b)
+
+
+# Based *largely* on the Rijndael implementation
+# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
+class AES(object):
+    '''Encapsulates the AES block cipher.
+
+    You generally should not need this. Use the AESModeOfOperation classes
+    below instead.'''
+
+    # Number of rounds by keysize
+    number_of_rounds = {16: 10, 24: 12, 32: 14}
+
+    # Round constant words
+    rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]
+
+    # S-box and Inverse S-box (S is for Substitution)
+    S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x [...]
+    Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x [...]
+
+    # Transformations for encryption
+    T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0 [...]
+    T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0 [...]
+    T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0 [...]
+    T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0 [...]
+
+    # Transformations for decryption
+    T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0 [...]
+    T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0 [...]
+    T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0 [...]
+    T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0 [...]
+
+    # Transformations for decryption key expansion
+    U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0 [...]
+    U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0 [...]
+    U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0 [...]
+    U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0 [...]
+
+    def __init__(self, key):
+
+        if len(key) not in (16, 24, 32):
+            raise ValueError('Invalid key size')
+
+        rounds = self.number_of_rounds[len(key)]
+
+        # Encryption round keys
+        self._Ke = [[0] * 4 for i in xrange(rounds + 1)]
+
+        # Decryption round keys
+        self._Kd = [[0] * 4 for i in xrange(rounds + 1)]
+
+        round_key_count = (rounds + 1) * 4
+        KC = len(key) // 4
+
+        # Convert the key into ints
+        tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ]
+
+        # Copy values into round key arrays
+        for i in xrange(0, KC):
+            self._Ke[i // 4][i % 4] = tk[i]
+            self._Kd[rounds - (i // 4)][i % 4] = tk[i]
+
+        # Key expansion (fips-197 section 5.2)
+        rconpointer = 0
+        t = KC
+        while t < round_key_count:
+
+            tt = tk[KC - 1]
+            tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^
+                      (self.S[(tt >>  8) & 0xFF] << 16) ^
+                      (self.S[ tt        & 0xFF] <<  8) ^
+                       self.S[(tt >> 24) & 0xFF]        ^
+                      (self.rcon[rconpointer] << 24))
+            rconpointer += 1
+
+            if KC != 8:
+                for i in xrange(1, KC):
+                    tk[i] ^= tk[i - 1]
+
+            # Key expansion for 256-bit keys is "slightly different" (fips-197)
+            else:
+                for i in xrange(1, KC // 2):
+                    tk[i] ^= tk[i - 1]
+                tt = tk[KC // 2 - 1]
+
+                tk[KC // 2] ^= (self.S[ tt        & 0xFF]        ^
+                               (self.S[(tt >>  8) & 0xFF] <<  8) ^
+                               (self.S[(tt >> 16) & 0xFF] << 16) ^
+                               (self.S[(tt >> 24) & 0xFF] << 24))
+
+                for i in xrange(KC // 2 + 1, KC):
+                    tk[i] ^= tk[i - 1]
+
+            # Copy values into round key arrays
+            j = 0
+            while j < KC and t < round_key_count:
+                self._Ke[t // 4][t % 4] = tk[j]
+                self._Kd[rounds - (t // 4)][t % 4] = tk[j]
+                j += 1
+                t += 1
+
+        # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
+        for r in xrange(1, rounds):
+            for j in xrange(0, 4):
+                tt = self._Kd[r][j]
+                self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^
+                                  self.U2[(tt >> 16) & 0xFF] ^
+                                  self.U3[(tt >>  8) & 0xFF] ^
+                                  self.U4[ tt        & 0xFF])
+
+    def encrypt(self, plaintext):
+        'Encrypt a block of plain text using the AES block cipher.'
+
+        if len(plaintext) != 16:
+            raise ValueError('wrong block length')
+
+        rounds = len(self._Ke) - 1
+        (s1, s2, s3) = [1, 2, 3]
+        a = [0, 0, 0, 0]
+
+        # Convert plaintext to (ints ^ key)
+        t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)]
+
+        # Apply round transforms
+        for r in xrange(1, rounds):
+            for i in xrange(0, 4):
+                a[i] = (self.T1[(t[ i          ] >> 24) & 0xFF] ^
+                        self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+                        self.T3[(t[(i + s2) % 4] >>  8) & 0xFF] ^
+                        self.T4[ t[(i + s3) % 4]        & 0xFF] ^
+                        self._Ke[r][i])
+            t = copy.copy(a)
+
+        # The last round is special
+        result = [ ]
+        for i in xrange(0, 4):
+            tt = self._Ke[rounds][i]
+            result.append((self.S[(t[ i           ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+            result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+            result.append((self.S[(t[(i + s2) % 4] >>  8) & 0xFF] ^ (tt >>  8)) & 0xFF)
+            result.append((self.S[ t[(i + s3) % 4]        & 0xFF] ^  tt       ) & 0xFF)
+
+        return result
+
+    def decrypt(self, ciphertext):
+        'Decrypt a block of cipher text using the AES block cipher.'
+
+        if len(ciphertext) != 16:
+            raise ValueError('wrong block length')
+
+        rounds = len(self._Kd) - 1
+        (s1, s2, s3) = [3, 2, 1]
+        a = [0, 0, 0, 0]
+
+        # Convert ciphertext to (ints ^ key)
+        t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)]
+
+        # Apply round transforms
+        for r in xrange(1, rounds):
+            for i in xrange(0, 4):
+                a[i] = (self.T5[(t[ i          ] >> 24) & 0xFF] ^
+                        self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+                        self.T7[(t[(i + s2) % 4] >>  8) & 0xFF] ^
+                        self.T8[ t[(i + s3) % 4]        & 0xFF] ^
+                        self._Kd[r][i])
+            t = copy.copy(a)
+
+        # The last round is special
+        result = [ ]
+        for i in xrange(0, 4):
+            tt = self._Kd[rounds][i]
+            result.append((self.Si[(t[ i           ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+            result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+            result.append((self.Si[(t[(i + s2) % 4] >>  8) & 0xFF] ^ (tt >>  8)) & 0xFF)
+            result.append((self.Si[ t[(i + s3) % 4]        & 0xFF] ^  tt       ) & 0xFF)
+
+        return result
+
+
+class Counter(object):
+    '''A counter object for the Counter (CTR) mode of operation.
+
+       To create a custom counter, you can usually just override the
+       increment method.'''
+
+    def __init__(self, initial_value = 1):
+
+        # Convert the value into an array of bytes long
+        self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ]
+
+    value = property(lambda s: s._counter)
+
+    def increment(self):
+        '''Increment the counter (overflow rolls back to 0).'''
+
+        for i in xrange(len(self._counter) - 1, -1, -1):
+            self._counter[i] += 1
+
+            if self._counter[i] < 256: break
+
+            # Carry the one
+            self._counter[i] = 0
+
+        # Overflow
+        else:
+            self._counter = [ 0 ] * len(self._counter)
+
+
+class AESBlockModeOfOperation(object):
+    '''Super-class for AES modes of operation that require blocks.'''
+    def __init__(self, key):
+        self._aes = AES(key)
+
+    def decrypt(self, ciphertext):
+        raise Exception('not implemented')
+
+    def encrypt(self, plaintext):
+        raise Exception('not implemented')
+
+
+class AESStreamModeOfOperation(AESBlockModeOfOperation):
+    '''Super-class for AES modes of operation that are stream-ciphers.'''
+
+class AESSegmentModeOfOperation(AESStreamModeOfOperation):
+    '''Super-class for AES modes of operation that segment data.'''
+
+    segment_bytes = 16
+
+
+
+class AESModeOfOperationECB(AESBlockModeOfOperation):
+    '''AES Electronic Codebook Mode of Operation.
+
+       o Block-cipher, so data must be padded to 16 byte boundaries
+
+   Security Notes:
+       o This mode is not recommended
+       o Any two identical blocks produce identical encrypted values,
+         exposing data patterns. (See the image of Tux on wikipedia)
+
+   Also see:
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1'''
+
+
+    name = "Electronic Codebook (ECB)"
+
+    def encrypt(self, plaintext):
+        if len(plaintext) != 16:
+            raise ValueError('plaintext block must be 16 bytes')
+
+        plaintext = _string_to_bytes(plaintext)
+        return _bytes_to_string(self._aes.encrypt(plaintext))
+
+    def decrypt(self, ciphertext):
+        if len(ciphertext) != 16:
+            raise ValueError('ciphertext block must be 16 bytes')
+
+        ciphertext = _string_to_bytes(ciphertext)
+        return _bytes_to_string(self._aes.decrypt(ciphertext))
+
+
+
+class AESModeOfOperationCBC(AESBlockModeOfOperation):
+    '''AES Cipher-Block Chaining Mode of Operation.
+
+       o The Initialization Vector (IV)
+       o Block-cipher, so data must be padded to 16 byte boundaries
+       o An incorrect initialization vector will only cause the first
+         block to be corrupt; all other blocks will be intact
+       o A corrupt bit in the cipher text will cause a block to be
+         corrupted, and the next block to be inverted, but all other
+         blocks will be intact.
+
+   Security Notes:
+       o This method (and CTR) ARE recommended.
+
+   Also see:
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2'''
+
+
+    name = "Cipher-Block Chaining (CBC)"
+
+    def __init__(self, key, iv = None):
+        if iv is None:
+            self._last_cipherblock = [ 0 ] * 16
+        elif len(iv) != 16:
+            raise ValueError('initialization vector must be 16 bytes')
+        else:
+            self._last_cipherblock = _string_to_bytes(iv)
+
+        AESBlockModeOfOperation.__init__(self, key)
+
+    def encrypt(self, plaintext):
+        if len(plaintext) != 16:
+            raise ValueError('plaintext block must be 16 bytes')
+
+        plaintext = _string_to_bytes(plaintext)
+        precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ]
+        self._last_cipherblock = self._aes.encrypt(precipherblock)
+
+        return _bytes_to_string(self._last_cipherblock)
+
+    def decrypt(self, ciphertext):
+        if len(ciphertext) != 16:
+            raise ValueError('ciphertext block must be 16 bytes')
+
+        cipherblock = _string_to_bytes(ciphertext)
+        plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ]
+        self._last_cipherblock = cipherblock
+
+        return _bytes_to_string(plaintext)
+
+
+
+class AESModeOfOperationCFB(AESSegmentModeOfOperation):
+    '''AES Cipher Feedback Mode of Operation.
+
+       o A stream-cipher, so input does not need to be padded to blocks,
+         but does need to be padded to segment_size
+
+    Also see:
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3'''
+
+
+    name = "Cipher Feedback (CFB)"
+
+    def __init__(self, key, iv, segment_size = 1):
+        if segment_size == 0: segment_size = 1
+
+        if iv is None:
+            self._shift_register = [ 0 ] * 16
+        elif len(iv) != 16:
+            raise ValueError('initialization vector must be 16 bytes')
+        else:
+          self._shift_register = _string_to_bytes(iv)
+
+        self._segment_bytes = segment_size
+
+        AESBlockModeOfOperation.__init__(self, key)
+
+    segment_bytes = property(lambda s: s._segment_bytes)
+
+    def encrypt(self, plaintext):
+        if len(plaintext) % self._segment_bytes != 0:
+            raise ValueError('plaintext block must be a multiple of segment_size')
+
+        plaintext = _string_to_bytes(plaintext)
+
+        # Break block into segments
+        encrypted = [ ]
+        for i in xrange(0, len(plaintext), self._segment_bytes):
+            plaintext_segment = plaintext[i: i + self._segment_bytes]
+            xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)]
+            cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ]
+
+            # Shift the top bits out and the ciphertext in
+            self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)
+
+            encrypted.extend(cipher_segment)
+
+        return _bytes_to_string(encrypted)
+
+    def decrypt(self, ciphertext):
+        if len(ciphertext) % self._segment_bytes != 0:
+            raise ValueError('ciphertext block must be a multiple of segment_size')
+
+        ciphertext = _string_to_bytes(ciphertext)
+
+        # Break block into segments
+        decrypted = [ ]
+        for i in xrange(0, len(ciphertext), self._segment_bytes):
+            cipher_segment = ciphertext[i: i + self._segment_bytes]
+            xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)]
+            plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ]
+
+            # Shift the top bits out and the ciphertext in
+            self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)
+
+            decrypted.extend(plaintext_segment)
+
+        return _bytes_to_string(decrypted)
+
+
+
+class AESModeOfOperationOFB(AESStreamModeOfOperation):
+    '''AES Output Feedback Mode of Operation.
+
+       o A stream-cipher, so input does not need to be padded to blocks,
+         allowing arbitrary length data.
+       o A bit twiddled in the cipher text, twiddles the same bit in the
+         same bit in the plain text, which can be useful for error
+         correction techniques.
+
+    Also see:
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4'''
+
+
+    name = "Output Feedback (OFB)"
+
+    def __init__(self, key, iv = None):
+        if iv is None:
+            self._last_precipherblock = [ 0 ] * 16
+        elif len(iv) != 16:
... 736 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pyaes.git



More information about the Python-modules-commits mailing list