Issue 33995: test_min_max_version in test_ssl.py fails when Python is built against LibreSSL; {min,max}imum_version behavior differs from OpenSSL (original) (raw)

LibreSSL's implementation of the function used to get the minimum and maximum SSL versions supported differs from OpenSSL's.

In short, the issue is in the implementations of SSL_CTX_new - OpenSSL initializes variables ret->{min,max}_proto_version to 0, while LibreSSL initializes corresponding variables to those of the SSL_METHOD given.

As such, when Python is built with LibreSSL, the default values for an instance of ssl.SSLContext are, in the case of ssl.SSLContext(), ctx.minimum_version = <TLSVersion.TLSv1: 769>; ctx.maximum_version = <TLSVersion.TLSv1_2: 771>. This is NOT what test_ssl.py expects; it expects OpenSSL's behavior of initializing the version variables to zero, which ssl.c then translates to PY_PROTO{MIN,MAX}IMUM_SUPPORTED -> ssl.TLSVersion.{MIN,MAX}IMUM_SUPPORTED.

Additionally, LibreSSL, when SSL_CTX_set_{min,max}_proto_version is called with version equal to zero, explicitly sets the minimum / maximum values equal to the minimum / maximum values for the SSL_METHOD it was called with (namely, 769/770/771, in the case of TLS_method()), not the minimum / maximum values supported by the library.

I have sent an email to the LibreSSL mailing list asking for clarification on this point, namely, if this is intended behavior. If it is, there are two ways that come to mind to work around this behavior. Both ways would only be necessary if IS_LIBRESSL.

  1. Skip test_min_max_version.
  2. Instead of testing that ctx is equal to the extremes after it's been set to one of the extremes, test that it's equal to the actual constants. There are two ways this could be accomplished as well: a. Use PY_PROTO_{MIN,MAX}IMUM_AVAILABLE, defined in _ssl.c (this may require the addition of another call to PyModule_AddIntConstant to provide access to the constant from ssl). The downside to this approach is that it assumes that TLS_method(), or whatever SSL_METHOD test_ssl.py uses has lower and upper bounds equal to PY_PROTO{MIN,MAX}IMUM_AVAILABLE, which may not always be the case. b. Access and store the values for ctx.{min,max}imum_value on creation, then reference them after setting. Either of these approaches would likely also have the benefit of removing the need for self.assertIn(ctx.minimum_version, {ssl.TLSVersion.TLSv1_2, ssl.TLSVersion.TLSv1_3}).

The test that failed was:

FAIL: test_min_max_version (test.test_ssl.ContextTests)

Traceback (most recent call last): File "/home/alan/src/cpython/Lib/test/test_ssl.py", line 1066, in test_min_max_version ctx.minimum_version, ssl.TLSVersion.MINIMUM_SUPPORTED AssertionError: <TLSVersion.TLSv1: 769> != <TLSVersion.MINIMUM_SUPPORTED: -2>

Addendum: I found the documentation for ssl.TLSVersion.{MAX,MIN}IMUM_SUPPORTED confusing at first - it took me quite a while to realize what the purpose of the constants were; I would have expected them to contain the maximum and minimum protocol versions supported by the library; I see why it was phrased that way, after having boned up on ssl.py, _ssl.c, and OpenSSL/LibreSSL, but think it could could be made clearer.