Repository: pyca/pyopenssl
Pull request #744 (2018-03-25 20:15:15)

Make sure a NotImplementedError is always raised on Connection.makefile()
With this patch, code which calls (for example) `conn.makefile('rb')` will get a `NotImplementedError` instead of a confusing `TypeError`:

    TypeError: makefile() takes 1 positional argument but 2 were given


Patch:
diff --git a/.travis.yml b/.travis.yml
index b2499b5..60731be 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,11 +14,11 @@ matrix:
   include:
   - language: generic
     os: osx
-    osx_image: xcode8.3
+    osx_image: xcode9.3
     env: TOXENV=py27
   - language: generic
     os: osx
-    osx_image: xcode8.3
+    osx_image: xcode9.3
     env: TOXENV=py27 OPENSSL=1.1.0
   - python: "2.7" # these are just to make travis's UI a bit prettier
     env: TOXENV=py27
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 080b30e..0d8765f 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -4,15 +4,15 @@ Changelog
 Versions are year-based with a strict backward-compatibility policy.
 The third digit is only for regressions.
 
-17.6.0 (UNRELEASED)
+18.0.0 (UNRELEASED)
 -------------------
 
 
 Backward-incompatible changes:
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-* The minimum ``cryptography`` version is now 2.2.1.
-* Support for Python 2.6 has been dropped.
+- The minimum ``cryptography`` version is now 2.2.1.
+- Support for Python 2.6 has been dropped.
 
 
 Deprecations:
@@ -24,7 +24,10 @@ Deprecations:
 Changes:
 ^^^^^^^^
 
-*none*
+- ``OpenSSL.SSL.Connection`` now sets ``SSL_MODE_AUTO_RETRY`` by default.
+  `#753 <https://github.com/pyca/pyopenssl/pull/753>`_
+- Added ``Context.set_tlsext_use_srtp`` to enable negotiation of SRTP keying material.
+  `#734 <https://github.com/pyca/pyopenssl/pull/734>`_
 
 
 ----
@@ -36,7 +39,7 @@ Changes:
 Backward-incompatible changes:
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-* The minimum ``cryptography`` version is now 2.1.4.
+- The minimum ``cryptography`` version is now 2.1.4.
 
 
 Deprecations:
diff --git a/MANIFEST.in b/MANIFEST.in
index 4ff05bf..036bcdb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,5 @@
 include             LICENSE MANIFEST.in *.rst tox.ini .coveragerc
-exclude             leakcheck
+exclude             leakcheck codecov.yml
 recursive-include   tests           *.py
 recursive-include   doc             *
 recursive-include   examples        *
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..69cb760
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,1 @@
+comment: false
diff --git a/doc/ChangeLog_old.txt b/doc/ChangeLog_old.txt
index 214a26d..1a16d72 100644
--- a/doc/ChangeLog_old.txt
+++ b/doc/ChangeLog_old.txt
@@ -740,7 +740,7 @@ found at <https://pyopenssl.readthedocs.io/en/latest/changelog.html>.
 2002-06-13  Martin Sjögren  <martin@strakt.com>
 
 	* src/ssl/context.c: Changed global_verify_callback so that it uses
-	  PyObject_IsTrue instead of requring ints.
+	  PyObject_IsTrue instead of requiring ints.
 	* Added pymemcompat.h to make the memory management uniform and
 	  backwards-compatible.
 	* src/util.h: Added conditional definition of PyModule_AddObject and
diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py
index f3c9db0..4c708ba 100644
--- a/src/OpenSSL/SSL.py
+++ b/src/OpenSSL/SSL.py
@@ -1371,6 +1371,21 @@ class Context(object):
         _lib.SSL_CTX_set_tlsext_servername_callback(
             self._context, self._tlsext_servername_callback)
 
+    def set_tlsext_use_srtp(self, profiles):
+        """
+        Enable support for negotiating SRTP keying material.
+
+        :param bytes profiles: A colon delimited list of protection profile
+            names, like ``b'SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32'``.
+        :return: None
+        """
+        if not isinstance(profiles, bytes):
+            raise TypeError("profiles must be a byte string.")
+
+        _openssl_assert(
+            _lib.SSL_CTX_set_tlsext_use_srtp(self._context, profiles) == 0
+        )
+
     @_requires_npn
     def set_npn_advertise_callback(self, callback):
         """
@@ -1529,6 +1544,11 @@ class Connection(object):
 
         ssl = _lib.SSL_new(context._context)
         self._ssl = _ffi.gc(ssl, _lib.SSL_free)
+        # We set SSL_MODE_AUTO_RETRY to handle situations where OpenSSL returns
+        # an SSL_ERROR_WANT_READ when processing a non-application data packet
+        # even though there is still data on the underlying transport.
+        # See https://github.com/openssl/openssl/issues/6234 for more details.
+        _lib.SSL_set_mode(self._ssl, _lib.SSL_MODE_AUTO_RETRY)
         self._context = context
         self._app_data = None
 
@@ -2019,7 +2039,7 @@ class Connection(object):
             result.append(pyname)
         return result
 
-    def makefile(self):
+    def makefile(self, *args, **kwargs):
         """
         The makefile() method is not implemented, since there is no dup
         semantics for SSL connections


Test patch:
diff --git a/tests/test_crypto.py b/tests/test_crypto.py
index 5642120..d1c261b 100644
--- a/tests/test_crypto.py
+++ b/tests/test_crypto.py
@@ -573,7 +573,7 @@ def x509_data():
     """
     # Basic setup stuff to generate a certificate
     pkey = PKey()
-    pkey.generate_key(TYPE_RSA, 384)
+    pkey.generate_key(TYPE_RSA, 512)
     req = X509Req()
     req.set_pubkey(pkey)
     # Authority good you have.
@@ -917,7 +917,7 @@ class TestPKey(object):
         `PKey.generate_key` generates an RSA key when passed `TYPE_RSA` as a
         type and a reasonable number of bits.
         """
-        bits = 128
+        bits = 512
         key = PKey()
         key.generate_key(TYPE_RSA, bits)
         assert key.type() == TYPE_RSA
diff --git a/tests/test_ssl.py b/tests/test_ssl.py
index 1d6bb9f..b09fce7 100644
--- a/tests/test_ssl.py
+++ b/tests/test_ssl.py
@@ -519,7 +519,7 @@ class TestContext(object):
         `Context.use_privatekey` takes an `OpenSSL.crypto.PKey` instance.
         """
         key = PKey()
-        key.generate_key(TYPE_RSA, 128)
+        key.generate_key(TYPE_RSA, 512)
         ctx = Context(TLSv1_METHOD)
         ctx.use_privatekey(key)
         with pytest.raises(TypeError):
@@ -540,7 +540,7 @@ class TestContext(object):
         arguments does not raise an exception.
         """
         key = PKey()
-        key.generate_key(TYPE_RSA, 128)
+        key.generate_key(TYPE_RSA, 512)
 
         with open(pemfile, "wt") as pem:
             pem.write(
@@ -843,7 +843,7 @@ class TestContext(object):
         passphrase.  Return the path to the new file.
         """
         key = PKey()
-        key.generate_key(TYPE_RSA, 128)
+        key.generate_key(TYPE_RSA, 512)
         pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", passphrase)
         with open(tmpfile, 'w') as fObj:
             fObj.write(pem.decode('ascii'))
@@ -1202,6 +1202,7 @@ class TestContext(object):
         client.connect(("encrypted.google.com", 443))
         clientSSL = Connection(context, client)
         clientSSL.set_connect_state()
+        clientSSL.set_tlsext_host_name(b"encrypted.google.com")
         clientSSL.do_handshake()
         clientSSL.send(b"GET / HTTP/1.0\r\n\r\n")
         assert clientSSL.recv(1024)
@@ -1482,7 +1483,7 @@ class TestContext(object):
     @pytest.mark.parametrize('callback', [None, 1.0, 'mode', ('foo', 'bar')])
     def test_set_verify_wrong_callable_arg(self, callback):
         """
-        `Context.set_verify` raises `TypeError` if the the second argument
+        `Context.set_verify` raises `TypeError` if the second argument
         is not callable.
         """
         context = Context(TLSv1_METHOD)
@@ -1592,6 +1593,35 @@ class TestContext(object):
         store = context.get_cert_store()
         assert isinstance(store, X509Store)
 
+    def test_set_tlsext_use_srtp_not_bytes(self):
+        """
+        `Context.set_tlsext_use_srtp' enables negotiating SRTP keying material.
+
+        It raises a TypeError if the list of profiles is not a byte string.
+        """
+        context = Context(TLSv1_METHOD)
+        with pytest.raises(TypeError):
+            context.set_tlsext_use_srtp(text_type('SRTP_AES128_CM_SHA1_80'))
+
+    def test_set_tlsext_use_srtp_invalid_profile(self):
+        """
+        `Context.set_tlsext_use_srtp' enables negotiating SRTP keying material.
+
+        It raises an Error if the call to OpenSSL fails.
+        """
+        context = Context(TLSv1_METHOD)
+        with pytest.raises(Error):
+            context.set_tlsext_use_srtp(b'SRTP_BOGUS')
+
+    def test_set_tlsext_use_srtp_valid(self):
+        """
+        `Context.set_tlsext_use_srtp' enables negotiating SRTP keying material.
+
+        It does not return anything.
+        """
+        context = Context(TLSv1_METHOD)
+        assert context.set_tlsext_use_srtp(b'SRTP_AES128_CM_SHA1_80') is None
+
 
 class TestServerNameCallback(object):
     """