Merge pull request #227 from dreid/explicit-backend-in-hazmat

Explicit backend
diff --git a/.travis/install.sh b/.travis/install.sh
index fdd7190..4aa3979 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -5,24 +5,8 @@
 
 if [[ "${OPENSSL}" == "0.9.8" ]]; then
     sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu/ lucid main"
-fi
-
-if [[ "${TOX_ENV}" == "pypy" ]]; then
-    sudo add-apt-repository -y ppa:pypy/ppa
-fi
-
-sudo apt-get -y update
-
-if [[ "${OPENSSL}" == "0.9.8" ]]; then
+    sudo apt-get -y update
     sudo apt-get install -y --force-yes libssl-dev/lucid
 fi
 
-if [[ "${TOX_ENV}" == "pypy" ]]; then
-    sudo apt-get install -y pypy
-
-    # This is required because we need to get rid of the Travis installed PyPy
-    # or it'll take precedence over the PPA installed one.
-    sudo rm -rf /usr/local/pypy/bin
-fi
-
 pip install tox coveralls
diff --git a/cryptography/hazmat/bindings/openssl/backend.py b/cryptography/hazmat/bindings/openssl/backend.py
index db4d18e..9f8ea93 100644
--- a/cryptography/hazmat/bindings/openssl/backend.py
+++ b/cryptography/hazmat/bindings/openssl/backend.py
@@ -193,6 +193,33 @@
     def create_symmetric_decryption_ctx(self, cipher, mode):
         return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
 
+    def _handle_error(self):
+        code = self.lib.ERR_get_error()
+        assert code != 0
+        lib = self.lib.ERR_GET_LIB(code)
+        func = self.lib.ERR_GET_FUNC(code)
+        reason = self.lib.ERR_GET_REASON(code)
+        return self._handle_error_code(lib, func, reason)
+
+    def _handle_error_code(self, lib, func, reason):
+        if lib == self.lib.ERR_LIB_EVP:
+            if func == self.lib.EVP_F_EVP_ENCRYPTFINAL_EX:
+                if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
+                    raise ValueError(
+                        "The length of the provided data is not a multiple of "
+                        "the block length"
+                    )
+            elif func == self.lib.EVP_F_EVP_DECRYPTFINAL_EX:
+                if reason == self.lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH:
+                    raise ValueError(
+                        "The length of the provided data is not a multiple of "
+                        "the block length"
+                    )
+
+        raise SystemError(
+            "Unknown error code from OpenSSL, you should probably file a bug."
+        )
+
 
 class GetCipherByName(object):
     def __init__(self, fmt):
@@ -268,7 +295,9 @@
         buf = self._backend.ffi.new("unsigned char[]", self._cipher.block_size)
         outlen = self._backend.ffi.new("int *")
         res = self._backend.lib.EVP_CipherFinal_ex(self._ctx, buf, outlen)
-        assert res != 0
+        if res == 0:
+            self._backend._handle_error()
+
         res = self._backend.lib.EVP_CIPHER_CTX_cleanup(self._ctx)
         assert res == 1
         return self._backend.ffi.buffer(buf)[:outlen[0]]
diff --git a/cryptography/hazmat/bindings/openssl/err.py b/cryptography/hazmat/bindings/openssl/err.py
index 6a36dee..3dac694 100644
--- a/cryptography/hazmat/bindings/openssl/err.py
+++ b/cryptography/hazmat/bindings/openssl/err.py
@@ -21,6 +21,13 @@
     const char *string;
 };
 typedef struct ERR_string_data_st ERR_STRING_DATA;
+
+static const int ERR_LIB_EVP;
+
+static const int EVP_F_EVP_ENCRYPTFINAL_EX;
+static const int EVP_F_EVP_DECRYPTFINAL_EX;
+
+static const int EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH;
 """
 
 FUNCTIONS = """
diff --git a/docs/exceptions.rst b/docs/exceptions.rst
index ab1b28f..c6f5a7c 100644
--- a/docs/exceptions.rst
+++ b/docs/exceptions.rst
@@ -12,4 +12,3 @@
 
     This is raised when a backend doesn't support the requested algorithm (or
     combination of algorithms).
-
diff --git a/docs/hazmat/primitives/padding.rst b/docs/hazmat/primitives/padding.rst
index aebb4d4..4d79ac8 100644
--- a/docs/hazmat/primitives/padding.rst
+++ b/docs/hazmat/primitives/padding.rst
@@ -25,8 +25,14 @@
         >>> padder = padding.PKCS7(128).padder()
         >>> padder.update(b"1111111111")
         ''
-        >>> padder.finalize()
+        >>> padded_data = padder.finalize()
+        >>> padded_data
         '1111111111\x06\x06\x06\x06\x06\x06'
+        >>> unpadder = padding.PKCS7(128).unpadder()
+        >>> unpadder.update(padded_data)
+        ''
+        >>> unpadder.finalize()
+        '1111111111'
 
     :param block_size: The size of the block in bits that the data is being
                        padded to.
diff --git a/docs/hazmat/primitives/symmetric-encryption.rst b/docs/hazmat/primitives/symmetric-encryption.rst
index f63ad90..4ab9140 100644
--- a/docs/hazmat/primitives/symmetric-encryption.rst
+++ b/docs/hazmat/primitives/symmetric-encryption.rst
@@ -86,6 +86,15 @@
     everything into the context. Once that is done call ``finalize()`` to
     finish the operation and obtain the remainder of the data.
 
+    Block ciphers require that plaintext or ciphertext always be a multiple of
+    their block size, because of that **padding** is often required to make a
+    message the correct size. ``CipherContext`` will not automatically apply
+    any padding; you'll need to add your own. For block ciphers the reccomended
+    padding is :class:`cryptography.hazmat.primitives.padding.PKCS7`. If you
+    are using a stream cipher mode (such as
+    :class:`cryptography.hazmat.primitives.modes.CTR`) you don't have to worry
+    about this.
+
     .. method:: update(data)
 
         :param bytes data: The data you wish to pass into the context.
@@ -101,6 +110,9 @@
     .. method:: finalize()
 
         :return bytes: Returns the remainder of the data.
+        :raises ValueError: This is raised when the data provided isn't
+                            correctly padded to be a multiple of the
+                            algorithm's block size.
 
         Once ``finalize`` is called this object can no longer be used and
         :meth:`update` and :meth:`finalize` will raise
diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py
index e6604e6..1eb6f20 100644
--- a/tests/hazmat/bindings/test_openssl.py
+++ b/tests/hazmat/bindings/test_openssl.py
@@ -74,3 +74,24 @@
         )
         with pytest.raises(UnsupportedAlgorithm):
             cipher.encryptor()
+
+    def test_handle_unknown_error(self):
+        with pytest.raises(SystemError):
+            backend._handle_error_code(0, 0, 0)
+
+        with pytest.raises(SystemError):
+            backend._handle_error_code(backend.lib.ERR_LIB_EVP, 0, 0)
+
+        with pytest.raises(SystemError):
+            backend._handle_error_code(
+                backend.lib.ERR_LIB_EVP,
+                backend.lib.EVP_F_EVP_ENCRYPTFINAL_EX,
+                0
+            )
+
+        with pytest.raises(SystemError):
+            backend._handle_error_code(
+                backend.lib.ERR_LIB_EVP,
+                backend.lib.EVP_F_EVP_DECRYPTFINAL_EX,
+                0
+            )
diff --git a/tests/hazmat/primitives/test_block.py b/tests/hazmat/primitives/test_block.py
index 70d7098..4a8e88b 100644
--- a/tests/hazmat/primitives/test_block.py
+++ b/tests/hazmat/primitives/test_block.py
@@ -104,3 +104,19 @@
 
         with pytest.raises(UnsupportedAlgorithm):
             cipher.decryptor()
+
+    def test_incorrectly_padded(self, backend):
+        cipher = Cipher(
+            algorithms.AES(b"\x00" * 16),
+            modes.CBC(b"\x00" * 16),
+            backend
+        )
+        encryptor = cipher.encryptor()
+        encryptor.update(b"1")
+        with pytest.raises(ValueError):
+            encryptor.finalize()
+
+        decryptor = cipher.decryptor()
+        decryptor.update(b"1")
+        with pytest.raises(ValueError):
+            decryptor.finalize()