Merge pull request #53 from reaperhulk/ecb-support-im-sorry

ECB Support
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py
index 54a74d0..917c184 100644
--- a/cryptography/bindings/openssl/api.py
+++ b/cryptography/bindings/openssl/api.py
@@ -13,6 +13,8 @@
 
 from __future__ import absolute_import, division, print_function
 
+from cryptography.primitives import interfaces
+
 import cffi
 
 
@@ -72,11 +74,14 @@
         )
         evp_cipher = self._lib.EVP_get_cipherbyname(ciphername.encode("ascii"))
         assert evp_cipher != self._ffi.NULL
-        # TODO: only use the key and initialization_vector as needed. Sometimes
-        # this needs to be a DecryptInit, when?
+        if isinstance(mode, interfaces.ModeWithInitializationVector):
+            iv_nonce = mode.initialization_vector
+        else:
+            iv_nonce = self._ffi.NULL
+
+        # TODO: Sometimes this needs to be a DecryptInit, when?
         res = self._lib.EVP_EncryptInit_ex(
-            ctx, evp_cipher, self._ffi.NULL, cipher.key,
-            mode.initialization_vector
+            ctx, evp_cipher, self._ffi.NULL, cipher.key, iv_nonce
         )
         assert res != 0
 
diff --git a/cryptography/primitives/block/modes.py b/cryptography/primitives/block/modes.py
index de31f08..c722e73 100644
--- a/cryptography/primitives/block/modes.py
+++ b/cryptography/primitives/block/modes.py
@@ -13,6 +13,8 @@
 
 from __future__ import absolute_import, division, print_function
 
+from cryptography.primitives import interfaces
+
 
 class CBC(object):
     name = "CBC"
@@ -20,3 +22,10 @@
     def __init__(self, initialization_vector):
         super(CBC, self).__init__()
         self.initialization_vector = initialization_vector
+
+
+class ECB(object):
+    name = "ECB"
+
+
+interfaces.ModeWithInitializationVector.register(CBC)
diff --git a/cryptography/primitives/interfaces.py b/cryptography/primitives/interfaces.py
new file mode 100644
index 0000000..6f74ccf
--- /dev/null
+++ b/cryptography/primitives/interfaces.py
@@ -0,0 +1,22 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+
+import abc
+
+import six
+
+
+class ModeWithInitializationVector(six.with_metaclass(abc.ABCMeta)):
+    pass
diff --git a/docs/primitives/symmetric-encryption.rst b/docs/primitives/symmetric-encryption.rst
index 1b8d1d7..d0429d4 100644
--- a/docs/primitives/symmetric-encryption.rst
+++ b/docs/primitives/symmetric-encryption.rst
@@ -67,3 +67,16 @@
                                         ``block_size`` of the cipher. Do not
                                         reuse an ``initialization_vector`` with
                                         a given ``key``.
+
+
+Insecure Modes
+--------------
+
+.. warning:: Do not use. This is an insecure mode.
+.. class:: cryptography.primitives.block.modes.ECB()
+
+    ECB (Electronic Code Book) is the simplest mode of operation for block
+    ciphers. The data is separated into blocks and each block is encrypted
+    separately. This means identical plaintext blocks will always result in
+    identical encrypted blocks. Due to this property it is not recommended
+    for use. Really, don't use it. Just. Don't.
diff --git a/setup.py b/setup.py
index 1044c97..cbbf100 100644
--- a/setup.py
+++ b/setup.py
@@ -21,9 +21,11 @@
 
 
 CFFI_DEPENDENCY = "cffi>=0.6"
+SIX_DEPENDENCY = "six>=1.4.1"
 
 install_requires = [
     CFFI_DEPENDENCY,
+    SIX_DEPENDENCY
 ]
 
 setup_requires = [
diff --git a/tests/bindings/test_openssl.py b/tests/bindings/test_openssl.py
index 1579f00..b23c4cc 100644
--- a/tests/bindings/test_openssl.py
+++ b/tests/bindings/test_openssl.py
@@ -11,7 +11,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from cryptography.bindings.openssl import api
+from cryptography.bindings.openssl.api import api
 
 
 class TestOpenSSL(object):
diff --git a/tests/primitives/test_nist.py b/tests/primitives/test_nist.py
index 8bef118..3dc8277 100644
--- a/tests/primitives/test_nist.py
+++ b/tests/primitives/test_nist.py
@@ -86,3 +86,50 @@
         actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
         actual_ciphertext += cipher.finalize()
         assert binascii.hexlify(actual_ciphertext) == ciphertext
+
+
+class TestAES_ECB(object):
+    @parameterize_encrypt_test(
+        "AES", "KAT",
+        ("key", "plaintext", "ciphertext"),
+        [
+            "ECBGFSbox128.rsp",
+            "ECBGFSbox192.rsp",
+            "ECBGFSbox256.rsp",
+            "ECBKeySbox128.rsp",
+            "ECBKeySbox192.rsp",
+            "ECBKeySbox256.rsp",
+            "ECBVarKey128.rsp",
+            "ECBVarKey192.rsp",
+            "ECBVarKey256.rsp",
+            "ECBVarTxt128.rsp",
+            "ECBVarTxt192.rsp",
+            "ECBVarTxt256.rsp",
+        ]
+    )
+    def test_KAT(self, key, plaintext, ciphertext):
+        cipher = BlockCipher(
+            ciphers.AES(binascii.unhexlify(key)),
+            modes.ECB()
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext
+
+    @parameterize_encrypt_test(
+        "AES", "MMT",
+        ("key", "plaintext", "ciphertext"),
+        [
+            "ECBMMT128.rsp",
+            "ECBMMT192.rsp",
+            "ECBMMT256.rsp",
+        ]
+    )
+    def test_MMT(self, key, plaintext, ciphertext):
+        cipher = BlockCipher(
+            ciphers.AES(binascii.unhexlify(key)),
+            modes.ECB()
+        )
+        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+        actual_ciphertext += cipher.finalize()
+        assert binascii.hexlify(actual_ciphertext) == ciphertext