Merge pull request #57 from reaperhulk/ofb-support

Output feedback mode support + test vectors (aes)
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py
index 917c184..af7fe43 100644
--- a/cryptography/bindings/openssl/api.py
+++ b/cryptography/bindings/openssl/api.py
@@ -76,6 +76,8 @@
         assert evp_cipher != self._ffi.NULL
         if isinstance(mode, interfaces.ModeWithInitializationVector):
             iv_nonce = mode.initialization_vector
+        elif isinstance(mode, interfaces.ModeWithNonce):
+            iv_nonce = mode.nonce
         else:
             iv_nonce = self._ffi.NULL
 
diff --git a/cryptography/primitives/block/modes.py b/cryptography/primitives/block/modes.py
index c722e73..62a1c2c 100644
--- a/cryptography/primitives/block/modes.py
+++ b/cryptography/primitives/block/modes.py
@@ -28,4 +28,13 @@
     name = "ECB"
 
 
+class OFB(object):
+    name = "OFB"
+
+    def __init__(self, nonce):
+        super(OFB, self).__init__()
+        self.nonce = nonce
+
+
 interfaces.ModeWithInitializationVector.register(CBC)
+interfaces.ModeWithNonce.register(OFB)
diff --git a/cryptography/primitives/interfaces.py b/cryptography/primitives/interfaces.py
index 6f74ccf..c1fc991 100644
--- a/cryptography/primitives/interfaces.py
+++ b/cryptography/primitives/interfaces.py
@@ -20,3 +20,7 @@
 
 class ModeWithInitializationVector(six.with_metaclass(abc.ABCMeta)):
     pass
+
+
+class ModeWithNonce(six.with_metaclass(abc.ABCMeta)):
+    pass
diff --git a/docs/primitives/symmetric-encryption.rst b/docs/primitives/symmetric-encryption.rst
index f028c75..c4f78a7 100644
--- a/docs/primitives/symmetric-encryption.rst
+++ b/docs/primitives/symmetric-encryption.rst
@@ -68,6 +68,17 @@
                                         reuse an ``initialization_vector`` with
                                         a given ``key``.
 
+.. class:: cryptography.primitives.block.modes.OFB(nonce)
+
+    OFB (Output Feedback) is a mode of operation for block ciphers. It
+    transforms a block cipher into a stream cipher.
+
+    :param bytes nonce: Must be random bytes. They do not need to be kept
+                        secret (they can be included in a transmitted message).
+                        Must be the same number of bytes as the ``block_size``
+                        of the cipher.  Reuse of a ``nonce`` with a given
+                        ``key`` can allow recovery of the original plaintext.
+
 
 Insecure Modes
 --------------
diff --git a/tests/primitives/test_nist.py b/tests/primitives/test_nist.py
index 3dc8277..0e16cc9 100644
--- a/tests/primitives/test_nist.py
+++ b/tests/primitives/test_nist.py
@@ -133,3 +133,50 @@
         actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
         actual_ciphertext += cipher.finalize()
         assert binascii.hexlify(actual_ciphertext) == ciphertext
+
+
+class TestAES_OFB(object):
+    @parameterize_encrypt_test(
+        "AES", "KAT",
+        ("key", "iv", "plaintext", "ciphertext"),
+        [
+            "OFBGFSbox128.rsp",
+            "OFBGFSbox192.rsp",
+            "OFBGFSbox256.rsp",
+            "OFBKeySbox128.rsp",
+            "OFBKeySbox192.rsp",
+            "OFBKeySbox256.rsp",
+            "OFBVarKey128.rsp",
+            "OFBVarKey192.rsp",
+            "OFBVarKey256.rsp",
+            "OFBVarTxt128.rsp",
+            "OFBVarTxt192.rsp",
+            "OFBVarTxt256.rsp",
+        ]
+    )
+    def test_KAT(self, key, iv, plaintext, ciphertext):
+        cipher = BlockCipher(
+            ciphers.AES(binascii.unhexlify(key)),
+            modes.OFB(binascii.unhexlify(iv))
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext
+
+    @parameterize_encrypt_test(
+        "AES", "MMT",
+        ("key", "iv", "plaintext", "ciphertext"),
+        [
+            "OFBMMT128.rsp",
+            "OFBMMT192.rsp",
+            "OFBMMT256.rsp",
+        ]
+    )
+    def test_MMT(self, key, iv, plaintext, ciphertext):
+        cipher = BlockCipher(
+            ciphers.AES(binascii.unhexlify(key)),
+            modes.OFB(binascii.unhexlify(iv))
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext