Repository: pyca/pyopenssl (python)

Issue:
_lib.RSA_print(bio, rsa, 0) causes segmentation fault
_lib.RSA_print(bio, rsa, 0) in crypto.py 

crashes with a "Segmentation fault: 11" [1] when executing dump_privatekey for the following x509req:

-----BEGIN CERTIFICATE REQUEST-----
MIICoTCCAmECAQAwgZ8xCzAJBgNVBAYTAkdCMRAwDgYDVQQIEwdFbmdsYW5kMRIw
EAYDVQQHEwlDYW1icmlkZ2UxJDAiBgNVBAoTG1RoZSBVbml2ZXJzaXR5IG9mIENh
bWJyaWRnZTEcMBoGA1UECxMTQ29tcHV0ZXIgTGFib3JhdG9yeTEmMCQGA1UEAxMd
d3d3LnB1YmxpY2F0aW9ucy5jbC5jYW0uYWMudWswggG2MIIBKwYHKoZIzjgEATCC
AR4CgYEAo0S4k91lqRq0HyBsHGeWRoJeoWSTmKD6Y5E7vYOOPeTMlGKAZf0UPrDm
5g2oQboWiBUoDs1TQ3f6M9pIhDPrJO9HR2PFKcaw07lopMmaftRmA8D8ktX/RmnC
XSTZ9kzvzI1zAAa9oVAs0MNpZhm05NCKiwn7h0slNz0z/3n3rL0CFQDrPJBz1lFD
+Y/VvJBQoqxxpHi6PQKBgEN24afjpBkD/ZaBgSAIPsMGTAqFVWZaAzuhqGIyxPpG
1vK4HcvPLC2ypCLb6antLYbKHTffL9Wcx9HVLjf8FIt6Yv/VgbObpY86pzmkdCzi
3ndEstMtXjRbbTog2dd2Dh95U3mqHw3Z8ScmxGSHguS64vg4CjN/XmRMBZujnCyK
A4GEAAKBgHPeYvecW6LPeC+o0Ceuz4bqIsXJHsbC8tKwBBppeM/ETrMybsehtPbd
TivaYJFzAoJgTAptIxF1n3f22FzNtpNzd8XDNAbUQm1lsIX5ySed3xn+Mwagqr5I
jsdB1ywrrj/d9/Ha9jyG6lNh3uKTwqUBr/7XU/SWNLjYLefPFgDPoAAwCQYHKoZI
zjgEAwMvADAsAhR7S2+G3gObSZ+XG953HJqC0YkWogIUWdPkEHtY1cID6xNEcjK4
yvb2luU=
-----END CERTIFICATE REQUEST-----

I'm using OSX 10.10.3 with Python 2.7.5

[1] SIGSEGV      11       Core    Invalid memory reference
extracted from http://man7.org/linux/man-pages/man7/signal.7.html


Pull request description:
dump_privatekey with FILETYPE_TEXT only supports RSA keys
FILETYPE_TEXT is terrible but everyone hold their nose

Fixes #259. Sort of. As good as it's gonna get for now.

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 36d1b63..871b1d5 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -33,6 +33,7 @@ Changes:
 ^^^^^^^^
 
 - Added ``OpenSSL.crypto.X509.from_cryptography`` and ``OpenSSL.crypto.X509.to_cryptography`` for converting X.509 certificate to and from pyca/cryptography objects. `#640 <https://github.com/pyca/pyopenssl/pull/640>`_
+- Added ``OpenSSL.crypto.X509Req.from_cryptography``, ``OpenSSL.crypto.X509Req.to_cryptography``, ``OpenSSL.crypto.CRL.from_cryptography``, and ``OpenSSL.crypto.CRL.to_cryptography`` for converting X.509 CSRs and CRLs to and from pyca/cryptography objects. `#645 <https://github.com/pyca/pyopenssl/pull/645>`_
 - Added ``OpenSSL.debug`` that allows to get an overview of used library versions (including linked OpenSSL) and other useful runtime information using ``python -m OpenSSL.debug``.
   `#620 <https://github.com/pyca/pyopenssl/pull/620>`_
 
diff --git a/src/OpenSSL/crypto.py b/src/OpenSSL/crypto.py
index ba918cc..5803ae9 100644
--- a/src/OpenSSL/crypto.py
+++ b/src/OpenSSL/crypto.py
@@ -843,6 +843,39 @@ class X509Req(object):
         # Default to version 0.
         self.set_version(0)
 
+    def to_cryptography(self):
+        """
+        Export as a ``cryptography`` certificate signing request.
+
+        :rtype: ``cryptography.x509.CertificateSigningRequest``
+
+        .. versionadded:: 17.1.0
+        """
+        from cryptography.hazmat.backends.openssl.x509 import (
+            _CertificateSigningRequest
+        )
+        backend = _get_backend()
+        return _CertificateSigningRequest(backend, self._req)
+
+    @classmethod
+    def from_cryptography(cls, crypto_req):
+        """
+        Construct based on a ``cryptography`` *crypto_req*.
+
+        :param crypto_req: A ``cryptography`` X.509 certificate signing request
+        :type crypto_req: ``cryptography.x509.CertificateSigningRequest``
+
+        :rtype: PKey
+
+        .. versionadded:: 17.1.0
+        """
+        if not isinstance(crypto_req, x509.CertificateSigningRequest):
+            raise TypeError("Must be a certificate signing request")
+
+        req = cls()
+        req._req = crypto_req._x509_req
+        return req
+
     def set_pubkey(self, pkey):
         """
         Set the public key of the certificate signing request.
@@ -1804,6 +1837,9 @@ def dump_privatekey(type, pkey, cipher=None, passphrase=None):
     """
     bio = _new_mem_buf()
 
+    if not isinstance(pkey, PKey):
+        raise TypeError("pkey must be a PKey")
+
     if cipher is not None:
         if passphrase is None:
             raise TypeError(
@@ -1824,6 +1860,9 @@ def dump_privatekey(type, pkey, cipher=None, passphrase=None):
     elif type == FILETYPE_ASN1:
         result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey)
     elif type == FILETYPE_TEXT:
+        if _lib.EVP_PKEY_id(pkey._pkey) != _lib.EVP_PKEY_RSA:
+            raise TypeError("Only RSA keys are supported for FILETYPE_TEXT")
+
         rsa = _ffi.gc(
             _lib.EVP_PKEY_get1_RSA(pkey._pkey),
             _lib.RSA_free
@@ -2018,6 +2057,39 @@ class CRL(object):
         crl = _lib.X509_CRL_new()
         self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
 
+    def to_cryptography(self):
+        """
+        Export as a ``cryptography`` CRL.
+
+        :rtype: ``cryptography.x509.CertificateRevocationList``
+
+        .. versionadded:: 17.1.0
+        """
+        from cryptography.hazmat.backends.openssl.x509 import (
+            _CertificateRevocationList
+        )
+        backend = _get_backend()
+        return _CertificateRevocationList(backend, self._crl)
+
+    @classmethod
+    def from_cryptography(cls, crypto_crl):
+        """
+        Construct based on a ``cryptography`` *crypto_crl*.
+
+        :param crypto_crl: A ``cryptography`` certificate revocation list
+        :type crypto_crl: ``cryptography.x509.CertificateRevocationList``
+
+        :rtype: CRL
+
+        .. versionadded:: 17.1.0
+        """
+        if not isinstance(crypto_crl, x509.CertificateRevocationList):
+            raise TypeError("Must be a certificate revocation list")
+
+        crl = cls()
+        crl._crl = crypto_crl._x509_crl
+        return crl
+
     def get_revoked(self):
         """
         Return the revocations in this certificate revocation list.


Test patch:
diff --git a/tests/test_crypto.py b/tests/test_crypto.py
index 2bcc933..916186b 100644
--- a/tests/test_crypto.py
+++ b/tests/test_crypto.py
@@ -1420,6 +1420,24 @@ class TestX509Req(_PKeyInteractionTestsMixin):
         request.sign(pkey, GOOD_DIGEST)
         assert request.verify(pkey)
 
+    def test_convert_from_cryptography(self):
+        crypto_req = x509.load_pem_x509_csr(
+            cleartextCertificateRequestPEM, backend
+        )
+        req = X509Req.from_cryptography(crypto_req)
+        assert isinstance(req, X509Req)
+
+    def test_convert_from_cryptography_unsupported_type(self):
+        with pytest.raises(TypeError):
+            X509Req.from_cryptography(object())
+
+    def test_convert_to_cryptography_key(self):
+        req = load_certificate_request(
+            FILETYPE_PEM, cleartextCertificateRequestPEM
+        )
+        crypto_req = req.to_cryptography()
+        assert isinstance(crypto_req, x509.CertificateSigningRequest)
+
 
 class TestX509(_PKeyInteractionTestsMixin):
     """
@@ -2613,6 +2631,20 @@ class TestFunction(object):
         with pytest.raises(TypeError):
             dump_privatekey(FILETYPE_PEM, key, cipher=GOOD_CIPHER)
 
+    def test_dump_privatekey_not_rsa_key(self):
+        """
+        `dump_privatekey` raises `TypeError` if called with a key that is
+        not RSA.
+        """
+        key = PKey()
+        key.generate_key(TYPE_DSA, 512)
+        with pytest.raises(TypeError):
+            dump_privatekey(FILETYPE_TEXT, key)
+
+    def test_dump_privatekey_invalid_pkey(self):
+        with pytest.raises(TypeError):
+            dump_privatekey(FILETYPE_TEXT, object())
+
     def test_dump_privatekey_unknown_cipher(self):
         """
         `dump_privatekey` raises `ValueError` if called with an unrecognized
@@ -3437,6 +3469,20 @@ class TestCRL(object):
         assert err.value.args[0][2] == 'unable to get certificate CRL'
         assert err.value.certificate.get_subject().CN == 'intermediate-service'
 
+    def test_convert_from_cryptography(self):
+        crypto_crl = x509.load_pem_x509_crl(crlData, backend)
+        crl = CRL.from_cryptography(crypto_crl)
+        assert isinstance(crl, CRL)
+
+    def test_convert_from_cryptography_unsupported_type(self):
+        with pytest.raises(TypeError):
+            CRL.from_cryptography(object())
+
+    def test_convert_to_cryptography_key(self):
+        crl = load_crl(FILETYPE_PEM, crlData)
+        crypto_crl = crl.to_cryptography()
+        assert isinstance(crypto_crl, x509.CertificateRevocationList)
+
 
 class TestX509StoreContext(object):
     """