Merge pull request #128 from reaperhulk/hash-saga-whirlpool

Hash Saga Part 6 (Whirlpool support)
diff --git a/.travis.yml b/.travis.yml
index defcd77..1c35b39 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,11 @@
     - TOX_ENV=py32
     - TOX_ENV=py33
     - TOX_ENV=pypy
+    - TOX_ENV=py26 CC=clang
+    - TOX_ENV=py27 CC=clang
+    - TOX_ENV=py32 CC=clang
+    - TOX_ENV=py33 CC=clang
+    - TOX_ENV=pypy CC=clang
     - TOX_ENV=docs
     - TOX_ENV=pep8
 
diff --git a/cryptography/bindings/openssl/api.py b/cryptography/bindings/openssl/api.py
index cfd4fc4..3c2cf2e 100644
--- a/cryptography/bindings/openssl/api.py
+++ b/cryptography/bindings/openssl/api.py
@@ -35,12 +35,17 @@
         "engine",
         "err",
         "evp",
+        "nid",
         "opensslv",
+        "pem",
         "pkcs7",
+        "pkcs12",
         "rand",
         "rsa",
         "ssl",
-        "x509name"
+        "x509",
+        "x509name",
+        "x509v3",
     ]
 
     def __init__(self):
@@ -123,7 +128,8 @@
         return ctx
 
     def update_encrypt_context(self, ctx, plaintext):
-        buf = self.ffi.new("unsigned char[]", len(plaintext))
+        block_size = self.lib.EVP_CIPHER_CTX_block_size(ctx)
+        buf = self.ffi.new("unsigned char[]", len(plaintext) + block_size - 1)
         outlen = self.ffi.new("int *")
         res = self.lib.EVP_EncryptUpdate(
             ctx, buf, outlen, plaintext, len(plaintext)
@@ -132,8 +138,7 @@
         return self.ffi.buffer(buf)[:outlen[0]]
 
     def finalize_encrypt_context(self, ctx):
-        cipher = self.lib.EVP_CIPHER_CTX_cipher(ctx)
-        block_size = self.lib.EVP_CIPHER_block_size(cipher)
+        block_size = self.lib.EVP_CIPHER_CTX_block_size(ctx)
         buf = self.ffi.new("unsigned char[]", block_size)
         outlen = self.ffi.new("int *")
         res = self.lib.EVP_EncryptFinal_ex(ctx, buf, outlen)
diff --git a/cryptography/bindings/openssl/evp.py b/cryptography/bindings/openssl/evp.py
index 2b7b0f4..2015990 100644
--- a/cryptography/bindings/openssl/evp.py
+++ b/cryptography/bindings/openssl/evp.py
@@ -76,4 +76,5 @@
 MACROS = """
 int EVP_PKEY_assign_RSA(EVP_PKEY *, RSA *);
 int EVP_PKEY_assign_DSA(EVP_PKEY *, DSA *);
+int EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *);
 """
diff --git a/cryptography/bindings/openssl/nid.py b/cryptography/bindings/openssl/nid.py
new file mode 100644
index 0000000..0f5b000
--- /dev/null
+++ b/cryptography/bindings/openssl/nid.py
@@ -0,0 +1,44 @@
+# 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 = ""
+
+TYPES = """
+static const int NID_undef;
+static const int NID_dsa;
+static const int NID_dsaWithSHA;
+static const int NID_dsaWithSHA1;
+static const int NID_md2;
+static const int NID_md4;
+static const int NID_md5;
+static const int NID_mdc2;
+static const int NID_ripemd160;
+static const int NID_sha;
+static const int NID_sha1;
+static const int NID_sha256;
+static const int NID_sha384;
+static const int NID_sha512;
+static const int NID_sha224;
+static const int NID_sha;
+static const int NID_ecdsa_with_SHA1;
+static const int NID_ecdsa_with_SHA224;
+static const int NID_ecdsa_with_SHA256;
+static const int NID_ecdsa_with_SHA384;
+static const int NID_ecdsa_with_SHA512;
+static const int NID_crl_reason;
+static const int NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
+"""
+
+FUNCTIONS = ""
+
+MACROS = ""
diff --git a/cryptography/bindings/openssl/pem.py b/cryptography/bindings/openssl/pem.py
new file mode 100644
index 0000000..8c8f736
--- /dev/null
+++ b/cryptography/bindings/openssl/pem.py
@@ -0,0 +1,44 @@
+# 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/pem.h>
+"""
+
+TYPES = """
+typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata);
+"""
+
+FUNCTIONS = """
+X509 *PEM_read_bio_X509(BIO *, X509 **, pem_password_cb *, void *);
+int PEM_write_bio_X509(BIO *, X509 *);
+
+int PEM_write_bio_PrivateKey(BIO *, EVP_PKEY *, const EVP_CIPHER *,
+                             unsigned char *, int, pem_password_cb *, void *);
+
+EVP_PKEY *PEM_read_bio_PrivateKey(BIO *, EVP_PKEY **, pem_password_cb *,
+                                  void *);
+
+int PEM_write_bio_X509_REQ(BIO *, X509_REQ *);
+
+X509_REQ *PEM_read_bio_X509_REQ(BIO *, X509_REQ **, pem_password_cb *, void *);
+
+X509_CRL *PEM_read_bio_X509_CRL(BIO *, X509_CRL **, pem_password_cb *, void *);
+
+int PEM_write_bio_X509_CRL(BIO *, X509_CRL *);
+
+PKCS7 *PEM_read_bio_PKCS7(BIO *, PKCS7 **, pem_password_cb *, void *);
+DH *PEM_read_bio_DHparams(BIO *, DH **, pem_password_cb *, void *);
+"""
+
+MACROS = ""
diff --git a/cryptography/bindings/openssl/pkcs12.py b/cryptography/bindings/openssl/pkcs12.py
new file mode 100644
index 0000000..5c002b9
--- /dev/null
+++ b/cryptography/bindings/openssl/pkcs12.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/pkcs12.h>
+"""
+
+TYPES = """
+typedef ... PKCS12;
+"""
+
+FUNCTIONS = """
+void PKCS12_free(PKCS12 *);
+
+PKCS12 *d2i_PKCS12_bio(BIO *, PKCS12 **);
+int i2d_PKCS12_bio(BIO *, PKCS12 *);
+"""
+
+MACROS = """
+int PKCS12_parse(PKCS12 *, const char *, EVP_PKEY **, X509 **,
+                 struct stack_st_X509 **);
+PKCS12 *PKCS12_create(char *, char *, EVP_PKEY *, X509 *,
+                      struct stack_st_X509 *, int, int, int, int, int);
+"""
diff --git a/cryptography/bindings/openssl/x509.py b/cryptography/bindings/openssl/x509.py
new file mode 100644
index 0000000..9a51a6d
--- /dev/null
+++ b/cryptography/bindings/openssl/x509.py
@@ -0,0 +1,187 @@
+# 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 = """
+typedef struct {
+    ASN1_OBJECT *algorithm;
+    ...;
+} X509_ALGOR;
+
+typedef struct {
+    X509_ALGOR *signature;
+    ...;
+} X509_CINF;
+
+typedef struct {
+    ASN1_OBJECT *object;
+    ASN1_BOOLEAN critical;
+    ASN1_OCTET_STRING *value;
+} X509_EXTENSION;
+
+typedef ... X509_EXTENSIONS;
+
+typedef ... X509_REQ;
+
+typedef ... x509_revoked_st;
+
+typedef struct {
+    ASN1_INTEGER *serialNumber;
+    ASN1_TIME *revocationDate;
+    X509_EXTENSIONS *extensions;
+    int sequence;
+    ...;
+} X509_REVOKED;
+
+typedef struct {
+    struct x509_revoked_st *revoked;
+    ...;
+} X509_CRL_INFO;
+
+typedef struct {
+    X509_CRL_INFO *crl;
+    ...;
+} X509_CRL;
+
+typedef struct {
+    X509_CINF *cert_info;
+    ...;
+} X509;
+
+typedef ... X509_STORE;
+typedef ... NETSCAPE_SPKI;
+"""
+
+FUNCTIONS = """
+X509 *X509_new();
+void X509_free(X509 *);
+X509 *X509_dup(X509 *);
+
+int X509_print_ex(BIO *, X509 *, unsigned long, unsigned long);
+
+int X509_set_version(X509 *, long);
+
+EVP_PKEY *X509_get_pubkey(X509 *);
+int X509_set_pubkey(X509 *, EVP_PKEY *);
+
+unsigned char *X509_alias_get0(X509 *, int *);
+int X509_sign(X509 *, EVP_PKEY *, const EVP_MD *);
+
+int X509_digest(const X509 *, const EVP_MD *, unsigned char *, unsigned int *);
+
+ASN1_TIME *X509_gmtime_adj(ASN1_TIME *, long);
+
+unsigned long X509_subject_name_hash(X509 *);
+
+X509_NAME *X509_get_subject_name(X509 *);
+int X509_set_subject_name(X509 *, X509_NAME *);
+
+X509_NAME *X509_get_issuer_name(X509 *);
+int X509_set_issuer_name(X509 *, X509_NAME *);
+
+int X509_get_ext_count(X509 *);
+int X509_add_ext(X509 *, X509_EXTENSION *, int);
+X509_EXTENSION *X509_EXTENSION_dup(X509_EXTENSION *);
+X509_EXTENSION *X509_get_ext(X509 *, int);
+int X509_EXTENSION_get_critical(X509_EXTENSION *);
+ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *);
+void X509_EXTENSION_free(X509_EXTENSION *);
+
+int X509_REQ_set_version(X509_REQ *, long);
+X509_REQ *X509_REQ_new();
+void X509_REQ_free(X509_REQ *);
+int X509_REQ_set_pubkey(X509_REQ *, EVP_PKEY *);
+int X509_REQ_sign(X509_REQ *, EVP_PKEY *, const EVP_MD *);
+int X509_REQ_verify(X509_REQ *, EVP_PKEY *);
+EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *);
+int X509_REQ_add_extensions(X509_REQ *, X509_EXTENSIONS *);
+int X509_REQ_print_ex(BIO *, X509_REQ *, unsigned long, unsigned long);
+
+int X509V3_EXT_print(BIO *, X509_EXTENSION *, unsigned long, int);
+ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *);
+
+X509_REVOKED *X509_REVOKED_new();
+void X509_REVOKED_free(X509_REVOKED *);
+
+int X509_REVOKED_set_serialNumber(X509_REVOKED *, ASN1_INTEGER *);
+
+int X509_REVOKED_add1_ext_i2d(X509_REVOKED *, int, void *, int, unsigned long);
+
+X509_CRL *d2i_X509_CRL_bio(BIO *, X509_CRL **);
+X509_CRL *X509_CRL_new();
+void X509_CRL_free(X509_CRL *);
+int X509_CRL_add0_revoked(X509_CRL *, X509_REVOKED *);
+int i2d_X509_CRL_bio(BIO *, X509_CRL *);
+int X509_CRL_print(BIO *, X509_CRL *);
+int X509_CRL_set_issuer_name(X509_CRL *, X509_NAME *);
+int X509_CRL_sign(X509_CRL *, EVP_PKEY *, const EVP_MD *);
+
+int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *, EVP_PKEY *);
+int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *, EVP_PKEY *, const EVP_MD *);
+char *NETSCAPE_SPKI_b64_encode(NETSCAPE_SPKI *);
+EVP_PKEY *NETSCAPE_SPKI_get_pubkey(NETSCAPE_SPKI *);
+int NETSCAPE_SPKI_set_pubkey(NETSCAPE_SPKI *, EVP_PKEY *);
+NETSCAPE_SPKI *NETSCAPE_SPKI_new();
+void NETSCAPE_SPKI_free(NETSCAPE_SPKI *);
+
+/*  ASN1 serialization */
+int i2d_X509_bio(BIO *, X509 *);
+X509 *d2i_X509_bio(BIO *, X509 **);
+
+int i2d_X509_REQ_bio(BIO *, X509_REQ *);
+X509_REQ *d2i_X509_REQ_bio(BIO *, X509_REQ **);
+
+int i2d_PrivateKey_bio(BIO *, EVP_PKEY *);
+EVP_PKEY *d2i_PrivateKey_bio(BIO *, EVP_PKEY **);
+
+ASN1_INTEGER *X509_get_serialNumber(X509 *);
+int X509_set_serialNumber(X509 *, ASN1_INTEGER *);
+
+/*  X509_STORE */
+X509_STORE *X509_STORE_new();
+void X509_STORE_free(X509_STORE *);
+int X509_STORE_add_cert(X509_STORE *, X509 *);
+"""
+
+MACROS = """
+long X509_get_version(X509 *);
+
+ASN1_TIME *X509_get_notBefore(X509 *);
+ASN1_TIME *X509_get_notAfter(X509 *);
+
+long X509_REQ_get_version(X509_REQ *);
+X509_NAME *X509_REQ_get_subject_name(X509_REQ *);
+
+struct stack_st_X509 *sk_X509_new_null();
+void sk_X509_free(struct stack_st_X509 *);
+int sk_X509_num(struct stack_st_X509 *);
+int sk_X509_push(struct stack_st_X509 *, X509 *);
+X509 *sk_X509_value(struct stack_st_X509 *, int);
+
+X509_EXTENSIONS *sk_X509_EXTENSION_new_null();
+int sk_X509_EXTENSION_num(X509_EXTENSIONS *);
+X509_EXTENSION *sk_X509_EXTENSION_value(X509_EXTENSIONS *, int);
+int sk_X509_EXTENSION_push(X509_EXTENSIONS *, X509_EXTENSION *);
+void sk_X509_EXTENSION_delete(X509_EXTENSIONS *, int);
+void sk_X509_EXTENSION_free(X509_EXTENSIONS *);
+
+int sk_X509_REVOKED_num(struct x509_revoked_st *);
+X509_REVOKED *sk_X509_REVOKED_value(struct x509_revoked_st *, int);
+
+/* These aren't macros these arguments are all const X on openssl > 1.0.x */
+int X509_CRL_set_lastUpdate(X509_CRL *, const ASN1_TIME *);
+int X509_CRL_set_nextUpdate(X509_CRL *, const ASN1_TIME *);
+"""
diff --git a/cryptography/bindings/openssl/x509v3.py b/cryptography/bindings/openssl/x509v3.py
new file mode 100644
index 0000000..413bde5
--- /dev/null
+++ b/cryptography/bindings/openssl/x509v3.py
@@ -0,0 +1,94 @@
+# 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/x509v3.h>
+"""
+
+TYPES = """
+typedef struct {
+    X509 *issuer_cert;
+    X509 *subject_cert;
+    ...;
+} X509V3_CTX;
+
+typedef void * (*X509V3_EXT_D2I)(void *, const unsigned char **, long);
+
+typedef struct {
+    ASN1_ITEM_EXP *it;
+    X509V3_EXT_D2I d2i;
+    ...;
+} X509V3_EXT_METHOD;
+
+static const int GEN_OTHERNAME;
+static const int GEN_EMAIL;
+static const int GEN_X400;
+static const int GEN_DNS;
+static const int GEN_URI;
+static const int GEN_DIRNAME;
+static const int GEN_EDIPARTY;
+static const int GEN_IPADD;
+static const int GEN_RID;
+
+typedef struct {
+    ...;
+} OTHERNAME;
+
+typedef struct {
+    ...;
+} EDIPARTYNAME;
+
+typedef struct {
+    int type;
+    union {
+        char *ptr;
+        OTHERNAME *otherName;  /* otherName */
+        ASN1_IA5STRING *rfc822Name;
+        ASN1_IA5STRING *dNSName;
+        ASN1_TYPE *x400Address;
+        X509_NAME *directoryName;
+        EDIPARTYNAME *ediPartyName;
+        ASN1_IA5STRING *uniformResourceIdentifier;
+        ASN1_OCTET_STRING *iPAddress;
+        ASN1_OBJECT *registeredID;
+
+        /* Old names */
+        ASN1_OCTET_STRING *ip; /* iPAddress */
+        X509_NAME *dirn;       /* dirn */
+        ASN1_IA5STRING *ia5;   /* rfc822Name, dNSName, */
+                               /*   uniformResourceIdentifier */
+        ASN1_OBJECT *rid;      /* registeredID */
+        ASN1_TYPE *other;      /* x400Address */
+    } d;
+    ...;
+} GENERAL_NAME;
+
+typedef struct stack_st_GENERAL_NAME GENERAL_NAMES;
+"""
+
+FUNCTIONS = """
+void X509V3_set_ctx(X509V3_CTX *, X509 *, X509 *, X509_REQ *, X509_CRL *, int);
+X509_EXTENSION *X509V3_EXT_nconf(CONF *, X509V3_CTX *, char *, char *);
+int GENERAL_NAME_print(BIO *, GENERAL_NAME *);
+"""
+
+MACROS = """
+void *X509V3_set_ctx_nodb(X509V3_CTX *);
+int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *);
+int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *);
+GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int);
+
+/* These aren't macros these functions are all const X on openssl > 1.0.x */
+const X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *);
+const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int);
+"""
diff --git a/docs/contributing.rst b/docs/contributing.rst
index b125d1a..6a76c70 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -32,11 +32,8 @@
 The purpose of these policies is to minimize the chances we merge a change
 which jeopardizes our users' security.
 
-We do not yet have a formal security contact. To report security issues in
-``cryptography`` you should email ``alex.gaynor@gmail.com``, messages may be
-encrypted with PGP to key fingerprint
-``E27D 4AA0 1651 72CB C5D2  AF2B 125F 5C67 DFE9 4084`` (this public key is
-available from most commonly-used keyservers).
+If you believe you've identified a security issue in ``cryptography``, please
+follow the directions on the :doc:`security page </security>`.
 
 Code
 ----
@@ -65,6 +62,16 @@
     // Bad
     long f(long x);
 
+...unless they're inside a struct:
+
+.. code-block:: c
+
+    struct my_struct {
+        char *name;
+        int number;
+        ...;
+    };
+
 Don't include stray ``void`` parameters:
 
 .. code-block:: c
diff --git a/docs/index.rst b/docs/index.rst
index 5cc455f..a868a5d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -34,4 +34,5 @@
     primitives/index
     bindings/index
     contributing
+    security
     community
diff --git a/docs/security.rst b/docs/security.rst
new file mode 100644
index 0000000..36c8e0f
--- /dev/null
+++ b/docs/security.rst
@@ -0,0 +1,12 @@
+Security
+========
+
+We take the security of ``cryptography`` seriously. If you believe you've
+identified a security issue in it, please report it to
+``alex.gaynor@gmail.com``. Message may be encrypted with PGP using key
+fingerprint ``E27D 4AA0 1651 72CB C5D2  AF2B 125F 5C67 DFE9 4084`` (this public
+key is available from most commonly-used keyservers).
+
+Once you’ve submitted an issue via email, you should receive an acknowledgment
+within 48 hours, and depending on the action to be taken, you may receive
+further followup emails.
diff --git a/tests/primitives/test_block.py b/tests/primitives/test_block.py
index f4d3f46..9f5905b 100644
--- a/tests/primitives/test_block.py
+++ b/tests/primitives/test_block.py
@@ -63,3 +63,14 @@
 
         with pytest.raises(ValueError):
             cipher.finalize()
+
+    def test_unaligned_block_encryption(self, api):
+        cipher = BlockCipher(
+            ciphers.AES(binascii.unhexlify(b"0" * 32)),
+            modes.ECB(),
+            api
+        )
+        ct = cipher.encrypt(b"a" * 15)
+        assert ct == b""
+        ct += cipher.encrypt(b"a" * 65)
+        assert len(ct) == 80
diff --git a/tests/primitives/utils.py b/tests/primitives/utils.py
index 8b32700..a3759b0 100644
--- a/tests/primitives/utils.py
+++ b/tests/primitives/utils.py
@@ -43,7 +43,7 @@
 
 
 def generate_hash_test(param_loader, path, file_names, hash_cls,
-                       only_if=lambda api: True, skip_message=None):
+                       only_if=None, skip_message=None):
     def test_hash(self):
         for api in _ALL_APIS:
             for file_name in file_names:
@@ -60,7 +60,7 @@
 
 
 def hash_test(api, hash_cls, params, only_if, skip_message):
-    if not only_if(api):
+    if only_if is not None and not only_if(api):
         pytest.skip(skip_message)
     msg = params[0]
     md = params[1]
@@ -70,7 +70,7 @@
 
 
 def generate_base_hash_test(hash_cls, digest_size, block_size,
-                            only_if=lambda api: True, skip_message=None):
+                            only_if=None, skip_message=None):
     def test_base_hash(self):
         for api in _ALL_APIS:
             yield (
@@ -87,7 +87,7 @@
 
 def base_hash_test(api, hash_cls, digest_size, block_size, only_if,
                    skip_message):
-    if not only_if(api):
+    if only_if is not None and not only_if(api):
         pytest.skip(skip_message)
     m = hash_cls(api=api)
     assert m.digest_size == digest_size
@@ -97,7 +97,7 @@
     assert m._ctx != m_copy._ctx
 
 
-def generate_long_string_hash_test(hash_factory, md, only_if=lambda api: True,
+def generate_long_string_hash_test(hash_factory, md, only_if=None,
                                    skip_message=None):
     def test_long_string_hash(self):
         for api in _ALL_APIS:
@@ -113,7 +113,7 @@
 
 
 def long_string_hash_test(api, hash_factory, md, only_if, skip_message):
-    if not only_if(api):
+    if only_if is not None and not only_if(api):
         pytest.skip(skip_message)
     m = hash_factory(api)
     m.update(b"a" * 1000000)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index a9bb6a8..3fe9e57 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -269,6 +269,20 @@
     ]
 
 
+def test_load_cryptrec_vectors_invalid():
+    vector_data = textwrap.dedent("""
+    # Vectors taken from http://info.isl.ntt.co.jp/crypt/eng/camellia/
+    # Download is t_camelia.txt
+
+    # Camellia with 128-bit key
+
+    E No.001 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+    """).splitlines()
+
+    with pytest.raises(ValueError):
+        load_cryptrec_vectors(vector_data)
+
+
 def test_load_cryptrec_vectors_from_file_encrypt():
     test_set = load_cryptrec_vectors_from_file(
         "Camellia/NTT/camellia-128-ecb.txt"
diff --git a/tests/utils.py b/tests/utils.py
index 03b780f..fa7cc68 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -90,6 +90,8 @@
                 "plaintext": pt,
                 "ciphertext": ct
             })
+        else:
+            raise ValueError("Invalid line in file '{}'".format(line))
     return cryptrec_list
 
 
diff --git a/tox.ini b/tox.ini
index 0a28af1..e72eb58 100644
--- a/tox.ini
+++ b/tox.ini
@@ -8,7 +8,7 @@
     pretend
 commands =
     coverage run --source=cryptography/,tests/ -m pytest
-    coverage report -m
+    coverage report -m --fail-under 100
 
 [testenv:docs]
 deps = sphinx