Merge pull request #792 from reaperhulk/rsa-pss-verify

RSA PSS Verify
diff --git a/.travis.yml b/.travis.yml
index b7fa090..7d5663d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,11 +16,13 @@
         - TOX_ENV=py27
         - TOX_ENV=py32
         - TOX_ENV=py33
+        - TOX_ENV=py34
         - TOX_ENV=pypy
         - TOX_ENV=py26 OPENSSL=0.9.8
         - TOX_ENV=py27 OPENSSL=0.9.8
         - TOX_ENV=py32 OPENSSL=0.9.8
         - TOX_ENV=py33 OPENSSL=0.9.8
+        - TOX_ENV=py34 OPENSSL=0.9.8
         - TOX_ENV=pypy OPENSSL=0.9.8
         - TOX_ENV=docs
         - TOX_ENV=pep8
@@ -60,6 +62,9 @@
           env: TOX_ENV=py33
           compiler: gcc
         - os: osx
+          env: TOX_ENV=py34
+          compiler: gcc
+        - os: osx
           env: TOX_ENV=pypy
           compiler: gcc
         - os: osx
@@ -75,6 +80,9 @@
           env: TOX_ENV=py33 OPENSSL=0.9.8
           compiler: gcc
         - os: osx
+          env: TOX_ENV=py34 OPENSSL=0.9.8
+          compiler: gcc
+        - os: osx
           env: TOX_ENV=pypy OPENSSL=0.9.8
           compiler: gcc
         - os: osx
diff --git a/.travis/install.sh b/.travis/install.sh
index 7e77fc8..58d7404 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -52,8 +52,13 @@
             pip install virtualenv
             ;;
         py33)
-            pyenv install 3.3.2
-            pyenv global 3.3.2
+            pyenv install 3.3.5
+            pyenv global 3.3.5
+            pip install virtualenv
+            ;;
+        py34)
+            pyenv install 3.4.0
+            pyenv global 3.4.0
             pip install virtualenv
             ;;
         docs)
@@ -78,6 +83,9 @@
         py33)
             sudo apt-get install python3.3 python3.3-dev
             ;;
+        py34)
+            sudo apt-get install python3.4 python3.4-dev
+            ;;
         py3pep8)
             sudo apt-get install python3.3 python3.3-dev
             ;;
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 391427d..abbea9f 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,8 +1,10 @@
 Changelog
 =========
 
-0.3 - 2014-XX-XX
-~~~~~~~~~~~~~~~~
+0.3 - `master`_
+~~~~~~~~~~~~~~~
+
+.. note:: This version is not yet released and is under active development.
 
 * Added :class:`~cryptography.hazmat.primitives.twofactor.hotp.HOTP`.
 * Added :class:`~cryptography.hazmat.primitives.twofactor.totp.TOTP`.
@@ -40,3 +42,4 @@
 
 * Initial release.
 
+.. _`master`: https://github.com/pyca/cryptography/
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 9b8e8f0..7c058f5 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -16,6 +16,8 @@
 import collections
 import itertools
 
+import six
+
 from cryptography import utils
 from cryptography.exceptions import (
     InvalidTag, InternalError, AlreadyFinalized, UnsupportedCipher,
@@ -262,11 +264,24 @@
         )
 
     def _bn_to_int(self, bn):
-        hex_cdata = self._lib.BN_bn2hex(bn)
-        assert hex_cdata != self._ffi.NULL
-        hex_str = self._ffi.string(hex_cdata)
-        self._lib.OPENSSL_free(hex_cdata)
-        return int(hex_str, 16)
+        if six.PY3:
+            # Python 3 has constant time from_bytes, so use that.
+
+            bn_num_bytes = (self._lib.BN_num_bits(bn) + 7) // 8
+            bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes)
+            bin_len = self._lib.BN_bn2bin(bn, bin_ptr)
+            assert bin_len > 0
+            assert bin_ptr != self._ffi.NULL
+            return int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")
+
+        else:
+            # Under Python 2 the best we can do is hex()
+
+            hex_cdata = self._lib.BN_bn2hex(bn)
+            assert hex_cdata != self._ffi.NULL
+            hex_str = self._ffi.string(hex_cdata)
+            self._lib.OPENSSL_free(hex_cdata)
+            return int(hex_str, 16)
 
     def _int_to_bn(self, num):
         """
@@ -275,12 +290,24 @@
         ownership of the object). Be sure to register it for GC if it will
         be discarded after use.
         """
-        hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0"
-        bn_ptr = self._ffi.new("BIGNUM **")
-        res = self._lib.BN_hex2bn(bn_ptr, hex_num)
-        assert res != 0
-        assert bn_ptr[0] != self._ffi.NULL
-        return bn_ptr[0]
+
+        if six.PY3:
+            # Python 3 has constant time to_bytes, so use that.
+
+            binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big")
+            bn_ptr = self._lib.BN_bin2bn(binary, len(binary), self._ffi.NULL)
+            assert bn_ptr != self._ffi.NULL
+            return bn_ptr
+
+        else:
+            # Under Python 2 the best we can do is hex()
+
+            hex_num = hex(num).rstrip("L").lstrip("0x").encode("ascii") or b"0"
+            bn_ptr = self._ffi.new("BIGNUM **")
+            res = self._lib.BN_hex2bn(bn_ptr, hex_num)
+            assert res != 0
+            assert bn_ptr[0] != self._ffi.NULL
+            return bn_ptr[0]
 
     def generate_rsa_private_key(self, public_exponent, key_size):
         if public_exponent < 3:
diff --git a/cryptography/hazmat/bindings/utils.py b/cryptography/hazmat/bindings/utils.py
index b825348..318b82b 100644
--- a/cryptography/hazmat/bindings/utils.py
+++ b/cryptography/hazmat/bindings/utils.py
@@ -13,6 +13,8 @@
 
 from __future__ import absolute_import, division, print_function
 
+import binascii
+
 import sys
 
 import cffi
@@ -50,7 +52,8 @@
         includes.append(module.INCLUDES)
         customizations.append(module.CUSTOMIZATIONS)
 
-    ffi.cdef("\n".join(types + functions + macros))
+    cdef_sources = types + functions + macros
+    ffi.cdef("\n".join(cdef_sources))
 
     # 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
@@ -60,14 +63,16 @@
     # is legal, but the following will fail to compile:
     #   int foo(int);
     #   int foo(short);
+    source = "\n".join(
+        [pre_include] +
+        includes +
+        [post_include] +
+        functions +
+        customizations
+    )
     lib = ffi.verify(
-        source="\n".join(
-            [pre_include] +
-            includes +
-            [post_include] +
-            functions +
-            customizations
-        ),
+        source=source,
+        modulename=_create_modulename(cdef_sources, source, sys.version),
         libraries=libraries,
         ext_package="cryptography",
     )
@@ -81,3 +86,20 @@
                     delattr(lib, name)
 
     return ffi, lib
+
+
+def _create_modulename(cdef_sources, source, sys_version):
+    """
+    cffi creates a modulename internally that incorporates the cffi version.
+    This will cause cryptography's wheels to break when the version of cffi
+    the user has does not match what was used when building the wheel. To
+    resolve this we build our own modulename that uses most of the same code
+    from cffi but elides the version key.
+    """
+    key = '\x00'.join([sys_version[:3], source] + cdef_sources)
+    key = key.encode('utf-8')
+    k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
+    k1 = k1.lstrip('0x').rstrip('L')
+    k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
+    k2 = k2.lstrip('0').rstrip('L')
+    return '_Cryptography_cffi_{0}{1}'.format(k1, k2)
diff --git a/cryptography/hazmat/primitives/interfaces.py b/cryptography/hazmat/primitives/interfaces.py
index 3824bcd..eab48b4 100644
--- a/cryptography/hazmat/primitives/interfaces.py
+++ b/cryptography/hazmat/primitives/interfaces.py
@@ -367,6 +367,12 @@
 
 class DSAPublicKey(six.with_metaclass(abc.ABCMeta)):
     @abc.abstractproperty
+    def key_size(self):
+        """
+        The bit length of the prime modulus.
+        """
+
+    @abc.abstractproperty
     def y(self):
         """
         The public key.
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index cc2a300..cefd81a 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -323,7 +323,13 @@
 
     .. versionadded:: 0.3
 
-    A `DSA`_ private key.
+    A `DSA`_ public key.
+
+    .. attribute:: key_size
+
+        :type: int
+
+        The bit length of the modulus.
 
     .. method:: parameters()
 
diff --git a/setup.py b/setup.py
index 7f7ba9e..f8b84a3 100644
--- a/setup.py
+++ b/setup.py
@@ -119,6 +119,7 @@
         "Programming Language :: Python :: 3",
         "Programming Language :: Python :: 3.2",
         "Programming Language :: Python :: 3.3",
+        "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: Implementation :: CPython",
         "Programming Language :: Python :: Implementation :: PyPy",
         "Topic :: Security :: Cryptography",
diff --git a/tests/hazmat/bindings/test_utils.py b/tests/hazmat/bindings/test_utils.py
new file mode 100644
index 0000000..0d5b34d
--- /dev/null
+++ b/tests/hazmat/bindings/test_utils.py
@@ -0,0 +1,25 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import, division, print_function
+
+from cryptography.hazmat.bindings import utils
+
+
+def test_create_modulename():
+    cdef_sources = ["cdef sources go here"]
+    source = "source code"
+    name = utils._create_modulename(cdef_sources, source, "2.7")
+    assert name == "_Cryptography_cffi_bcba7f4bx4a14b588"
+    name = utils._create_modulename(cdef_sources, source, "3.2")
+    assert name == "_Cryptography_cffi_a7462526x4a14b588"
diff --git a/tox.ini b/tox.ini
index 3ee449f..7288465 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py26,py27,pypy,py32,py33,docs,pep8,py3pep8
+envlist = py26,py27,pypy,py32,py33,py34,docs,pep8,py3pep8
 
 [testenv]
 deps =