Merge pull request #80 from reaperhulk/evp-md

MD test vectors
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..398ff08
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,2 @@
+[run]
+branch = True
diff --git a/.gitignore b/.gitignore
index ecf07a0..5d187cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@
 pycparser-*.egg/
 pytest-*.egg/
 dist/
+htmlcov/
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 1aa37e4..b3b7f35 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -1,7 +1,9 @@
 AUTHORS
 =======
 
-* Alex Gaynor <alex.gaynor@gmail.com>
+PGP key fingerprints are enclosed in parentheses.
+
+* Alex Gaynor <alex.gaynor@gmail.com> (E27D 4AA0 1651 72CB C5D2  AF2B 125F 5C67 DFE9 4084)
 * Hynek Schlawack <hs@ox.cx>
 * Donald Stufft <donald@stufft.io>
 * Laurens Van Houtven <_@lvh.io>
diff --git a/README.rst b/README.rst
index 9188106..01f3ead 100644
--- a/README.rst
+++ b/README.rst
@@ -1,11 +1,11 @@
 Cryptography
 ============
 
-.. image:: https://travis-ci.org/alex/cryptography.png?branch=master
-   :target: https://travis-ci.org/alex/cryptography
+.. image:: https://travis-ci.org/pyca/cryptography.png?branch=master
+   :target: https://travis-ci.org/pyca/cryptography
 
-.. image:: https://coveralls.io/repos/alex/cryptography/badge.png?branch=master
-    :target: https://coveralls.io/r/alex/cryptography?branch=master
+.. image:: https://coveralls.io/repos/pyca/cryptography/badge.png?branch=master
+    :target: https://coveralls.io/r/pyca/cryptography?branch=master
 
 ``cryptography`` is a package designed to expose cryptographic primitives and
 recipes to Python developers.
@@ -16,3 +16,12 @@
 You can find more documentation at `Read The Docs`_.
 
 .. _`Read The Docs`: https://cryptography.readthedocs.org/
+
+Discussion
+~~~~~~~~~~
+
+We maintain a `cryptography-dev`_ mailing list for development discussion.
+
+You can also join #cryptography-dev on Freenode to ask questions or get involved.
+
+.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev
diff --git a/cryptography/__about__.py b/cryptography/__about__.py
index 6499ff2..e5eca6c 100644
--- a/cryptography/__about__.py
+++ b/cryptography/__about__.py
@@ -20,7 +20,7 @@
 __title__ = "cryptography"
 __summary__ = ("cryptography is a package designed to expose cryptographic "
                "primitives and recipes to Python developers.")
-__uri__ = "https://github.com/alex/cryptography"
+__uri__ = "https://github.com/pyca/cryptography"
 
 __version__ = "0.1.dev1"
 
diff --git a/cryptography/bindings/__init__.py b/cryptography/bindings/__init__.py
index e69de29..215f17c 100644
--- a/cryptography/bindings/__init__.py
+++ b/cryptography/bindings/__init__.py
@@ -0,0 +1,20 @@
+# 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 cryptography.bindings import openssl
+
+
+_default_api = openssl.api
+_ALL_APIS = [
+    openssl.api
+]
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py
index 2843757..79ec5ee 100644
--- a/cryptography/bindings/openssl/api.py
+++ b/cryptography/bindings/openssl/api.py
@@ -25,26 +25,58 @@
     OpenSSL API wrapper.
     """
     _modules = [
+        "bignum",
+        "bio",
+        "conf",
+        "crypto",
+        "dh",
+        "dsa",
+        "engine",
+        "err",
         "evp",
         "opensslv",
+        "rand",
+        "rsa",
+        "ssl",
     ]
 
     def __init__(self):
         self.ffi = cffi.FFI()
         includes = []
+        functions = []
+        macros = []
         for name in self._modules:
             __import__("cryptography.bindings.openssl." + name)
             module = sys.modules["cryptography.bindings.openssl." + name]
             self.ffi.cdef(module.TYPES)
-            self.ffi.cdef(module.FUNCTIONS)
+
+            macros.append(module.MACROS)
+            functions.append(module.FUNCTIONS)
             includes.append(module.INCLUDES)
 
+        # loop over the functions & macros after declaring all the types
+        # so we can set interdependent types in different files and still
+        # have them all defined before we parse the funcs & macros
+        for func in functions:
+            self.ffi.cdef(func)
+        for macro in macros:
+            self.ffi.cdef(macro)
+
+        # We include functions here so that if we got any of their definitions
+        # wrong, the underlying C compiler will explode. In C you are allowed
+        # to re-declare a function if it has the same signature. That is:
+        #   int foo(int);
+        #   int foo(int);
+        # is legal, but the following will fail to compile:
+        #   int foo(int);
+        #   int foo(short);
         self.lib = self.ffi.verify(
-            source="\n".join(includes),
-            libraries=["crypto"]
+            source="\n".join(includes + functions),
+            libraries=["crypto", "ssl"],
         )
 
         self.lib.OpenSSL_add_all_algorithms()
+        self.lib.SSL_load_error_strings()
 
     def openssl_version_text(self):
         """
@@ -54,6 +86,10 @@
         """
         return self.ffi.string(self.lib.OPENSSL_VERSION_TEXT).decode("ascii")
 
+    def supports_cipher(self, ciphername):
+        return (self.ffi.NULL !=
+                self.lib.EVP_get_cipherbyname(ciphername.encode("ascii")))
+
     def create_block_cipher_context(self, cipher, mode):
         ctx = self.ffi.new("EVP_CIPHER_CTX *")
         res = self.lib.EVP_CIPHER_CTX_init(ctx)
@@ -62,11 +98,13 @@
         # TODO: compute name using a better algorithm
         ciphername = "{0}-{1}-{2}".format(
             cipher.name, cipher.key_size, mode.name
-        )
+        ).lower()
         evp_cipher = self.lib.EVP_get_cipherbyname(ciphername.encode("ascii"))
         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/bindings/openssl/bignum.py b/cryptography/bindings/openssl/bignum.py
new file mode 100644
index 0000000..72d467c
--- /dev/null
+++ b/cryptography/bindings/openssl/bignum.py
@@ -0,0 +1,34 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/bn.h>
+"""
+
+TYPES = """
+typedef ... BIGNUM;
+typedef ... BN_ULONG;
+"""
+
+FUNCTIONS = """
+BIGNUM *BN_new();
+void BN_free(BIGNUM *);
+
+int BN_set_word(BIGNUM *, BN_ULONG);
+
+char *BN_bn2hex(const BIGNUM *);
+int BN_hex2bn(BIGNUM **, const char *);
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/bio.py b/cryptography/bindings/openssl/bio.py
new file mode 100644
index 0000000..88be788
--- /dev/null
+++ b/cryptography/bindings/openssl/bio.py
@@ -0,0 +1,170 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/bio.h>
+"""
+
+TYPES = """
+typedef struct bio_st BIO;
+typedef void bio_info_cb(BIO *, int, const char *, int, long, long);
+struct bio_method_st {
+    int type;
+    const char *name;
+    int (*bwrite)(BIO *, const char *, int);
+    int (*bread)(BIO *, char *, int);
+    int (*bputs)(BIO *, const char *);
+    int (*bgets)(BIO *, char*, int);
+    long (*ctrl)(BIO *, int, long, void *);
+    int (*create)(BIO *);
+    int (*destroy)(BIO *);
+    long (*callback_ctrl)(BIO *, int, bio_info_cb *);
+    ...;
+};
+typedef struct bio_method_st BIO_METHOD;
+struct bio_st {
+    BIO_METHOD *method;
+    long (*callback)(struct bio_st*, int, const char*, int, long, long);
+    char *cb_arg;
+    int init;
+    int shutdown;
+    int flags;
+    int retry_reason;
+    int num;
+    void *ptr;
+    struct bio_st *next_bio;
+    struct bio_st *prev_bio;
+    int references;
+    unsigned long num_read;
+    unsigned long num_write;
+    ...;
+};
+typedef ... BUF_MEM;
+"""
+
+FUNCTIONS = """
+BIO* BIO_new(BIO_METHOD *);
+int BIO_set(BIO *, BIO_METHOD *);
+int BIO_free(BIO *);
+void BIO_vfree(BIO *);
+void BIO_free_all(BIO *);
+BIO *BIO_push(BIO *, BIO *);
+BIO *BIO_pop(BIO *);
+BIO *BIO_next(BIO *);
+BIO *BIO_find_type(BIO *, int);
+int BIO_method_type(const BIO *);
+BIO_METHOD *BIO_s_mem();
+BIO *BIO_new_mem_buf(void *, int);
+BIO_METHOD *BIO_s_file();
+BIO *BIO_new_file(const char *, const char *);
+BIO *BIO_new_fp(FILE *, int);
+BIO_METHOD *BIO_s_fd();
+BIO *BIO_new_fd(int, int);
+BIO_METHOD *BIO_s_socket();
+BIO *BIO_new_socket(int, int);
+BIO_METHOD *BIO_s_null();
+long BIO_ctrl(BIO *, int, long, void *);
+long BIO_callback_ctrl(
+    BIO *,
+    int,
+    void (*)(struct bio_st *, int, const char *, int, long, long)
+);
+char* BIO_ptr_ctrl(BIO *bp, int cmd, long larg);
+long BIO_int_ctrl(BIO *bp, int cmd, long larg, int iarg);
+size_t BIO_ctrl_pending(BIO *b);
+size_t BIO_ctrl_wpending(BIO *b);
+int BIO_read(BIO *, void *, int);
+int BIO_gets(BIO *, char *, int);
+int BIO_write(BIO *, const void *, int);
+int BIO_puts(BIO *, const char *);
+BIO_METHOD *BIO_f_null();
+BIO_METHOD *BIO_f_buffer();
+"""
+
+MACROS = """
+long BIO_set_fd(BIO *, long, int);
+long BIO_get_fd(BIO *, char *);
+long BIO_set_mem_eof_return(BIO *, int);
+long BIO_get_mem_data(BIO *, char **);
+long BIO_set_mem_buf(BIO *, BUF_MEM *, int);
+long BIO_get_mem_ptr(BIO *, BUF_MEM **);
+long BIO_set_fp(BIO *, FILE *, int);
+long BIO_get_fp(BIO *, FILE **);
+int BIO_read_filename(BIO *, char *);
+int BIO_write_filename(BIO *, char *);
+int BIO_append_filename(BIO *, char *);
+int BIO_rw_filename(BIO *, char *);
+int BIO_should_read(BIO *);
+int BIO_should_write(BIO *);
+int BIO_should_io_special(BIO *);
+int BIO_retry_type(BIO *);
+int BIO_should_retry(BIO *);
+int BIO_reset(BIO *);
+int BIO_seek(BIO *, int);
+int BIO_tell(BIO *);
+int BIO_flush(BIO *);
+int BIO_eof(BIO *);
+int BIO_set_close(BIO *,long);
+int BIO_get_close(BIO *);
+int BIO_pending(BIO *);
+int BIO_wpending(BIO *);
+int BIO_get_info_callback(BIO *, bio_info_cb **);
+int BIO_set_info_callback(BIO *, bio_info_cb *);
+long BIO_get_buffer_num_lines(BIO *);
+long BIO_set_read_buffer_size(BIO *, long);
+long BIO_set_write_buffer_size(BIO *, long);
+long BIO_set_buffer_size(BIO *, long);
+long BIO_set_buffer_read_data(BIO *, void *, long);
+#define BIO_TYPE_MEM ...
+#define BIO_TYPE_FILE ...
+#define BIO_TYPE_FD ...
+#define BIO_TYPE_SOCKET ...
+#define BIO_TYPE_CONNECT ...
+#define BIO_TYPE_ACCEPT ...
+#define BIO_TYPE_NULL ...
+#define BIO_CLOSE ...
+#define BIO_NOCLOSE ...
+#define BIO_TYPE_SOURCE_SINK ...
+#define BIO_CTRL_RESET ...
+#define BIO_CTRL_EOF ...
+#define BIO_CTRL_SET ...
+#define BIO_CTRL_SET_CLOSE ...
+#define BIO_CTRL_FLUSH ...
+#define BIO_CTRL_DUP ...
+#define BIO_CTRL_GET_CLOSE ...
+#define BIO_CTRL_INFO ...
+#define BIO_CTRL_GET ...
+#define BIO_CTRL_PENDING ...
+#define BIO_CTRL_WPENDING ...
+#define BIO_C_FILE_SEEK ...
+#define BIO_C_FILE_TELL ...
+#define BIO_TYPE_NONE ...
+#define BIO_TYPE_PROXY_CLIENT ...
+#define BIO_TYPE_PROXY_SERVER ...
+#define BIO_TYPE_NBIO_TEST ...
+#define BIO_TYPE_BER ...
+#define BIO_TYPE_BIO ...
+#define BIO_TYPE_DESCRIPTOR ...
+#define BIO_FLAGS_READ ...
+#define BIO_FLAGS_WRITE ...
+#define BIO_FLAGS_IO_SPECIAL ...
+#define BIO_FLAGS_RWS ...
+#define BIO_FLAGS_SHOULD_RETRY ...
+#define BIO_TYPE_NULL_FILTER ...
+#define BIO_TYPE_SSL ...
+#define BIO_TYPE_MD ...
+#define BIO_TYPE_BUFFER ...
+#define BIO_TYPE_CIPHER ...
+#define BIO_TYPE_BASE64 ...
+#define BIO_TYPE_FILTER ...
+"""
diff --git a/cryptography/bindings/openssl/conf.py b/cryptography/bindings/openssl/conf.py
new file mode 100644
index 0000000..85c7a21
--- /dev/null
+++ b/cryptography/bindings/openssl/conf.py
@@ -0,0 +1,26 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/conf.h>
+"""
+
+TYPES = """
+typedef ... CONF;
+"""
+
+FUNCTIONS = """
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/crypto.py b/cryptography/bindings/openssl/crypto.py
new file mode 100644
index 0000000..501fb5a
--- /dev/null
+++ b/cryptography/bindings/openssl/crypto.py
@@ -0,0 +1,37 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/crypto.h>
+"""
+
+TYPES = """
+"""
+
+FUNCTIONS = """
+void CRYPTO_free(void *);
+int CRYPTO_mem_ctrl(int);
+int CRYPTO_is_mem_check_on();
+void CRYPTO_mem_leaks(struct bio_st *);
+void CRYPTO_cleanup_all_ex_data();
+"""
+
+MACROS = """
+void CRYPTO_add(int *, int, int);
+void CRYPTO_malloc_init();
+void CRYPTO_malloc_debug_init();
+#define CRYPTO_MEM_CHECK_ON ...
+#define CRYPTO_MEM_CHECK_OFF ...
+#define CRYPTO_MEM_CHECK_ENABLE ...
+#define CRYPTO_MEM_CHECK_DISABLE ...
+"""
diff --git a/cryptography/bindings/openssl/dh.py b/cryptography/bindings/openssl/dh.py
new file mode 100644
index 0000000..ac13005
--- /dev/null
+++ b/cryptography/bindings/openssl/dh.py
@@ -0,0 +1,28 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/dh.h>
+"""
+
+TYPES = """
+typedef ... DH;
+"""
+
+FUNCTIONS = """
+DH *DH_new();
+void DH_free(DH *);
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/dsa.py b/cryptography/bindings/openssl/dsa.py
new file mode 100644
index 0000000..2fa67b8
--- /dev/null
+++ b/cryptography/bindings/openssl/dsa.py
@@ -0,0 +1,30 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/dsa.h>
+"""
+
+TYPES = """
+typedef ... DSA;
+"""
+
+FUNCTIONS = """
+DSA *DSA_generate_parameters(int, unsigned char *, int, int *, unsigned long *,
+                             void (*)(int, int, void *), void *);
+int DSA_generate_key(DSA *);
+void DSA_free(DSA *);
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/engine.py b/cryptography/bindings/openssl/engine.py
new file mode 100644
index 0000000..b3ec312
--- /dev/null
+++ b/cryptography/bindings/openssl/engine.py
@@ -0,0 +1,52 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/engine.h>
+"""
+
+TYPES = """
+typedef ... ENGINE;
+"""
+
+FUNCTIONS = """
+ENGINE *ENGINE_get_first();
+ENGINE *ENGINE_get_last();
+ENGINE *ENGINE_get_next(ENGINE *);
+ENGINE *ENGINE_get_prev(ENGINE *);
+int ENGINE_add(ENGINE *);
+int ENGINE_remove(ENGINE *);
+ENGINE *ENGINE_by_id(const char *);
+int ENGINE_init(ENGINE *);
+int ENGINE_finish(ENGINE *);
+int ENGINE_free(ENGINE *);
+void ENGINE_cleanup();
+void ENGINE_load_dynamic();
+void ENGINE_load_builtin_engines();
+int ENGINE_ctrl_cmd_string(ENGINE *, const char *, const char *, int);
+int ENGINE_set_default(ENGINE *, unsigned int);
+int ENGINE_register_complete(ENGINE *);
+"""
+
+MACROS = """
+#define ENGINE_METHOD_RSA ...
+#define ENGINE_METHOD_DSA ...
+#define ENGINE_METHOD_RAND ...
+#define ENGINE_METHOD_ECDH ...
+#define ENGINE_METHOD_ECDSA ...
+#define ENGINE_METHOD_CIPHERS ...
+#define ENGINE_METHOD_DIGESTS ...
+#define ENGINE_METHOD_STORE ...
+#define ENGINE_METHOD_ALL ...
+#define ENGINE_METHOD_NONE ...
+"""
diff --git a/cryptography/bindings/openssl/err.py b/cryptography/bindings/openssl/err.py
new file mode 100644
index 0000000..76c34a0
--- /dev/null
+++ b/cryptography/bindings/openssl/err.py
@@ -0,0 +1,54 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/err.h>
+"""
+
+TYPES = """
+struct ERR_string_data_st {
+    unsigned long error;
+    const char *string;
+};
+typedef struct ERR_string_data_st ERR_STRING_DATA;
+"""
+
+FUNCTIONS = """
+void ERR_load_crypto_strings();
+void ERR_free_strings();
+char* ERR_error_string(unsigned long, char *);
+void ERR_error_string_n(unsigned long, char *, size_t);
+const char* ERR_lib_error_string(unsigned long);
+const char* ERR_func_error_string(unsigned long);
+const char* ERR_reason_error_string(unsigned long);
+void ERR_print_errors(BIO *);
+void ERR_print_errors_fp(FILE *);
+unsigned long ERR_get_error();
+unsigned long ERR_peek_error();
+unsigned long ERR_peek_last_error();
+unsigned long ERR_get_error_line(const char **, int *);
+unsigned long ERR_peek_error_line(const char **, int *);
+unsigned long ERR_peek_last_error_line(const char **, int *);
+unsigned long ERR_get_error_line_data(const char **, int *,
+                                      const char **, int *);
+unsigned long ERR_peek_error_line_data(const char **,
+                                       int *, const char **, int *);
+unsigned long ERR_peek_last_error_line_data(const char **,
+                                            int *, const char **, int *);
+void ERR_put_error(int, int, int, const char *, int);
+void ERR_add_error_data(int, ...);
+int ERR_get_next_error_library();
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/evp.py b/cryptography/bindings/openssl/evp.py
index 0bc5cff..6336437 100644
--- a/cryptography/bindings/openssl/evp.py
+++ b/cryptography/bindings/openssl/evp.py
@@ -20,20 +20,22 @@
     ...;
 } EVP_CIPHER_CTX;
 typedef ... EVP_CIPHER;
-typedef ... ENGINE;
 """
 
 FUNCTIONS = """
 void OpenSSL_add_all_algorithms();
 const EVP_CIPHER *EVP_get_cipherbyname(const char *);
 int EVP_EncryptInit_ex(EVP_CIPHER_CTX *, const EVP_CIPHER *, ENGINE *,
-                       unsigned char *, unsigned char *);
+                       const unsigned char *, const unsigned char *);
 int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *, int);
 int EVP_EncryptUpdate(EVP_CIPHER_CTX *, unsigned char *, int *,
-                      unsigned char *, int);
+                      const unsigned char *, int);
 int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *, unsigned char *, int *);
 int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *);
 const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *);
 int EVP_CIPHER_block_size(const EVP_CIPHER *);
 void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *);
 """
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/opensslv.py b/cryptography/bindings/openssl/opensslv.py
index 9b2db27..d1a1b3e 100644
--- a/cryptography/bindings/openssl/opensslv.py
+++ b/cryptography/bindings/openssl/opensslv.py
@@ -21,3 +21,6 @@
 
 FUNCTIONS = """
 """
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/rand.py b/cryptography/bindings/openssl/rand.py
new file mode 100644
index 0000000..e4f6be2
--- /dev/null
+++ b/cryptography/bindings/openssl/rand.py
@@ -0,0 +1,37 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/rand.h>
+"""
+
+TYPES = """
+"""
+
+FUNCTIONS = """
+void RAND_seed(const void *, int);
+void RAND_add(const void *, int, double);
+int RAND_status();
+int RAND_egd(const char *);
+int RAND_egd_bytes(const char *, int);
+int RAND_query_egd_bytes(const char *, unsigned char *, int);
+const char *RAND_file_name(char *, size_t);
+int RAND_load_file(const char *, long);
+int RAND_write_file(const char *);
+void RAND_cleanup();
+int RAND_bytes(unsigned char *, int);
+int RAND_pseudo_bytes(unsigned char *, int);
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/rsa.py b/cryptography/bindings/openssl/rsa.py
new file mode 100644
index 0000000..c8bf1cc
--- /dev/null
+++ b/cryptography/bindings/openssl/rsa.py
@@ -0,0 +1,31 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/rsa.h>
+"""
+
+TYPES = """
+typedef ... RSA;
+typedef ... BN_GENCB;
+"""
+
+FUNCTIONS = """
+RSA *RSA_new();
+void RSA_free(RSA *);
+int RSA_generate_key_ex(RSA *, int, BIGNUM *, BN_GENCB *);
+int RSA_check_key(const RSA *);
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/bindings/openssl/ssl.py b/cryptography/bindings/openssl/ssl.py
new file mode 100644
index 0000000..8aca86e
--- /dev/null
+++ b/cryptography/bindings/openssl/ssl.py
@@ -0,0 +1,26 @@
+# 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.
+
+INCLUDES = """
+#include <openssl/ssl.h>
+"""
+
+TYPES = """
+"""
+
+FUNCTIONS = """
+void SSL_load_error_strings();
+"""
+
+MACROS = """
+"""
diff --git a/cryptography/primitives/block/base.py b/cryptography/primitives/block/base.py
index b4137fd..50e9e9e 100644
--- a/cryptography/primitives/block/base.py
+++ b/cryptography/primitives/block/base.py
@@ -15,8 +15,7 @@
 
 from enum import Enum
 
-# TODO: which binding is used should be an option somewhere
-from cryptography.bindings.openssl import api
+from cryptography.bindings import _default_api
 
 
 class _Operation(Enum):
@@ -25,19 +24,18 @@
 
 
 class BlockCipher(object):
-    def __init__(self, cipher, mode):
+    def __init__(self, cipher, mode, api=None):
         super(BlockCipher, self).__init__()
+
+        if api is None:
+            api = _default_api
+
         self.cipher = cipher
         self.mode = mode
+        self._api = api
         self._ctx = api.create_block_cipher_context(cipher, mode)
         self._operation = None
 
-    @property
-    def name(self):
-        return "{0}-{1}-{2}".format(
-            self.cipher.name, self.cipher.key_size, self.mode.name,
-        )
-
     def encrypt(self, plaintext):
         if self._ctx is None:
             raise ValueError("BlockCipher was already finalized")
@@ -48,14 +46,14 @@
             raise ValueError("BlockCipher cannot encrypt when the operation is"
                              " set to %s" % self._operation.name)
 
-        return api.update_encrypt_context(self._ctx, plaintext)
+        return self._api.update_encrypt_context(self._ctx, plaintext)
 
     def finalize(self):
         if self._ctx is None:
             raise ValueError("BlockCipher was already finalized")
 
         if self._operation is _Operation.encrypt:
-            result = api.finalize_encrypt_context(self._ctx)
+            result = self._api.finalize_encrypt_context(self._ctx)
         else:
             raise ValueError("BlockCipher cannot finalize the unknown "
                              "operation %s" % self._operation.name)
diff --git a/cryptography/primitives/block/ciphers.py b/cryptography/primitives/block/ciphers.py
index cf54aa3..4ac150a 100644
--- a/cryptography/primitives/block/ciphers.py
+++ b/cryptography/primitives/block/ciphers.py
@@ -17,7 +17,7 @@
 class AES(object):
     name = "AES"
     block_size = 128
-    key_sizes = set([128, 192, 256])
+    key_sizes = frozenset([128, 192, 256])
 
     def __init__(self, key):
         super(AES, self).__init__()
@@ -32,3 +32,23 @@
     @property
     def key_size(self):
         return len(self.key) * 8
+
+
+class Camellia(object):
+    name = "camellia"
+    block_size = 128
+    key_sizes = frozenset([128, 192, 256])
+
+    def __init__(self, key):
+        super(Camellia, self).__init__()
+        self.key = key
+
+        # Verify that the key size matches the expected key size
+        if self.key_size not in self.key_sizes:
+            raise ValueError("Invalid key size ({0}) for {1}".format(
+                self.key_size, self.name
+            ))
+
+    @property
+    def key_size(self):
+        return len(self.key) * 8
diff --git a/cryptography/primitives/block/modes.py b/cryptography/primitives/block/modes.py
index 9cfbca6..4363180 100644
--- a/cryptography/primitives/block/modes.py
+++ b/cryptography/primitives/block/modes.py
@@ -16,6 +16,14 @@
 from cryptography.primitives import interfaces
 
 
+def register(iface):
+    def register_decorator(klass):
+        iface.register(klass)
+        return klass
+    return register_decorator
+
+
+@register(interfaces.ModeWithInitializationVector)
 class CBC(object):
     name = "CBC"
 
@@ -28,6 +36,7 @@
     name = "ECB"
 
 
+@register(interfaces.ModeWithInitializationVector)
 class OFB(object):
     name = "OFB"
 
@@ -36,6 +45,7 @@
         self.initialization_vector = initialization_vector
 
 
+@register(interfaces.ModeWithInitializationVector)
 class CFB(object):
     name = "CFB"
 
@@ -44,6 +54,10 @@
         self.initialization_vector = initialization_vector
 
 
-interfaces.ModeWithInitializationVector.register(CBC)
-interfaces.ModeWithInitializationVector.register(OFB)
-interfaces.ModeWithInitializationVector.register(CFB)
+@register(interfaces.ModeWithNonce)
+class CTR(object):
+    name = "CTR"
+
+    def __init__(self, nonce):
+        super(CTR, self).__init__()
+        self.nonce = nonce
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/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
index 0000000..01030e8
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1,5 @@
+flake8
+pretend
+pytest-cov
+sphinx
+tox
diff --git a/docs/bindings/openssl.rst b/docs/bindings/openssl.rst
index 79468cb..241cc4d 100644
--- a/docs/bindings/openssl.rst
+++ b/docs/bindings/openssl.rst
@@ -4,7 +4,7 @@
 .. warning::
 
     The OpenSSL API is not easy to use, small mistakes can lead to significant
-    security vulnerabilities. We strongly reccomend not using this directly,
+    security vulnerabilities. We strongly recommend not using this directly,
     and instead using one of the higher level APIs exposed by ``cryptography``.
 
 
diff --git a/docs/community.rst b/docs/community.rst
index 809ffd1..86ba505 100644
--- a/docs/community.rst
+++ b/docs/community.rst
@@ -10,6 +10,6 @@
 * IRC: ``#cryptography-dev`` on ``irc.freenode.net``
 
 .. _`Mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev
-.. _`Source code`: https://github.com/alex/cryptography
-.. _`Issue tracker`: https://github.com/alex/cryptography/issues
+.. _`Source code`: https://github.com/pyca/cryptography
+.. _`Issue tracker`: https://github.com/pyca/cryptography/issues
 .. _`Documentation`: https://cryptography.readthedocs.org/
diff --git a/docs/contributing.rst b/docs/contributing.rst
index b4c72ba..2d8fcee 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -73,8 +73,81 @@
 - No blank line at the end.
 - Use Sphinx parameter/attribute documentation `syntax`_.
 
+Development Environment
+-----------------------
 
-.. _`GitHub`: https://github.com/alex/cryptography
+Working on ``cryptography`` requires the installation of a small number of
+development dependencies. These are listed in ``dev-requirements.txt`` and they
+can be installed in a `virtualenv`_ using `pip`_. Once you've installed the
+dependencies, install ``cryptography`` in ``editable`` mode. For example:
+
+.. code-block:: console
+
+   $ # Create a virtualenv and activate it
+   $ pip install --requirement dev-requirements.txt
+   $ pip install --editable .
+
+You are now ready to run the tests and build the documentation.
+
+Running Tests
+-------------
+
+``cryptography`` unit tests are found in the ``tests/`` directory and are
+designed to be run using `pytest`_. `pytest`_ will discover the tests
+automatically, so all you have to do is:
+
+.. code-block:: console
+
+   $ py.test
+   ...
+   4294 passed in 15.24 seconds
+
+This runs the tests with the default Python interpreter.
+
+You can also verify that the tests pass on other supported Python interpreters.
+For this we use `tox`_, which will automatically create a `virtualenv`_ for
+each supported Python version and run the tests. For example:
+
+.. code-block:: console
+
+   $ tox
+   ...
+   ERROR:   py26: InterpreterNotFound: python2.6
+    py27: commands succeeded
+   ERROR:   pypy: InterpreterNotFound: pypy
+   ERROR:   py32: InterpreterNotFound: python3.2
+    py33: commands succeeded
+    docs: commands succeeded
+    pep8: commands succeeded
+
+You may not have all the required Python versions installed, in which case you
+will see one or more ``InterpreterNotFound`` errors.
+
+Building Documentation
+----------------------
+
+``cryptography`` documentation is stored in the ``docs/`` directory. It is
+written in `reStructured Text`_ and rendered using `Sphinx`_.
+
+Use `tox`_ to build the documentation. For example:
+
+.. code-block:: console
+
+   $ tox -e docs
+   ...
+   docs: commands succeeded
+   congratulations :)
+
+The HTML documentation index can now be found at ``docs/_build/html/index.html``
+
+
+.. _`GitHub`: https://github.com/pyca/cryptography
 .. _`our mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev
 .. _`PEP 8`: http://www.peps.io/8/
 .. _`syntax`: http://sphinx-doc.org/domains.html#info-field-lists
+.. _`pytest`: https://pypi.python.org/pypi/pytest
+.. _`tox`: https://pypi.python.org/pypi/tox
+.. _`virtualenv`: https://pypi.python.org/pypi/virtualenv
+.. _`pip`: https://pypi.python.org/pypi/pip
+.. _`sphinx`: https://pypi.python.org/pypi/sphinx
+.. _`reStructured Text`: http://docutils.sourceforge.net/rst.html
diff --git a/docs/primitives/symmetric-encryption.rst b/docs/primitives/symmetric-encryption.rst
index 46d7c07..7899e67 100644
--- a/docs/primitives/symmetric-encryption.rst
+++ b/docs/primitives/symmetric-encryption.rst
@@ -51,6 +51,15 @@
     :param bytes key: The secret key, either ``128``, ``192``, or ``256`` bits.
                       This must be kept secret.
 
+.. class:: cryptography.primitives.block.ciphers.Camellia(key)
+
+    Camellia is a block cipher approved for use by CRYPTREC and ISO/IEC.
+    It is considered to have comparable security and performance to AES, but
+    is not as widely studied or deployed.
+
+    :param bytes key: The secret key, either ``128``, ``192``, or ``256`` bits.
+                      This must be kept secret.
+
 
 Modes
 ~~~~~
@@ -68,6 +77,25 @@
                                         reuse an ``initialization_vector`` with
                                         a given ``key``.
 
+
+.. class:: cryptography.primitives.block.modes.CTR(nonce)
+
+    .. warning::
+
+        Counter mode is not recommended for use with block ciphers that have a
+        block size of less than 128-bits.
+
+    CTR (Counter) is a mode of operation for block ciphers. It is considered
+    cryptographically strong.
+
+    :param bytes nonce: Should be random bytes. It is critical to never reuse a
+                        ``nonce`` with a given key.  Any reuse of a nonce
+                        with the same key compromises the security of every
+                        message encrypted with that key. Must be the same
+                        number of bytes as the ``block_size`` of the cipher
+                        with a given key. The nonce does not need to be kept
+                        secret and may be included alongside the ciphertext.
+
 .. class:: cryptography.primitives.block.modes.OFB(initialization_vector)
 
     OFB (Output Feedback) is a mode of operation for block ciphers. It
diff --git a/pytest.ini b/pytest.ini
new file mode 100644
index 0000000..723735a
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+addopts = -r s
diff --git a/tests/bindings/test_openssl.py b/tests/bindings/test_openssl.py
index b23c4cc..e5b78d1 100644
--- a/tests/bindings/test_openssl.py
+++ b/tests/bindings/test_openssl.py
@@ -28,3 +28,6 @@
         for every OpenSSL.
         """
         assert api.openssl_version_text().startswith("OpenSSL")
+
+    def test_supports_cipher(self):
+        assert api.supports_cipher("not-a-real-cipher") is False
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..b526f2b
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,5 @@
+def pytest_generate_tests(metafunc):
+    from cryptography.bindings import _ALL_APIS
+
+    if "api" in metafunc.fixturenames:
+        metafunc.parametrize("api", _ALL_APIS)
diff --git a/tests/primitives/test_block.py b/tests/primitives/test_block.py
index 774409c..f4d3f46 100644
--- a/tests/primitives/test_block.py
+++ b/tests/primitives/test_block.py
@@ -23,17 +23,17 @@
 
 
 class TestBlockCipher(object):
-    def test_cipher_name(self):
-        cipher = BlockCipher(
+    def test_instantiate_without_api(self):
+        BlockCipher(
             ciphers.AES(binascii.unhexlify(b"0" * 32)),
             modes.CBC(binascii.unhexlify(b"0" * 32))
         )
-        assert cipher.name == "AES-128-CBC"
 
-    def test_use_after_finalize(self):
+    def test_use_after_finalize(self, api):
         cipher = BlockCipher(
             ciphers.AES(binascii.unhexlify(b"0" * 32)),
-            modes.CBC(binascii.unhexlify(b"0" * 32))
+            modes.CBC(binascii.unhexlify(b"0" * 32)),
+            api
         )
         cipher.encrypt(b"a" * 16)
         cipher.finalize()
@@ -42,20 +42,22 @@
         with pytest.raises(ValueError):
             cipher.finalize()
 
-    def test_encrypt_with_invalid_operation(self):
+    def test_encrypt_with_invalid_operation(self, api):
         cipher = BlockCipher(
             ciphers.AES(binascii.unhexlify(b"0" * 32)),
-            modes.CBC(binascii.unhexlify(b"0" * 32))
+            modes.CBC(binascii.unhexlify(b"0" * 32)),
+            api
         )
         cipher._operation = _Operation.decrypt
 
         with pytest.raises(ValueError):
             cipher.encrypt(b"b" * 16)
 
-    def test_finalize_with_invalid_operation(self):
+    def test_finalize_with_invalid_operation(self, api):
         cipher = BlockCipher(
             ciphers.AES(binascii.unhexlify(b"0" * 32)),
-            modes.CBC(binascii.unhexlify(b"0" * 32))
+            modes.CBC(binascii.unhexlify(b"0" * 32)),
+            api
         )
         cipher._operation = pretend.stub(name="wat")
 
diff --git a/tests/primitives/test_ciphers.py b/tests/primitives/test_ciphers.py
index 5ee9f22..27d3585 100644
--- a/tests/primitives/test_ciphers.py
+++ b/tests/primitives/test_ciphers.py
@@ -17,7 +17,7 @@
 
 import pytest
 
-from cryptography.primitives.block.ciphers import AES
+from cryptography.primitives.block.ciphers import AES, Camellia
 
 
 class TestAES(object):
@@ -33,3 +33,18 @@
     def test_invalid_key_size(self):
         with pytest.raises(ValueError):
             AES(binascii.unhexlify(b"0" * 12))
+
+
+class TestCamellia(object):
+    @pytest.mark.parametrize(("key", "keysize"), [
+        (b"0" * 32, 128),
+        (b"0" * 48, 192),
+        (b"0" * 64, 256),
+    ])
+    def test_key_size(self, key, keysize):
+        cipher = Camellia(binascii.unhexlify(key))
+        assert cipher.key_size == keysize
+
+    def test_invalid_key_size(self):
+        with pytest.raises(ValueError):
+            Camellia(binascii.unhexlify(b"0" * 12))
diff --git a/tests/primitives/test_cryptrec.py b/tests/primitives/test_cryptrec.py
new file mode 100644
index 0000000..edf9765
--- /dev/null
+++ b/tests/primitives/test_cryptrec.py
@@ -0,0 +1,42 @@
+# 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.
+
+"""
+Tests using the CRYPTREC (Camellia) Test Vectors
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import binascii
+import os
+
+from cryptography.primitives.block import ciphers, modes
+
+from .utils import generate_encrypt_test
+from ..utils import load_cryptrec_vectors_from_file
+
+
+class TestCamelliaECB(object):
+    test_NTT = generate_encrypt_test(
+        load_cryptrec_vectors_from_file,
+        os.path.join("Camellia", "NTT"),
+        [
+            "camellia-128-ecb.txt",
+            "camellia-192-ecb.txt",
+            "camellia-256-ecb.txt"
+        ],
+        lambda key: ciphers.Camellia(binascii.unhexlify((key))),
+        lambda key: modes.ECB(),
+        only_if=lambda api: api.supports_cipher("camellia-128-ecb"),
+        skip_message="Does not support Camellia ECB",
+    )
diff --git a/tests/primitives/test_nist.py b/tests/primitives/test_nist.py
index 1e5d239..d97b207 100644
--- a/tests/primitives/test_nist.py
+++ b/tests/primitives/test_nist.py
@@ -18,33 +18,18 @@
 from __future__ import absolute_import, division, print_function
 
 import binascii
-import itertools
 import os
 
-import pytest
+from cryptography.primitives.block import ciphers, modes
 
-from cryptography.primitives.block import BlockCipher, ciphers, modes
-
+from .utils import generate_encrypt_test
 from ..utils import load_nist_vectors_from_file
 
 
-def parameterize_encrypt_test(cipher, vector_type, params, fnames):
-    return pytest.mark.parametrize(params,
-        list(itertools.chain.from_iterable(
-            load_nist_vectors_from_file(
-                os.path.join(cipher, vector_type, fname),
-                "ENCRYPT",
-                params
-            )
-            for fname in fnames
-        ))
-    )
-
-
 class TestAES_CBC(object):
-    @parameterize_encrypt_test(
-        "AES", "KAT",
-        ("key", "iv", "plaintext", "ciphertext"),
+    test_KAT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("AES", "KAT"),
         [
             "CBCGFSbox128.rsp",
             "CBCGFSbox192.rsp",
@@ -58,40 +43,28 @@
             "CBCVarTxt128.rsp",
             "CBCVarTxt192.rsp",
             "CBCVarTxt256.rsp",
-        ]
+        ],
+        lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
+        lambda key, iv: modes.CBC(binascii.unhexlify(iv)),
     )
-    def test_KAT(self, key, iv, plaintext, ciphertext):
-        cipher = BlockCipher(
-            ciphers.AES(binascii.unhexlify(key)),
-            modes.CBC(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"),
+    test_MMT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("AES", "MMT"),
         [
             "CBCMMT128.rsp",
             "CBCMMT192.rsp",
             "CBCMMT256.rsp",
-        ]
+        ],
+        lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
+        lambda key, iv: modes.CBC(binascii.unhexlify(iv)),
     )
-    def test_MMT(self, key, iv, plaintext, ciphertext):
-        cipher = BlockCipher(
-            ciphers.AES(binascii.unhexlify(key)),
-            modes.CBC(binascii.unhexlify(iv)),
-        )
-        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"),
+    test_KAT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("AES", "KAT"),
         [
             "ECBGFSbox128.rsp",
             "ECBGFSbox192.rsp",
@@ -105,40 +78,28 @@
             "ECBVarTxt128.rsp",
             "ECBVarTxt192.rsp",
             "ECBVarTxt256.rsp",
-        ]
+        ],
+        lambda key: ciphers.AES(binascii.unhexlify(key)),
+        lambda key: modes.ECB(),
     )
-    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"),
+    test_MMT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("AES", "MMT"),
         [
             "ECBMMT128.rsp",
             "ECBMMT192.rsp",
             "ECBMMT256.rsp",
-        ]
+        ],
+        lambda key: ciphers.AES(binascii.unhexlify(key)),
+        lambda key: modes.ECB(),
     )
-    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
 
 
 class TestAES_OFB(object):
-    @parameterize_encrypt_test(
-        "AES", "KAT",
-        ("key", "iv", "plaintext", "ciphertext"),
+    test_KAT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("AES", "KAT"),
         [
             "OFBGFSbox128.rsp",
             "OFBGFSbox192.rsp",
@@ -152,40 +113,28 @@
             "OFBVarTxt128.rsp",
             "OFBVarTxt192.rsp",
             "OFBVarTxt256.rsp",
-        ]
+        ],
+        lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
+        lambda key, iv: modes.OFB(binascii.unhexlify(iv)),
     )
-    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"),
+    test_MMT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("AES", "MMT"),
         [
             "OFBMMT128.rsp",
             "OFBMMT192.rsp",
             "OFBMMT256.rsp",
-        ]
+        ],
+        lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
+        lambda key, iv: modes.OFB(binascii.unhexlify(iv)),
     )
-    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
 
 
 class TestAES_CFB(object):
-    @parameterize_encrypt_test(
-        "AES", "KAT",
-        ("key", "iv", "plaintext", "ciphertext"),
+    test_KAT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("AES", "KAT"),
         [
             "CFB128GFSbox128.rsp",
             "CFB128GFSbox192.rsp",
@@ -199,31 +148,19 @@
             "CFB128VarTxt128.rsp",
             "CFB128VarTxt192.rsp",
             "CFB128VarTxt256.rsp",
-        ]
+        ],
+        lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
+        lambda key, iv: modes.CFB(binascii.unhexlify(iv)),
     )
-    def test_KAT(self, key, iv, plaintext, ciphertext):
-        cipher = BlockCipher(
-            ciphers.AES(binascii.unhexlify(key)),
-            modes.CFB(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"),
+    test_MMT = generate_encrypt_test(
+        lambda path: load_nist_vectors_from_file(path, "ENCRYPT"),
+        os.path.join("AES", "MMT"),
         [
             "CFB128MMT128.rsp",
             "CFB128MMT192.rsp",
             "CFB128MMT256.rsp",
-        ]
+        ],
+        lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
+        lambda key, iv: modes.CFB(binascii.unhexlify(iv)),
     )
-    def test_MMT(self, key, iv, plaintext, ciphertext):
-        cipher = BlockCipher(
-            ciphers.AES(binascii.unhexlify(key)),
-            modes.CFB(binascii.unhexlify(iv))
-        )
-        actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
-        actual_ciphertext += cipher.finalize()
-        assert binascii.hexlify(actual_ciphertext) == ciphertext
diff --git a/tests/primitives/test_openssl_vectors.py b/tests/primitives/test_openssl_vectors.py
new file mode 100644
index 0000000..5b2be78
--- /dev/null
+++ b/tests/primitives/test_openssl_vectors.py
@@ -0,0 +1,73 @@
+# 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.
+
+"""
+Test using the OpenSSL Test Vectors
+"""
+
+from __future__ import absolute_import, division, print_function
+
+import binascii
+
+from cryptography.primitives.block import ciphers, modes
+
+from .utils import generate_encrypt_test
+from ..utils import load_openssl_vectors_from_file
+
+
+class TestCamelliaCBC(object):
+    test_OpenSSL = generate_encrypt_test(
+        load_openssl_vectors_from_file,
+        "Camellia",
+        ["camellia-cbc.txt"],
+        lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
+        lambda key, iv: modes.CBC(binascii.unhexlify(iv)),
+        only_if=lambda api: api.supports_cipher("camellia-128-cbc"),
+        skip_message="Does not support Camellia CBC",
+    )
+
+
+class TestCamelliaOFB(object):
+    test_OpenSSL = generate_encrypt_test(
+        load_openssl_vectors_from_file,
+        "Camellia",
+        ["camellia-ofb.txt"],
+        lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
+        lambda key, iv: modes.OFB(binascii.unhexlify(iv)),
+        only_if=lambda api: api.supports_cipher("camellia-128-ofb"),
+        skip_message="Does not support Camellia OFB",
+    )
+
+
+class TestCamelliaCFB(object):
+    test_OpenSSL = generate_encrypt_test(
+        load_openssl_vectors_from_file,
+        "Camellia",
+        ["camellia-cfb.txt"],
+        lambda key, iv: ciphers.Camellia(binascii.unhexlify(key)),
+        lambda key, iv: modes.CFB(binascii.unhexlify(iv)),
+        only_if=lambda api: api.supports_cipher("camellia-128-cfb"),
+        skip_message="Does not support Camellia CFB",
+    )
+
+
+class TestAESCTR(object):
+    test_OpenSSL = generate_encrypt_test(
+        load_openssl_vectors_from_file,
+        "AES",
+        ["aes-128-ctr.txt", "aes-192-ctr.txt", "aes-256-ctr.txt"],
+        lambda key, iv: ciphers.AES(binascii.unhexlify(key)),
+        lambda key, iv: modes.CTR(binascii.unhexlify(iv)),
+        only_if=lambda api: api.supports_cipher("aes-128-ctr"),
+        skip_message="Does not support AES CTR",
+    )
diff --git a/tests/primitives/test_utils.py b/tests/primitives/test_utils.py
new file mode 100644
index 0000000..4666ece
--- /dev/null
+++ b/tests/primitives/test_utils.py
@@ -0,0 +1,14 @@
+import pytest
+
+from .utils import encrypt_test
+
+
+class TestEncryptTest(object):
+    def test_skips_if_only_if_returns_false(self):
+        with pytest.raises(pytest.skip.Exception) as exc_info:
+            encrypt_test(
+                None, None, None, None,
+                only_if=lambda api: False,
+                skip_message="message!"
+            )
+        assert exc_info.value.args[0] == "message!"
diff --git a/tests/primitives/utils.py b/tests/primitives/utils.py
new file mode 100644
index 0000000..3cf08c2
--- /dev/null
+++ b/tests/primitives/utils.py
@@ -0,0 +1,42 @@
+import binascii
+import os
+
+import pytest
+
+from cryptography.bindings import _ALL_APIS
+from cryptography.primitives.block import BlockCipher
+
+
+def generate_encrypt_test(param_loader, path, file_names, cipher_factory,
+                          mode_factory, only_if=lambda api: True,
+                          skip_message=None):
+    def test_encryption(self):
+        for api in _ALL_APIS:
+            for file_name in file_names:
+                for params in param_loader(os.path.join(path, file_name)):
+                    yield (
+                        encrypt_test,
+                        api,
+                        cipher_factory,
+                        mode_factory,
+                        params,
+                        only_if,
+                        skip_message
+                    )
+    return test_encryption
+
+
+def encrypt_test(api, cipher_factory, mode_factory, params, only_if,
+                 skip_message):
+    if not only_if(api):
+        pytest.skip(skip_message)
+    plaintext = params.pop("plaintext")
+    ciphertext = params.pop("ciphertext")
+    cipher = BlockCipher(
+        cipher_factory(**params),
+        mode_factory(**params),
+        api
+    )
+    actual_ciphertext = cipher.encrypt(binascii.unhexlify(plaintext))
+    actual_ciphertext += cipher.finalize()
+    assert actual_ciphertext == binascii.unhexlify(ciphertext)
diff --git a/tests/primitives/vectors/OpenSSL/AES/aes-128-ctr.txt b/tests/primitives/vectors/OpenSSL/AES/aes-128-ctr.txt
new file mode 100644
index 0000000..f4ce15e
--- /dev/null
+++ b/tests/primitives/vectors/OpenSSL/AES/aes-128-ctr.txt
@@ -0,0 +1,4 @@
+# AES Counter test vectors from RFC3686
+aes-128-ctr:AE6852F8121067CC4BF7A5765577F39E:00000030000000000000000000000001:53696E676C6520626C6F636B206D7367:E4095D4FB7A7B3792D6175A3261311B8:1
+aes-128-ctr:7E24067817FAE0D743D6CE1F32539163:006CB6DBC0543B59DA48D90B00000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:5104A106168A72D9790D41EE8EDAD388EB2E1EFC46DA57C8FCE630DF9141BE28:1
+aes-128-ctr:7691BE035E5020A8AC6E618529F9A0DC:00E0017B27777F3F4A1786F000000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223:C1CF48A89F2FFDD9CF4652E9EFDB72D74540A42BDE6D7836D59A5CEAAEF3105325B2072F:1
diff --git a/tests/primitives/vectors/OpenSSL/AES/aes-192-ctr.txt b/tests/primitives/vectors/OpenSSL/AES/aes-192-ctr.txt
new file mode 100644
index 0000000..9e166fc
--- /dev/null
+++ b/tests/primitives/vectors/OpenSSL/AES/aes-192-ctr.txt
@@ -0,0 +1,4 @@
+# AES Counter test vectors from RFC3686
+aes-192-ctr:16AF5B145FC9F579C175F93E3BFB0EED863D06CCFDB78515:0000004836733C147D6D93CB00000001:53696E676C6520626C6F636B206D7367:4B55384FE259C9C84E7935A003CBE928:1
+aes-192-ctr:7C5CB2401B3DC33C19E7340819E0F69C678C3DB8E6F6A91A:0096B03B020C6EADC2CB500D00000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:453243FC609B23327EDFAAFA7131CD9F8490701C5AD4A79CFC1FE0FF42F4FB00:1
+aes-192-ctr:02BF391EE8ECB159B959617B0965279BF59B60A786D3E0FE:0007BDFD5CBD60278DCC091200000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223:96893FC55E5C722F540B7DD1DDF7E758D288BC95C69165884536C811662F2188ABEE0935:1
diff --git a/tests/primitives/vectors/OpenSSL/AES/aes-256-ctr.txt b/tests/primitives/vectors/OpenSSL/AES/aes-256-ctr.txt
new file mode 100644
index 0000000..d499875
--- /dev/null
+++ b/tests/primitives/vectors/OpenSSL/AES/aes-256-ctr.txt
@@ -0,0 +1,5 @@
+# AES Counter test vectors from RFC3686
+aes-256-ctr:776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104:00000060DB5672C97AA8F0B200000001:53696E676C6520626C6F636B206D7367:145AD01DBF824EC7560863DC71E3E0C0:1
+aes-256-ctr:F6D66D6BD52D59BB0796365879EFF886C66DD51A5B6A99744B50590C87A23884:00FAAC24C1585EF15A43D87500000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F:F05E231B3894612C49EE000B804EB2A9B8306B508F839D6A5530831D9344AF1C:1
+aes-256-ctr:FF7A617CE69148E4F1726E2F43581DE2AA62D9F805532EDFF1EED687FB54153D:001CC5B751A51D70A1C1114800000001:000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223:EB6C52821D0BBBF7CE7594462ACA4FAAB407DF866569FD07F48CC0B583D6071F1EC0E6B8:1
+
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 73394a5..28e7407 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -56,21 +56,19 @@
     PLAINTEXT = 9798c4640bad75c7c3227db910174e72
     """).splitlines()
 
-    assert load_nist_vectors(vector_data, "ENCRYPT",
-        ["key", "iv", "plaintext", "ciphertext"],
-    ) == [
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"f34481ec3cc627bacd5dc3fb08f273e6",
-            b"0336763e966d92595a567cc9ce537f5e",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"9798c4640bad75c7c3227db910174e72",
-            b"a9a1631bf4996954ebc093957b234589",
-        ),
+    assert load_nist_vectors(vector_data, "ENCRYPT") == [
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"f34481ec3cc627bacd5dc3fb08f273e6",
+            "ciphertext": b"0336763e966d92595a567cc9ce537f5e",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"9798c4640bad75c7c3227db910174e72",
+            "ciphertext": b"a9a1631bf4996954ebc093957b234589",
+        },
     ]
 
 
@@ -112,72 +110,69 @@
     PLAINTEXT = 9798c4640bad75c7c3227db910174e72
     """).splitlines()
 
-    assert load_nist_vectors(vector_data, "DECRYPT",
-        ["key", "iv", "ciphertext", "plaintext"],
-    ) == [
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"0336763e966d92595a567cc9ce537f5e",
-            b"f34481ec3cc627bacd5dc3fb08f273e6",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"a9a1631bf4996954ebc093957b234589",
-            b"9798c4640bad75c7c3227db910174e72",
-        ),
+    assert load_nist_vectors(vector_data, "DECRYPT") == [
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"f34481ec3cc627bacd5dc3fb08f273e6",
+            "ciphertext": b"0336763e966d92595a567cc9ce537f5e",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"9798c4640bad75c7c3227db910174e72",
+            "ciphertext": b"a9a1631bf4996954ebc093957b234589",
+        },
     ]
 
 
 def test_load_nist_vectors_from_file_encrypt():
     assert load_nist_vectors_from_file(
         "AES/KAT/CBCGFSbox128.rsp",
-        "ENCRYPT",
-        ["key", "iv", "plaintext", "ciphertext"],
+        "ENCRYPT"
     ) == [
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"f34481ec3cc627bacd5dc3fb08f273e6",
-            b"0336763e966d92595a567cc9ce537f5e",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"9798c4640bad75c7c3227db910174e72",
-            b"a9a1631bf4996954ebc093957b234589",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"96ab5c2ff612d9dfaae8c31f30c42168",
-            b"ff4f8391a6a40ca5b25d23bedd44a597",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"6a118a874519e64e9963798a503f1d35",
-            b"dc43be40be0e53712f7e2bf5ca707209",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"cb9fceec81286ca3e989bd979b0cb284",
-            b"92beedab1895a94faa69b632e5cc47ce",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"b26aeb1874e47ca8358ff22378f09144",
-            b"459264f4798f6a78bacb89c15ed3d601",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"58c8e00b2631686d54eab84b91f0aca1",
-            b"08a4e2efec8a8e3312ca7460b9040bbf",
-        ),
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"f34481ec3cc627bacd5dc3fb08f273e6",
+            "ciphertext": b"0336763e966d92595a567cc9ce537f5e",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"9798c4640bad75c7c3227db910174e72",
+            "ciphertext": b"a9a1631bf4996954ebc093957b234589",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"96ab5c2ff612d9dfaae8c31f30c42168",
+            "ciphertext": b"ff4f8391a6a40ca5b25d23bedd44a597",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"6a118a874519e64e9963798a503f1d35",
+            "ciphertext": b"dc43be40be0e53712f7e2bf5ca707209",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"cb9fceec81286ca3e989bd979b0cb284",
+            "ciphertext": b"92beedab1895a94faa69b632e5cc47ce",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"b26aeb1874e47ca8358ff22378f09144",
+            "ciphertext": b"459264f4798f6a78bacb89c15ed3d601",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"58c8e00b2631686d54eab84b91f0aca1",
+            "ciphertext": b"08a4e2efec8a8e3312ca7460b9040bbf",
+        },
     ]
 
 
@@ -185,50 +180,49 @@
     assert load_nist_vectors_from_file(
         "AES/KAT/CBCGFSbox128.rsp",
         "DECRYPT",
-        ["key", "iv", "ciphertext", "plaintext"],
     ) == [
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"0336763e966d92595a567cc9ce537f5e",
-            b"f34481ec3cc627bacd5dc3fb08f273e6",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"a9a1631bf4996954ebc093957b234589",
-            b"9798c4640bad75c7c3227db910174e72",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"ff4f8391a6a40ca5b25d23bedd44a597",
-            b"96ab5c2ff612d9dfaae8c31f30c42168",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"dc43be40be0e53712f7e2bf5ca707209",
-            b"6a118a874519e64e9963798a503f1d35",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"92beedab1895a94faa69b632e5cc47ce",
-            b"cb9fceec81286ca3e989bd979b0cb284",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"459264f4798f6a78bacb89c15ed3d601",
-            b"b26aeb1874e47ca8358ff22378f09144"
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"00000000000000000000000000000000",
-            b"08a4e2efec8a8e3312ca7460b9040bbf",
-            b"58c8e00b2631686d54eab84b91f0aca1"
-        ),
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"f34481ec3cc627bacd5dc3fb08f273e6",
+            "ciphertext": b"0336763e966d92595a567cc9ce537f5e",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"9798c4640bad75c7c3227db910174e72",
+            "ciphertext": b"a9a1631bf4996954ebc093957b234589",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"96ab5c2ff612d9dfaae8c31f30c42168",
+            "ciphertext": b"ff4f8391a6a40ca5b25d23bedd44a597",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"6a118a874519e64e9963798a503f1d35",
+            "ciphertext": b"dc43be40be0e53712f7e2bf5ca707209",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"cb9fceec81286ca3e989bd979b0cb284",
+            "ciphertext": b"92beedab1895a94faa69b632e5cc47ce",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"b26aeb1874e47ca8358ff22378f09144",
+            "ciphertext": b"459264f4798f6a78bacb89c15ed3d601",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "iv": b"00000000000000000000000000000000",
+            "plaintext": b"58c8e00b2631686d54eab84b91f0aca1",
+            "ciphertext": b"08a4e2efec8a8e3312ca7460b9040bbf",
+        },
     ]
 
 
@@ -254,21 +248,21 @@
     """).splitlines()
 
     assert load_cryptrec_vectors(vector_data) == [
-        (
-            b"00000000000000000000000000000000",
-            b"80000000000000000000000000000000",
-            b"07923A39EB0A817D1C4D87BDB82D1F1C",
-        ),
-        (
-            b"00000000000000000000000000000000",
-            b"40000000000000000000000000000000",
-            b"48CD6419809672D2349260D89A08D3D3",
-        ),
-        (
-            b"10000000000000000000000000000000",
-            b"80000000000000000000000000000000",
-            b"07923A39EB0A817D1C4D87BDB82D1F1C",
-        ),
+        {
+            "key": b"00000000000000000000000000000000",
+            "plaintext": b"80000000000000000000000000000000",
+            "ciphertext": b"07923A39EB0A817D1C4D87BDB82D1F1C",
+        },
+        {
+            "key": b"00000000000000000000000000000000",
+            "plaintext": b"40000000000000000000000000000000",
+            "ciphertext": b"48CD6419809672D2349260D89A08D3D3",
+        },
+        {
+            "key": b"10000000000000000000000000000000",
+            "plaintext": b"80000000000000000000000000000000",
+            "ciphertext": b"07923A39EB0A817D1C4D87BDB82D1F1C",
+        },
     ]
 
 
@@ -277,11 +271,11 @@
         "Camellia/NTT/camellia-128-ecb.txt"
     )
     assert test_set[0] == (
-        (
-            b"00000000000000000000000000000000",
-            b"80000000000000000000000000000000",
-            b"07923A39EB0A817D1C4D87BDB82D1F1C",
-        )
+        {
+            "key": b"00000000000000000000000000000000",
+            "plaintext": b"80000000000000000000000000000000",
+            "ciphertext": b"07923A39EB0A817D1C4D87BDB82D1F1C",
+        }
     )
     assert len(test_set) == 1280
 
@@ -310,30 +304,30 @@
     ).splitlines()
 
     assert load_openssl_vectors(vector_data) == [
-        (
-            b"2B7E151628AED2A6ABF7158809CF4F3C",
-            b"000102030405060708090A0B0C0D0E0F",
-            b"6BC1BEE22E409F96E93D7E117393172A",
-            b"14F7646187817EB586599146B82BD719",
-        ),
-        (
-            b"2B7E151628AED2A6ABF7158809CF4F3C",
-            b"14F7646187817EB586599146B82BD719",
-            b"AE2D8A571E03AC9C9EB76FAC45AF8E51",
-            b"A53D28BB82DF741103EA4F921A44880B",
-        ),
-        (
-            b"2B7E151628AED2A6ABF7158809CF4F3C",
-            b"000102030405060708090A0B0C0D0E0F",
-            b"6BC1BEE22E409F96E93D7E117393172A",
-            b"14F7646187817EB586599146B82BD719",
-        ),
-        (
-            b"2B7E151628AED2A6ABF7158809CF4F3C",
-            b"14F7646187817EB586599146B82BD719",
-            b"AE2D8A571E03AC9C9EB76FAC45AF8E51",
-            b"A53D28BB82DF741103EA4F921A44880B",
-        ),
+        {
+            "key": b"2B7E151628AED2A6ABF7158809CF4F3C",
+            "iv": b"000102030405060708090A0B0C0D0E0F",
+            "plaintext": b"6BC1BEE22E409F96E93D7E117393172A",
+            "ciphertext": b"14F7646187817EB586599146B82BD719",
+        },
+        {
+            "key": b"2B7E151628AED2A6ABF7158809CF4F3C",
+            "iv": b"14F7646187817EB586599146B82BD719",
+            "plaintext": b"AE2D8A571E03AC9C9EB76FAC45AF8E51",
+            "ciphertext": b"A53D28BB82DF741103EA4F921A44880B",
+        },
+        {
+            "key": b"2B7E151628AED2A6ABF7158809CF4F3C",
+            "iv": b"000102030405060708090A0B0C0D0E0F",
+            "plaintext": b"6BC1BEE22E409F96E93D7E117393172A",
+            "ciphertext": b"14F7646187817EB586599146B82BD719",
+        },
+        {
+            "key": b"2B7E151628AED2A6ABF7158809CF4F3C",
+            "iv": b"14F7646187817EB586599146B82BD719",
+            "plaintext": b"AE2D8A571E03AC9C9EB76FAC45AF8E51",
+            "ciphertext": b"A53D28BB82DF741103EA4F921A44880B",
+        },
     ]
 
 
@@ -341,28 +335,28 @@
     test_list = load_openssl_vectors_from_file("Camellia/camellia-ofb.txt")
     assert len(test_list) == 24
     assert test_list[:4] == [
-        (
-            b"2B7E151628AED2A6ABF7158809CF4F3C",
-            b"000102030405060708090A0B0C0D0E0F",
-            b"6BC1BEE22E409F96E93D7E117393172A",
-            b"14F7646187817EB586599146B82BD719",
-        ),
-        (
-            b"2B7E151628AED2A6ABF7158809CF4F3C",
-            b"50FE67CC996D32B6DA0937E99BAFEC60",
-            b"AE2D8A571E03AC9C9EB76FAC45AF8E51",
-            b"25623DB569CA51E01482649977E28D84",
-        ),
-        (
-            b"2B7E151628AED2A6ABF7158809CF4F3C",
-            b"D9A4DADA0892239F6B8B3D7680E15674",
-            b"30C81C46A35CE411E5FBC1191A0A52EF",
-            b"C776634A60729DC657D12B9FCA801E98",
-        ),
-        (
-            b"2B7E151628AED2A6ABF7158809CF4F3C",
-            b"A78819583F0308E7A6BF36B1386ABF23",
-            b"F69F2445DF4F9B17AD2B417BE66C3710",
-            b"D776379BE0E50825E681DA1A4C980E8E",
-        ),
+        {
+            "key": b"2B7E151628AED2A6ABF7158809CF4F3C",
+            "iv": b"000102030405060708090A0B0C0D0E0F",
+            "plaintext": b"6BC1BEE22E409F96E93D7E117393172A",
+            "ciphertext": b"14F7646187817EB586599146B82BD719",
+        },
+        {
+            "key": b"2B7E151628AED2A6ABF7158809CF4F3C",
+            "iv": b"50FE67CC996D32B6DA0937E99BAFEC60",
+            "plaintext": b"AE2D8A571E03AC9C9EB76FAC45AF8E51",
+            "ciphertext": b"25623DB569CA51E01482649977E28D84",
+        },
+        {
+            "key": b"2B7E151628AED2A6ABF7158809CF4F3C",
+            "iv": b"D9A4DADA0892239F6B8B3D7680E15674",
+            "plaintext": b"30C81C46A35CE411E5FBC1191A0A52EF",
+            "ciphertext": b"C776634A60729DC657D12B9FCA801E98",
+        },
+        {
+            "key": b"2B7E151628AED2A6ABF7158809CF4F3C",
+            "iv": b"A78819583F0308E7A6BF36B1386ABF23",
+            "plaintext": b"F69F2445DF4F9B17AD2B417BE66C3710",
+            "ciphertext": b"D776379BE0E50825E681DA1A4C980E8E",
+        },
     ]
diff --git a/tests/utils.py b/tests/utils.py
index d06c9e3..6b1cfd7 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -14,7 +14,7 @@
 import os.path
 
 
-def load_nist_vectors(vector_data, op, fields):
+def load_nist_vectors(vector_data, op):
     section, count, data = None, None, {}
 
     for line in vector_data:
@@ -44,21 +44,19 @@
         # For all other tokens we simply want the name, value stored in
         # the dictionary
         else:
-            data[section][count][name.lower()] = value
+            data[section][count][name.lower()] = value.encode("ascii")
 
-    # We want to test only for a particular operation
-    return [
-        tuple(vector[1][f].encode("ascii") for f in fields)
-        for vector in sorted(data[op].items(), key=lambda v: v[0])
-    ]
+    # We want to test only for a particular operation, we sort them for the
+    # benefit of the tests of this function.
+    return [v for k, v in sorted(data[op].items(), key=lambda kv: kv[0])]
 
 
-def load_nist_vectors_from_file(filename, op, fields):
+def load_nist_vectors_from_file(filename, op):
     base = os.path.join(
         os.path.dirname(__file__), "primitives", "vectors", "NIST",
     )
     with open(os.path.join(base, filename), "r") as vector_file:
-        return load_nist_vectors(vector_file, op, fields)
+        return load_nist_vectors(vector_file, op)
 
 
 def load_cryptrec_vectors_from_file(filename):
@@ -87,7 +85,11 @@
             ct = line.split(" : ")[1].replace(" ", "").encode("ascii")
             # after a C is found the K+P+C tuple is complete
             # there are many P+C pairs for each K
-            cryptrec_list.append((key, pt, ct))
+            cryptrec_list.append({
+                "key": key,
+                "plaintext": pt,
+                "ciphertext": ct
+            })
     return cryptrec_list
 
 
@@ -110,15 +112,10 @@
             continue
 
         vector = line.split(":")
-        params = (
-            # key
-            vector[1].encode("ascii"),
-            # iv
-            vector[2].encode("ascii"),
-            # plaintext
-            vector[3].encode("ascii"),
-            # ciphertext
-            vector[4].encode("ascii")
-        )
-        vectors.append(params)
+        vectors.append({
+            "key": vector[1].encode("ascii"),
+            "iv": vector[2].encode("ascii"),
+            "plaintext": vector[3].encode("ascii"),
+            "ciphertext": vector[4].encode("ascii"),
+        })
     return vectors
diff --git a/tox.ini b/tox.ini
index 4d17ebe..0a28af1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,9 +3,12 @@
 
 [testenv]
 deps =
-    pytest-cov
+    pytest
+    coverage
     pretend
-commands = py.test --cov=cryptography/ --cov=tests/
+commands =
+    coverage run --source=cryptography/,tests/ -m pytest
+    coverage report -m
 
 [testenv:docs]
 deps = sphinx