RSA PSS signature support
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index abbea9f..ef2f7c1 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,8 @@
 * Added :class:`~cryptography.hazmat.primitives.twofactor.hotp.HOTP`.
 * Added :class:`~cryptography.hazmat.primitives.twofactor.totp.TOTP`.
 * Added :class:`~cryptography.hazmat.primitives.ciphers.algorithms.IDEA` support.
+* Added signature support to :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey`
+  and verification support to :class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey`.
 
 0.2.2 - 2014-03-03
 ~~~~~~~~~~~~~~~~~~
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 7c058f5..1495e75 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -704,12 +704,29 @@
             raise TypeError(
                 "Expected provider of interfaces.AsymmetricPadding")
 
-        if padding.name == "EMSA-PKCS1-v1_5":
+        if isinstance(padding, PKCS1v15):
             if self._backend._lib.Cryptography_HAS_PKEY_CTX:
                 self._finalize_method = self._finalize_pkey_ctx
                 self._padding_enum = self._backend._lib.RSA_PKCS1_PADDING
             else:
                 self._finalize_method = self._finalize_pkcs1
+        elif isinstance(padding, PSS):
+            if not isinstance(padding._mgf, MGF1):
+                raise UnsupportedAlgorithm(
+                    "Only MGF1 is supported by this backend"
+                )
+
+            if not self._backend.mgf1_hash_supported(padding._mgf._algorithm):
+                raise UnsupportedHash(
+                    "When OpenSSL is older than 1.0.1 then only SHA1 is "
+                    "supported with MGF1."
+                )
+
+            if self._backend._lib.Cryptography_HAS_PKEY_CTX:
+                self._finalize_method = self._finalize_pkey_ctx
+                self._padding_enum = self._backend._lib.RSA_PKCS1_PSS_PADDING
+            else:
+                self._finalize_method = self._finalize_pss
         else:
             raise UnsupportedPadding(
                 "{0} is not supported by this backend".format(padding.name)
@@ -754,6 +771,19 @@
         res = self._backend._lib.EVP_PKEY_CTX_set_rsa_padding(
             pkey_ctx, self._padding_enum)
         assert res > 0
+        if isinstance(self._padding, PSS):
+            res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen(
+                pkey_ctx, self._get_salt_length())
+            assert res > 0
+            if self._backend._lib.Cryptography_HAS_MGF1_MD:
+                # MGF1 MD is configurable in OpenSSL 1.0.1+
+                mgf1_md = self._backend._lib.EVP_get_digestbyname(
+                    self._padding._mgf._algorithm.name.encode("ascii"))
+                assert mgf1_md != self._backend._ffi.NULL
+                res = self._backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(
+                    pkey_ctx, mgf1_md
+                )
+                assert res > 0
         data_to_sign = self._hash_ctx.finalize()
         self._hash_ctx = None
         buflen = self._backend._ffi.new("size_t *")
@@ -768,7 +798,13 @@
         buf = self._backend._ffi.new("unsigned char[]", buflen[0])
         res = self._backend._lib.EVP_PKEY_sign(
             pkey_ctx, buf, buflen, data_to_sign, len(data_to_sign))
-        assert res == 1
+        if res != 1:
+            errors = self._backend._consume_errors()
+            assert errors[0].lib == self._backend._lib.ERR_LIB_RSA
+            assert (errors[0].reason ==
+                    self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE)
+            raise ValueError("Salt length too long for key size")
+
         return self._backend._ffi.buffer(buf)[:]
 
     def _finalize_pkcs1(self, evp_pkey, pkey_size, evp_md):
@@ -785,6 +821,45 @@
         assert res == 1
         return self._backend._ffi.buffer(sig_buf)[:sig_len[0]]
 
+    def _finalize_pss(self, evp_pkey, pkey_size, evp_md):
+        data_to_sign = self._hash_ctx.finalize()
+        self._hash_ctx = None
+        padded = self._backend._ffi.new("unsigned char[]", pkey_size)
+        rsa_cdata = self._backend._lib.EVP_PKEY_get1_RSA(evp_pkey)
+        assert rsa_cdata != self._backend._ffi.NULL
+        rsa_cdata = self._backend._ffi.gc(rsa_cdata,
+                                          self._backend._lib.RSA_free)
+        res = self._backend._lib.RSA_padding_add_PKCS1_PSS(
+            rsa_cdata,
+            padded,
+            data_to_sign,
+            evp_md,
+            self._get_salt_length()
+        )
+        if res != 1:
+            errors = self._backend._consume_errors()
+            assert errors[0].lib == self._backend._lib.ERR_LIB_RSA
+            assert (errors[0].reason ==
+                    self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE)
+            raise ValueError("Salt length too long for key size")
+
+        sig_buf = self._backend._ffi.new("char[]", pkey_size)
+        sig_len = self._backend._lib.RSA_private_encrypt(
+            pkey_size,
+            padded,
+            sig_buf,
+            rsa_cdata,
+            self._backend._lib.RSA_NO_PADDING
+        )
+        assert sig_len != -1
+        return self._backend._ffi.buffer(sig_buf)[:sig_len]
+
+    def _get_salt_length(self):
+        if self._padding._mgf._salt_length is MGF1.MAX_LENGTH:
+            return -2
+        else:
+            return self._padding._mgf._salt_length
+
 
 @utils.register_interface(interfaces.AsymmetricVerificationContext)
 class _RSAVerificationContext(object):
diff --git a/docs/hazmat/primitives/asymmetric/rsa.rst b/docs/hazmat/primitives/asymmetric/rsa.rst
index 03a7cae..dbb0da4 100644
--- a/docs/hazmat/primitives/asymmetric/rsa.rst
+++ b/docs/hazmat/primitives/asymmetric/rsa.rst
@@ -72,7 +72,12 @@
             ...     backend=default_backend()
             ... )
             >>> signer = private_key.signer(
-            ...     padding.PKCS1v15(),
+            ...     padding.PSS(
+            ...         mgf=padding.MGF1(
+            ...             algorithm=hashes.SHA256(),
+            ...             salt_length=padding.MGF1.MAX_LENGTH
+            ...         )
+            ...     ),
             ...     hashes.SHA256(),
             ...     default_backend()
             ... )
@@ -99,6 +104,22 @@
             the provided ``backend`` does not implement
             :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
 
+        :raises TypeError: This is raised when the padding is not an
+            :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
+            provider.
+
+        :raises UnsupportedHash: This is raised when the backend does not
+            support the chosen hash algorithm. If the padding is
+            :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`
+            with the
+            :class:`~cryptography.hazmat.primitives.asymmetric.padding.MGF1`
+            mask generation function it may also refer to the `MGF1` hash
+            algorithm.
+
+        :raises UnsupportedPadding: This is raised when the backend does not
+            support the chosen padding.
+
+
 .. class:: RSAPublicKey(public_exponent, modulus)
 
     .. versionadded:: 0.2
@@ -136,12 +157,31 @@
             ...     key_size=2048,
             ...     backend=default_backend()
             ... )
-            >>> signer = private_key.signer(padding.PKCS1v15(), hashes.SHA256(), default_backend())
+            >>> signer = private_key.signer(
+            ...     padding.PSS(
+            ...         mgf=padding.MGF1(
+            ...             algorithm=hashes.SHA256(),
+            ...             salt_length=padding.MGF1.MAX_LENGTH
+            ...         )
+            ...     ),
+            ...     hashes.SHA256(),
+            ...     default_backend()
+            ... )
             >>> data= b"this is some data I'd like to sign"
             >>> signer.update(data)
             >>> signature = signer.finalize()
             >>> public_key = private_key.public_key()
-            >>> verifier = public_key.verifier(signature, padding.PKCS1v15(), hashes.SHA256(), default_backend())
+            >>> verifier = public_key.verifier(
+            ...     signature,
+            ...     padding.PSS(
+            ...         mgf=padding.MGF1(
+            ...             algorithm=hashes.SHA256(),
+            ...             salt_length=padding.MGF1.MAX_LENGTH
+            ...         )
+            ...     ),
+            ...     hashes.SHA256(),
+            ...     default_backend()
+            ... )
             >>> verifier.update(data)
             >>> verifier.verify()
 
@@ -166,6 +206,21 @@
             the provided ``backend`` does not implement
             :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
 
+        :raises TypeError: This is raised when the padding is not an
+            :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricPadding`
+            provider.
+
+        :raises UnsupportedHash: This is raised when the backend does not
+            support the chosen hash algorithm. If the padding is
+            :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`
+            with the
+            :class:`~cryptography.hazmat.primitives.asymmetric.padding.MGF1`
+            mask generation function it may also refer to the `MGF1` hash
+            algorithm.
+
+        :raises UnsupportedPadding: This is raised when the backend does not
+            support the chosen padding.
+
 .. _`RSA`: https://en.wikipedia.org/wiki/RSA_(cryptosystem)
 .. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography
 .. _`use 65537`: http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html
diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py
index c5d0a01..ebabd5f 100644
--- a/tests/hazmat/backends/test_openssl.py
+++ b/tests/hazmat/backends/test_openssl.py
@@ -148,6 +148,17 @@
             key_size=512,
             backend=backend
         )
+        with pytest.raises(UnsupportedHash):
+            private_key.signer(
+                padding.PSS(
+                    mgf=padding.MGF1(
+                        algorithm=hashes.SHA256(),
+                        salt_length=padding.MGF1.MAX_LENGTH
+                    )
+                ),
+                hashes.SHA1(),
+                backend
+            )
         public_key = private_key.public_key()
         with pytest.raises(UnsupportedHash):
             public_key.verifier(
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index 955e69c..a1ed895 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -16,6 +16,7 @@
 
 import binascii
 import itertools
+import math
 import os
 
 import pytest
@@ -428,6 +429,80 @@
         signature = signer.finalize()
         assert binascii.hexlify(signature) == example["signature"]
 
+    @pytest.mark.parametrize(
+        "pkcs1_example",
+        _flatten_pkcs1_examples(load_vectors_from_file(
+            os.path.join(
+                "asymmetric", "RSA", "pkcs-1v2-1d2-vec", "pss-vect.txt"),
+            load_pkcs1_vectors
+        ))
+    )
+    def test_pss_signing(self, pkcs1_example, backend):
+        private, public, example = pkcs1_example
+        private_key = rsa.RSAPrivateKey(
+            p=private["p"],
+            q=private["q"],
+            private_exponent=private["private_exponent"],
+            dmp1=private["dmp1"],
+            dmq1=private["dmq1"],
+            iqmp=private["iqmp"],
+            public_exponent=private["public_exponent"],
+            modulus=private["modulus"]
+        )
+        public_key = rsa.RSAPublicKey(
+            public_exponent=public["public_exponent"],
+            modulus=public["modulus"]
+        )
+        signer = private_key.signer(
+            padding.PSS(
+                mgf=padding.MGF1(
+                    algorithm=hashes.SHA1(),
+                    salt_length=padding.MGF1.MAX_LENGTH
+                )
+            ),
+            hashes.SHA1(),
+            backend
+        )
+        signer.update(binascii.unhexlify(example["message"]))
+        signature = signer.finalize()
+        assert len(signature) == math.ceil(private_key.key_size / 8.0)
+        # PSS signatures contain randomness so we can't do an exact
+        # signature check. Instead we'll verify that the signature created
+        # successfully verifies.
+        verifier = public_key.verifier(
+            signature,
+            padding.PSS(
+                mgf=padding.MGF1(
+                    algorithm=hashes.SHA1(),
+                    salt_length=padding.MGF1.MAX_LENGTH
+                )
+            ),
+            hashes.SHA1(),
+            backend
+        )
+        verifier.update(binascii.unhexlify(example["message"]))
+        verifier.verify()
+
+    def test_pss_signing_salt_length_too_long(self, backend):
+        private_key = rsa.RSAPrivateKey.generate(
+            public_exponent=65537,
+            key_size=512,
+            backend=backend
+        )
+        signer = private_key.signer(
+            padding.PSS(
+                mgf=padding.MGF1(
+                    algorithm=hashes.SHA1(),
+                    salt_length=1000000
+                )
+            ),
+            hashes.SHA1(),
+            backend
+        )
+        signer.update(b"failure coming")
+        with pytest.raises(ValueError):
+            signer.finalize()
+
     def test_use_after_finalize(self, backend):
         private_key = rsa.RSAPrivateKey.generate(
             public_exponent=65537,
@@ -468,6 +543,16 @@
             private_key.signer(
                 padding.PKCS1v15(), hashes.SHA256, pretend_backend)
 
+    def test_unsupported_pss_mgf(self, backend):
+        private_key = rsa.RSAPrivateKey.generate(
+            public_exponent=65537,
+            key_size=512,
+            backend=backend
+        )
+        with pytest.raises(UnsupportedAlgorithm):
+            private_key.signer(padding.PSS(mgf=DummyMGF()), hashes.SHA1(),
+                               backend)
+
 
 @pytest.mark.rsa
 class TestRSAVerification(object):