[Python-ideas] Unified TLS API for Python

Robert Collins robertc at robertcollins.net
Thu Feb 2 06:03:22 EST 2017


Nice. What's needed to move this forward?

On 2 Feb. 2017 11:38 pm, "Cory Benfield" <cory at lukasa.co.uk> wrote:

> All,
>
> A recent change in the Fastly policy has led the distutils-sig to discover
> that there is a very real risk of many macOS installations of Python being
> entirely unable to access PyPI using pip, due to the fact that they are
> linked against the ancient system OpenSSL (see [1]). This problem
> ultimately stems from the fact that Python has never exposed the ability to
> use TLS backends other than OpenSSL in the standard library. As Christian
> and I discussed back at PyCon US 2015, the ssl module would more properly
> be called the openssl module, due to exposing many OpenSSL-specific
> concepts and behaviours in its API. This has meant that the Python
> ecosystem is overwhelmingly also an OpenSSL ecosystem, which is problematic
> on Windows and macOS (and Unices that for whatever reason aren’t interested
> in shipping an OpenSSL), as it has meant that Python needs to bring its own
> OpenSSL, and that it is troublesome to interact with the system trust
> database.
>
> The first step to resolving this would be to provide a new module that
> exposes TLS concepts in a much more generic manner. There are two possible
> approaches to this. The first is to have this module be a generic concrete
> implementation that can be compiled against multiple TLS backends (like
> curl). This would require that all the relevant bindings be built into the
> standard library from the get-go, which provides a substantial maintenance
> burden on a team of people that are already understaffed maintaining the
> ssl module alone. The second approach is to define a generic high-level TLS
> interface that provides a minimal usable interface that can be implemented
> by both first- and third-party modules. This would allow the standard
> library to continue to ship with only exactly what it needs (for example,
> OpenSSL, SecureTransport and SChannel), but would give those who want to
> use more esoteric TLS choices (NSS, GnuTLS, mbedTLS, and BoringSSL are some
> examples) an API that they can implement that will guarantee that complying
> modules can use the appropriate TLS backend.
>
> To that end, I’ve composed a draft PEP that would define this API. The
> current copy can be found on GitHub[2] and is also provided below. This
> draft PEP has been under discussion in the Python Security-SIG since the
> start of January, and has gone through a number of revisions. This is the
> next step in moving this forward: exposing the wider Python development
> community to the PEP for further revision before it is proposed to
> python-dev.
>
> Please note that this proposal is not intended to resolve the current
> problem pip has with TLSv1.2, and so is not subject to the tight time
> constraints associated with that problem. That problem will be solved by
> building a temporary shim into urllib3 that can use SecureTransport
> bindings on the Mac. This proposal is intended as a solution to the
> more-general problem of supporting different TLS backends in Python, and so
> is larger in scope and less urgent.
>
> I should also mention that there will be a tendency to want to make this
> API all things to all people from the get-go. I’m going to strongly resist
> attempts to extend this API too much, because each additional bit of API
> surface makes it harder for us to encourage module authors to conform to
> this API. I will encourage people to extend this API over time as needed,
> but to begin with I think it is most important that basic TLS clients and
> servers can be constructed with this API. More specialised features should
> be considered future enhancements, rather than being tacked on to this
> initial PEP.
>
> Please let me know what you think.
>
> Cory
>
> [1]: https://mail.python.org/pipermail/distutils-sig/2017-
> January/029970.html
> [2]: https://github.com/Lukasa/peps/pull/1
>
>>
> PEP: XXX
> Title: TLS Abstract Base Classes
> Version: $Revision$
> Last-Modified: $Date$
> Author: Cory Benfield <cory at lukasa.co.uk>
> Status: Draft
> Type: Standards Track
> Content-Type: text/x-rst
> Created: 17-Oct-2016
> Python-Version: 3.7
> Post-History: 30-Aug-2002
>
>
> Abstract
> ========
>
> This PEP would define a standard TLS interface in the form of a collection
> of
> abstract base classes. This interface would allow Python implementations
> and
> third-party libraries to provide bindings to TLS libraries other than
> OpenSSL
> that can be used by tools that expect the interface provided by the Python
> standard library, with the goal of reducing the dependence of the Python
> ecosystem on OpenSSL.
>
>
> Rationale
> =========
>
> In the 21st century it has become increasingly clear that robust and
> user-friendly TLS support is an extremely important part of the ecosystem
> of
> any popular programming language. For most of its lifetime, this role in
> the
> Python ecosystem has primarily been served by the `ssl module`_, which
> provides
> a Python API to the `OpenSSL library`_.
>
> Because the ``ssl`` module is distributed with the Python standard
> library, it
> has become the overwhelmingly most-popular method for handling TLS in
> Python.
> An extraordinary majority of Python libraries, both in the standard
> library and
> on the Python Package Index, rely on the ``ssl`` module for their TLS
> connectivity.
>
> Unfortunately, the preeminence of the ``ssl`` module has had a number of
> unforeseen side-effects that have had the effect of tying the entire Python
> ecosystem tightly to OpenSSL. This has forced Python users to use OpenSSL
> even
> in situations where it may provide a worse user experience than
> alternative TLS
> implementations, which imposes a cognitive burden and makes it hard to
> provide
> "platform-native" experiences.
>
>
> Problems
> --------
>
> The fact that the ``ssl`` module is build into the standard library has
> meant
> that all standard-library Python networking libraries are entirely reliant
> on
> the OpenSSL that the Python implementation has been linked against. This
> leads to the following issues:
>
> * It is difficult to take advantage of new, higher-security TLS without
>   recompiling Python to get a new OpenSSL. While there are third-party
> bindings
>   to OpenSSL (e.g. `pyOpenSSL`_), these need to be shimmed into a format
> that
>   the standard library understands, forcing projects that want to use them
> to
>   maintain substantial compatibility layers.
>
> * For Windows distributions of Python, they need to be shipped with a copy
> of
>   OpenSSL. This puts the CPython development team in the position of being
>   OpenSSL redistributors, potentially needing to ship security updates to
> the
>   Windows Python distributions when OpenSSL vulnerabilities are released.
>
> * For macOS distributions of Python, they need either to be shipped with a
> copy
>   of OpenSSL or linked against the system OpenSSL library. Apple has
> formally
>   deprecated linking against the system OpenSSL library, and even if they
> had
>   not, that library version has been unsupported by upstream for nearly one
>   year as of the time of writing. The CPython development team has started
>   shipping newer OpenSSLs with the Python available from python.org, but
> this
>   has the same problem as with Windows.
>
> * Many systems, including but not limited to Windows and macOS, do not make
>   their system certificate stores available to OpenSSL. This forces users
> to
>   either obtain their trust roots from elsewhere (e.g. `certifi`_) or to
>   attempt to export their system trust stores in some form.
>
>   Relying on `certifi`_ is less than ideal, as most system administrators
> do
>   not expect to receive security-critical software updates from PyPI.
>   Additionally, it is not easy to extend the `certifi`_ trust bundle to
> include
>   custom roots, or to centrally manage trust using the `certifi`_ model.
>
>   Even in situations where the system certificate stores are made
> available to
>   OpenSSL in some form, the experience is still sub-standard, as OpenSSL
> will
>   perform different validation checks than the platform-native TLS
>   implementation. This can lead to users experiencing different behaviour
> on
>   their browsers or other platform-native tools than they experience in
> Python,
>   with little or no recourse to resolve the problem.
>
> * Users may wish to integrate with TLS libraries other than OpenSSL for
> many
>   other reasons, such as OpenSSL missing features (e.g. TLS 1.3 support),
> or
>   because OpenSSL is simply too large and unweildy for the platform (e.g.
> for
>   embedded Python). Those users are left with the requirement to use
>   third-party networking libraries that can interact with their preferred
> TLS
>   library or to shim their preferred library into the OpenSSL-specific
> ``ssl``
>   module API.
>
> Additionally, the ``ssl`` module as implemented today limits the ability of
> CPython itself to add support for alternative TLS backends, or remove
> OpenSSL
> support entirely, should either of these become necessary or useful. The
> ``ssl`` module exposes too many OpenSSL-specific function calls and
> features to
> easily map to an alternative TLS backend.
>
>
> Proposal
> ========
>
> This PEP proposes to introduce a few new Abstract Base Classes in Python
> 3.7 to
> provide TLS functionality that is not so strongly tied to OpenSSL. It also
> proposes to update standard library modules to use only the interface
> exposed
> by these abstract base classes wherever possible. There are three goals
> here:
>
> 1. To provide a common API surface for both core and third-party
> developers to
>    target their TLS implementations to. This allows TLS developers to
> provide
>    interfaces that can be used by most Python code, and allows network
>    developers to have an interface that they can target that will work
> with a
>    wide range of TLS implementations.
> 2. To provide an API that has few or no OpenSSL-specific concepts leak
> through.
>    The ``ssl`` module today has a number of warts caused by leaking OpenSSL
>    concepts through to the API: the new ABCs would remove those specific
>    concepts.
> 3. To provide a path for the core development team to make OpenSSL one of
> many
>    possible TLS backends, rather than requiring that it be present on a
> system
>    in order for Python to have TLS support.
>
> The proposed interface is laid out below.
>
>
> Abstract Base Classes
> ---------------------
>
> There are several interfaces that require standardisation. Those interfaces
> are:
>
> 1. Configuring TLS, currently implemented by the `SSLContext`_ class in the
>    ``ssl`` module.
> 2. Wrapping a socket object, currently implemented by the `SSLSocket`_
> class
>    in the ``ssl`` module.
> 3. Providing an in-memory buffer for doing in-memory encryption or
> decryption
>    with no actual I/O (necessary for asynchronous I/O models), currently
>    implemented by the `SSLObject`_ class in the ``ssl`` module.
> 4. Applying TLS configuration to the wrapping objects in (2) and (3).
> Currently
>    this is also implemented by the `SSLContext`_ class in the ``ssl``
> module.
> 5. Specifying TLS cipher suites. There is currently no code for doing this
> in
>    the standard library: instead, the standard library uses OpenSSL cipher
>    suite strings.
> 6. Specifying application-layer protocols that can be negotiated during the
>    TLS handshake.
> 7. Specifying TLS versions.
> 8. Reporting errors to the caller, currently implemented by the `SSLError`_
>    class in the ``ssl`` module.
> 9. Specifying certificates to load, either as client or server
> certificates.
> 10. Specifying which trust database should be used to validate certificates
>     presented by a remote peer.
> 11. Finding a way to get hold of these interfaces at run time.
>
> While it is technically possible to define (2) in terms of (3), for the
> sake of
> simplicity it is easier to define these as two separate ABCs.
> Implementations
> are of course free to implement the concrete subclasses however they see
> fit.
>
> Obviously, (5) doesn't require an abstract base class: instead, it
> requires a
> richer API for configuring supported cipher suites that can be easily
> updated
> with supported cipher suites for different implementations.
>
> (9) is a thorny problem, becuase in an ideal world the private keys
> associated
> with these certificates would never end up in-memory in the Python process
> (that is, the TLS library would collaborate with a Hardware Security Module
> (HSM) to provide the private key in such a way that it cannot be extracted
> from
> process memory). Thus, we need to provide an extensible model of providing
> certificates that allows concrete implementations the ability to provide
> this
> higher level of security, while also allowing a lower bar for those
> implementations that cannot. This lower bar would be the same as the status
> quo: that is, the certificate may be loaded from an in-memory buffer or
> from a
> file on disk.
>
> (10) also represents an issue because different TLS implementations vary
> wildly
> in how they allow users to select trust stores. Some implementations have
> specific trust store formats that only they can use (such as the OpenSSL CA
> directory format that is created by ``c_rehash``), and others may not
> allow you
> to specify a trust store that does not include their default trust store.
>
> For this reason, we need to provide a model that assumes very little about
> the
> form that trust stores take. The "Trust Store" section below goes into more
> detail about how this is achieved.
>
> Finally, this API will split the responsibilities currently assumed by the
> `SSLContext`_ object: specifically, the responsibility for holding and
> managing
> configuration and the responsibility for using that configuration to build
> wrapper objects.
>
> This is necessarily primarily for supporting functionality like Server Name
> Indication (SNI). In OpenSSL (and thus in the ``ssl`` module), the server
> has
> the ability to modify the TLS configuration in response to the client
> telling
> the server what hostname it is trying to reach. This is mostly used to
> change
> certificate chain so as to present the correct TLS certificate chain for
> the
> given hostname. The specific mechanism by which this is done is by
> returning
> a new `SSLContext`_ object with the appropriate configuration.
>
> This is not a model that maps well to other TLS implementations. Instead,
> we
> need to make it possible to provide a return value from the SNI callback
> that
> can be used to indicate what configuration changes should be made. This
> means
> providing an object that can hold TLS configuration. This object needs to
> be
> applied to specific TLSWrappedBuffer, and TLSWrappedSocket objects.
>
> For this reason, we split the responsibility of `SSLContext`_ into two
> separate
> objects. The ``TLSConfiguration`` object is an object that acts as
> container
> for TLS configuration: the ``ClientContext`` and ``ServerContext`` objects
> are
> objects that are instantiated with a ``TLSConfiguration`` object. Both
> objects
> would be immutable.
>
> Configuration
> ~~~~~~~~~~~~~
>
> The ``TLSConfiguration`` concrete class defines an object that can hold and
> manage TLS configuration. The goals of this class are as follows:
>
> 1. To provide a method of specifying TLS configuration that avoids the
> risk of
>    errors in typing (this excludes the use of a simple dictionary).
> 2. To provide an object that can be safely compared to other configuration
>    objects to detect changes in TLS configuration, for use with the SNI
>    callback.
>
> This class is not an ABC, primarily because it is not expected to have
> implementation-specific behaviour. The responsibility for transforming a
> ``TLSConfiguration`` object into a useful set of configuration for a given
> TLS
> implementation belongs to the Context objects discussed below.
>
> This class has one other notable property: it is immutable. This is a
> desirable
> trait for a few reasons. The most important one is that it allows these
> objects
> to be used as dictionary keys, which is potentially extremely valuable for
> certain TLS backends and their SNI configuration. On top of this, it frees
> implementations from needing to worry about their configuration objects
> being
> changed under their feet, which allows them to avoid needing to carefully
> synchronize changes between their concrete data structures and the
> configuration object.
>
> This object is extendable: that is, future releases of Python may add
> configuration fields to this object as they become useful. For
> backwards-compatibility purposes, new fields are only appended to this
> object.
> Existing fields will never be removed, renamed, or reordered.
>
> The ``TLSConfiguration`` object would be defined by the following code::
>
>     ServerNameCallback = Callable[[TLSBufferObject, Optional[str],
> TLSConfiguration], Any]
>
>
>     _configuration_fields = [
>         'validate_certificates',
>         'certificate_chain',
>         'ciphers',
>         'inner_protocols',
>         'lowest_supported_version',
>         'highest_supported_version',
>         'trust_store',
>         'sni_callback',
>     ]
>
>
>     _DEFAULT_VALUE = object()
>
>
>     class TLSConfiguration(namedtuple('TLSConfiguration',
> _configuration_fields)):
>         """
>         An immutable TLS Configuration object. This object has the
> following
>         properties:
>
>         :param validate_certificates bool: Whether to validate the TLS
>             certificates. This switch operates at a very broad scope:
> either
>             validation is enabled, in which case all forms of validation
> are
>             performed including hostname validation if possible, or
> validation
>             is disabled, in which case no validation is performed.
>
>             Not all backends support having their certificate validation
>             disabled. If a backend does not support having their
> certificate
>             validation disabled, attempting to set this property to
> ``False``
>             will throw a ``TLSError`` when this object is passed into a
>             context object.
>
>         :param certificate_chain Tuple[Tuple[Certificate],PrivateKey]: The
>             certificate, intermediate certificate, and the corresponding
>             private key for the leaf certificate. These certificates will
> be
>             offered to the remote peer during the handshake if required.
>
>             The first Certificate in the list must be the leaf
> certificate. All
>             subsequent certificates will be offered as intermediate
> additional
>             certificates.
>
>         :param ciphers Tuple[CipherSuite]:
>             The available ciphers for TLS connections created with this
>             configuration, in priority order.
>
>         :param inner_protocols Tuple[Union[NextProtocol, bytes]]:
>             Protocols that connections created with this configuration
> should
>             advertise as supported during the TLS handshake. These may be
>             advertised using either or both of ALPN or NPN. This list of
>             protocols should be ordered by preference.
>
>         :param lowest_supported_version TLSVersion:
>             The minimum version of TLS that should be allowed on TLS
>             connections using this configuration.
>
>         :param highest_supported_version TLSVersion:
>             The maximum version of TLS that should be allowed on TLS
>             connections using this configuration.
>
>         :param trust_store TrustStore:
>             The trust store that connections using this configuration will
> use
>             to validate certificates.
>
>         :param sni_callback Optional[ServerNameCallback]:
>             A callback function that will be called after the TLS Client
> Hello
>             handshake message has been received by the TLS server when the
> TLS
>             client specifies a server name indication.
>
>             Only one callback can be set per ``TLSConfiguration``. If the
>             ``sni_callback`` is ``None`` then the callback is disabled. If
> the
>             ``TLSConfiguration`` is used for a ``ClientContext`` then this
>             setting will be ignored.
>
>             The ``callback`` function will be called with three arguments:
> the
>             first will be the ``TLSBufferObject`` for the connection; the
>             second will be a string that represents the server name that
> the
>             client is intending to communicate (or ``None`` if the TLS
> Client
>             Hello does not contain a server name); and the third argument
> will
>             be the original ``Context``. The server name argument will be
> the
>             IDNA *decoded* server name.
>
>             The ``callback`` must return a ``TLSConfiguration`` to allow
>             negotiation to continue. Other return values signal errors.
>             Attempting to control what error is signaled by the underlying
> TLS
>             implementation is not specified in this API, but is up to the
>             concrete implementation to handle.
>
>             The Context will do its best to apply the ``TLSConfiguration``
>             changes from its original configuration to the incoming
> connection.
>             This will usually include changing the certificate chain, but
> may
>             also include changes to allowable ciphers or any other
>             configuration settings.
>         """
>         __slots__ = ()
>
>         def __new__(cls, validate_certificates=None: Optional[bool],
>                          certificate_chain=None: Optional[Tuple[Tuple[Certificate],
> PrivateKey]],
>                          ciphers=None: Optional[Tuple[CipherSuite]],
>                          inner_protocols=None: Optional[Tuple[Union[NextProtocol,
> bytes]]],
>                          lowest_supported_version=None:
> Optional[TLSVersion],
>                          highest_supported_version=None:
> Optional[TLSVersion],
>                          trust_store=None: Optional[TrustStore],
>                          sni_callback=None: Optional[ServerNameCallback]):
>
>             if validate_certificates is None:
>                 validate_certificates = True
>
>             if ciphers is None:
>                 ciphers = DEFAULT_CIPHER_LIST
>
>             if inner_protocols is None:
>                 inner_protocols = []
>
>             if lowest_supported_version is None:
>                 lowest_supported_version = TLSVersion.TLSv1
>
>             if highest_supported_version is None:
>                 highest_supported_version = TLSVersion.MAXIMUM_SUPPORTED
>
>             return super().__new__(
>                 cls, validate_certificates, certificate_chain, ciphers,
>                 inner_protocols, lowest_supported_version,
>                 highest_supported_version, trust_store, sni_callback
>             )
>
>         def update(self, validate_certificates=_DEFAULT_VALUE,
>                          certificate_chain=_DEFAULT_VALUE,
>                          ciphers=_DEFAULT_VALUE,
>                          inner_protocols=_DEFAULT_VALUE,
>                          lowest_supported_version=_DEFAULT_VALUE,
>                          highest_supported_version=_DEFAULT_VALUE,
>                          trust_store=_DEFAULT_VALUE,
>                          sni_callback=_DEFAULT_VALUE):
>             """
>             Create a new ``TLSConfiguration``, overriding some of the
> settings
>             on the original configuration with the new settings.
>             """
>             if validate_certificates is _DEFAULT_VALUE:
>                 validate_certificates = self.validate_certificates
>
>             if certificate_chain is _DEFAULT_VALUE:
>                 certificate_chain = self.certificate_chain
>
>             if ciphers is _DEFAULT_VALUE:
>                 ciphers = self.ciphers
>
>             if inner_protocols is _DEFAULT_VALUE:
>                 inner_protocols = self.inner_protocols
>
>             if lowest_supported_version is _DEFAULT_VALUE:
>                 lowest_supported_version = self.lowest_supported_version
>
>             if highest_supported_version is _DEFAULT_VALUE:
>                 highest_supported_version = self.highest_supported_version
>
>             if trust_store is _DEFAULT_VALUE:
>                 trust_store = self.trust_store
>
>             if sni_callback is _DEFAULT_VALUE:
>                 sni_callback = self.sni_callback
>
>             return self.__class__(
>                 validate_certificates, certificate_chain, ciphers,
>                 inner_protocols, lowest_supported_version,
>                 highest_supported_version, trust_store, sni_callback
>             )
>
>
>
> Context
> ~~~~~~~
>
> We define two Context abstract base classes. These ABCs define objects that
> allow configuration of TLS to be applied to specific connections. They can
> be
> thought of as factories for ``TLSWrappedSocket`` and ``TLSWrappedBuffer``
> objects.
>
> Unlike the current ``ssl`` module, we provide two context classes instead
> of
> one. Specifically, we provide the ``ClientContext`` and ``ServerContext``
> classes. This simplifies the APIs (for example, there is no sense in the
> server
> providing the ``server_hostname`` parameter to
> ``ssl.SSLContext.wrap_socket``,
> but because there is only one context class that parameter is still
> available),
> and ensures that implementations know as early as possible which side of a
> TLS
> connection they will serve. Additionally, it allows implementations to
> opt-out
> of one or either side of the connection. For example, SChannel on macOS is
> not
> really intended for server use and has an enormous amount of functionality
> missing for server-side use. This would allow SChannel implementations to
> simply not define a concrete subclass of ``ServerContext`` to signal their
> lack
> of support.
>
> As much as possible implementers should aim to make these classes
> immutable:
> that is, they should prefer not to allow users to mutate their internal
> state
> directly, instead preferring to create new contexts from new
> TLSConfiguration
> objects. Obviously, the ABCs cannot enforce this constraint, and so they
> do not
> attempt to.
>
> The ``Context`` abstract base class has the following class definition::
>
>     TLSBufferObject = Union[TLSWrappedSocket, TLSWrappedBuffer]
>
>
>     class _BaseContext(metaclass=ABCMeta):
>         @abstractmethod
>         def __init__(self, configuration: TLSConfiguration):
>             """
>             Create a new context object from a given TLS configuration.
>             """
>
>         @property
>         @abstractmethod
>         def configuration(self) -> TLSConfiguration:
>             """
>             Returns the TLS configuration that was used to create the
> context.
>             """
>
>
>     class ClientContext(_BaseContext):
>         @abstractmethod
>         def wrap_socket(self,
>                         socket: socket.socket,
>                         server_hostname: Optional[str],
>                         auto_handshake=True: bool) -> TLSWrappedSocket:
>             """
>             Wrap an existing Python socket object ``socket`` and return a
>             ``TLSWrappedSocket`` object. ``socket`` must be a
> ``SOCK_STREAM``
>             socket: all other socket types are unsupported.
>
>             The returned SSL socket is tied to the context, its settings
> and
>             certificates.
>
>             The parameter ``server_hostname`` specifies the hostname of the
>             service which we are connecting to. This allows a single
> server to
>             host multiple SSL-based services with distinct certificates,
> quite
>             similarly to HTTP virtual hosts. This is also used to validate
> the
>             TLS certificate for the given hostname. If hostname validation
> is
>             not desired, then pass ``None`` for this parameter.
>
>             The parameter ``auto_handshake`` specifies whether to do the
> SSL
>             handshake automatically after doing a ``socket.connect()``, or
>             whether the application program will call it explicitly, by
>             invoking the ``TLSWrappedSocket.do_handshake()`` method.
> Calling
>             ``TLSWrappedSocket.do_handshake()`` explicitly gives the
> program
>             control over the blocking behavior of the socket I/O involved
> in
>             the handshake.
>             """
>
>         @abstractmethod
>         def wrap_buffers(self, incoming: Any, outgoing: Any,
>                          server_hostname: Optional[str]) ->
> TLSWrappedBuffer:
>             """
>             Wrap a pair of buffer objects (``incoming`` and ``outgoing``)
> to
>             create an in-memory stream for TLS. The SSL routines will read
> data
>             from ``incoming`` and decrypt it, and write encrypted data to
>             ``outgoing``.
>
>             The buffer objects must be either file objects or objects that
>             implement the buffer protocol.
>
>             The ``server_hostname`` parameter has the same meaning as in
>             ``wrap_socket``.
>             """
>
>
>     class ServerContext(_BaseContext):
>         @abstractmethod
>         def wrap_socket(self, socket: socket.socket,
>                         auto_handshake=True: bool) -> TLSWrappedSocket:
>             """
>             Wrap an existing Python socket object ``socket`` and return a
>             ``TLSWrappedSocket`` object. ``socket`` must be a
> ``SOCK_STREAM``
>             socket: all other socket types are unsupported.
>
>             The returned SSL socket is tied to the context, its settings
> and
>             certificates.
>
>             The parameter ``auto_handshake`` specifies whether to do the
> SSL
>             handshake automatically after doing a ``socket.accept()``, or
>             whether the application program will call it explicitly, by
>             invoking the ``TLSWrappedSocket.do_handshake()`` method.
> Calling
>             ``TLSWrappedSocket.do_handshake()`` explicitly gives the
> program
>             control over the blocking behavior of the socket I/O involved
> in
>             the handshake.
>             """
>
>         @abstractmethod
>         def wrap_buffers(self, incoming: Any, outgoing: Any) ->
> TLSWrappedBuffer:
>             """
>             Wrap a pair of buffer objects (``incoming`` and ``outgoing``)
> to
>             create an in-memory stream for TLS. The SSL routines will read
> data
>             from ``incoming`` and decrypt it, and write encrypted data to
>             ``outgoing``.
>
>             The buffer objects must be either file objects or objects that
>             implement the buffer protocol.
>             """
>
>
> Socket
> ~~~~~~
>
> The socket-wrapper ABC will be defined by the ``TLSWrappedSocket`` ABC,
> which
> has the following definition::
>
>     class TLSWrappedSocket(metaclass=ABCMeta):
>         # The various socket methods all must be implemented. Their
> definitions
>         # have been elided from this class defintion in the PEP because
> they
>         # aren't instructive.
>         @abstractmethod
>         def do_handshake(self) -> None:
>             """
>             Performs the TLS handshake. Also performs certificate
> validation
>             and hostname verification.
>             """
>
>         @abstractmethod
>         def cipher(self) -> Optional[CipherSuite]:
>             """
>             Returns the CipherSuite entry for the cipher that has been
>             negotiated on the connection. If no connection has been
> negotiated,
>             returns ``None``.
>             """
>
>         @abstractmethod
>         def negotiated_protocol(self) -> Optional[Union[NextProtocol,
> bytes]]:
>             """
>             Returns the protocol that was selected during the TLS
> handshake.
>             This selection may have been made using ALPN, NPN, or some
> future
>             negotiation mechanism.
>
>             If the negotiated protocol is one of the protocols defined in
> the
>             ``NextProtocol`` enum, the value from that enum will be
> returned.
>             Otherwise, the raw bytestring of the negotiated protocol will
> be
>             returned.
>
>             If ``Context.set_inner_protocols()`` was not called, if the
> other
>             party does not support protocol negotiation, if this socket
> does
>             not support any of the peer's proposed protocols, or if the
>             handshake has not happened yet, ``None`` is returned.
>             """
>
>         @property
>         @abstractmethod
>         def context(self) -> Context:
>             """
>             The ``Context`` object this socket is tied to.
>             """
>
>         @abstractproperty
>         def negotiated_tls_version(self) -> Optional[TLSVersion]:
>             """
>             The version of TLS that has been negotiated on this connection.
>             """
>
>         @abstractmethod
>         def unwrap(self) -> socket.socket:
>             """
>             Cleanly terminate the TLS connection on this wrapped socket.
> Once
>             called, this ``TLSWrappedSocket`` can no longer be used to
> transmit
>             data. Returns the socket that was wrapped with TLS.
>             """
>
>
> Buffer
> ~~~~~~
>
> The buffer-wrapper ABC will be defined by the ``TLSWrappedBuffer`` ABC,
> which
> has the following definition::
>
>     class TLSWrappedBuffer(metaclass=ABCMeta):
>         @abstractmethod
>         def read(self, amt=None: int) -> bytes:
>             """
>             Read up to ``amt`` bytes of data from the input buffer and
> return
>             the result as a ``bytes`` instance. If ``amt`` is ``None``,
> will
>             attempt to read until either EOF is reached or until further
>             attempts to read would raise either ``WantReadError`` or
>             ``WantWriteError``.
>
>             Raise ``WantReadError`` or ``WantWriteError`` if there is
>             insufficient data in either the input or output buffer and the
>             operation would have caused data to be written or read.
>
>             As at any time a re-negotiation is possible, a call to
> ``read()``
>             can also cause write operations.
>             """
>
>         @abstractmethod
>         def readinto(self, buffer: Any, amt=None: int) -> int:
>             """
>             Read up to ``amt`` bytes of data from the input buffer into
>             ``buffer``, which must be an object that implements the buffer
>             protocol. Returns the number of bytes read. If ``amt`` is
> ``None``,
>             will attempt to read until either EOF is reached or until
> further
>             attempts to read would raise either ``WantReadError`` or
>             ``WantWriteError``, or until the buffer is full.
>
>             Raises ``WantReadError`` or ``WantWriteError`` if there is
>             insufficient data in either the input or output buffer and the
>             operation would have caused data to be written or read.
>
>             As at any time a re-negotiation is possible, a call to
>             ``readinto()`` can also cause write operations.
>             """
>
>         @abstractmethod
>         def write(self, buf: Any) -> int:
>             """
>             Write ``buf`` in encrypted form to the output buffer and
> return the
>             number of bytes written. The ``buf`` argument must be an object
>             supporting the buffer interface.
>
>             Raise ``WantReadError`` or ``WantWriteError`` if there is
>             insufficient data in either the input or output buffer and the
>             operation would have caused data to be written or read.
>
>             As at any time a re-negotiation is possible, a call to
> ``write()``
>             can also cause read operations.
>             """
>
>         @abstractmethod
>         def do_handshake(self) -> None:
>             """
>             Performs the TLS handshake. Also performs certificate
> validation
>             and hostname verification.
>             """
>
>         @abstractmethod
>         def cipher(self) -> Optional[CipherSuite]:
>             """
>             Returns the CipherSuite entry for the cipher that has been
>             negotiated on the connection. If no connection has been
> negotiated,
>             returns ``None``.
>             """
>
>         @abstractmethod
>         def negotiated_protocol(self) -> Optional[Union[NextProtocol,
> bytes]]:
>             """
>             Returns the protocol that was selected during the TLS
> handshake.
>             This selection may have been made using ALPN, NPN, or some
> future
>             negotiation mechanism.
>
>             If the negotiated protocol is one of the protocols defined in
> the
>             ``NextProtocol`` enum, the value from that enum will be
> returned.
>             Otherwise, the raw bytestring of the negotiated protocol will
> be
>             returned.
>
>             If ``Context.set_inner_protocols()`` was not called, if the
> other
>             party does not support protocol negotiation, if this socket
> does
>             not support any of the peer's proposed protocols, or if the
>             handshake has not happened yet, ``None`` is returned.
>             """
>
>         @property
>         @abstractmethod
>         def context(self) -> Context:
>             """
>             The ``Context`` object this socket is tied to.
>             """
>
>         @abstractproperty
>         def negotiated_tls_version(self) -> Optional[TLSVersion]:
>             """
>             The version of TLS that has been negotiated on this connection.
>             """
>
>         @abstractmethod
>         def shutdown(self) -> None:
>             """
>             Performs a clean TLS shut down. This should generally be used
>             whenever possible to signal to the remote peer that the
> content is
>             finished.
>             """
>
>
> Cipher Suites
> ~~~~~~~~~~~~~
>
> Supporting cipher suites in a truly library-agnostic fashion is a
> remarkably
> difficult undertaking. Different TLS implementations often have *radically*
> different APIs for specifying cipher suites, but more problematically these
> APIs frequently differ in capability as well as in style. Some examples are
> shown below:
>
> OpenSSL
> ^^^^^^^
>
> OpenSSL uses a well-known cipher string format. This format has been
> adopted as
> a configuration language by most products that use OpenSSL, including
> Python.
> This format is relatively easy to read, but has a number of downsides: it
> is
> a string, which makes it remarkably easy to provide bad inputs; it lacks
> much
> detailed validation, meaning that it is possible to configure OpenSSL in a
> way
> that doesn't allow it to negotiate any cipher at all; and it allows
> specifying
> cipher suites in a number of different ways that make it tricky to parse.
> The
> biggest problem with this format is that there is no formal specification
> for
> it, meaning that the only way to parse a given string the way OpenSSL
> would is
> to get OpenSSL to parse it.
>
> OpenSSL's cipher strings can look like this::
>
>     'ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+
> AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!eNULL:!MD5'
>
> This string demonstrates some of the complexity of the OpenSSL format. For
> example, it is possible for one entry to specify multiple cipher suites:
> the
> entry ``ECDH+AESGCM`` means "all ciphers suites that include both
> elliptic-curve Diffie-Hellman key exchange and AES in Galois Counter Mode".
> More explicitly, that will expand to four cipher suites::
>
>     "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-
> SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
>
> That makes parsing a complete OpenSSL cipher string extremely tricky. Add
> to
> the fact that there are other meta-characters, such as "!" (exclude all
> cipher
> suites that match this criterion, even if they would otherwise be included:
> "!MD5" means that no cipher suites using the MD5 hash algorithm should be
> included), "-" (exclude matching ciphers if they were already included, but
> allow them to be re-added later if they get included again), and "+"
> (include
> the matching ciphers, but place them at the end of the list), and you get
> an
> *extremely* complex format to parse. On top of this complexity it should be
> noted that the actual result depends on the OpenSSL version, as an OpenSSL
> cipher string is valid so long as it contains at least one cipher that
> OpenSSL
> recognises.
>
> OpenSSL also uses different names for its ciphers than the names used in
> the
> relevant specifications. See the manual page for ``ciphers(1)`` for more
> details.
>
> The actual API inside OpenSSL for the cipher string is simple::
>
>     char *cipher_list = <some cipher list>;
>     int rc = SSL_CTX_set_cipher_list(context, cipher_list);
>
> This means that any format that is used by this module must be able to be
> converted to an OpenSSL cipher string for use with OpenSSL.
>
> SecureTransport
> ^^^^^^^^^^^^^^^
>
> SecureTransport is the macOS system TLS library. This library is
> substantially
> more restricted than OpenSSL in many ways, as it has a much more restricted
> class of users. One of these substantial restrictions is in controlling
> supported cipher suites.
>
> Ciphers in SecureTransport are represented by a C ``enum``. This enum has
> one
> entry per cipher suite, with no aggregate entries, meaning that it is not
> possible to reproduce the meaning of an OpenSSL cipher string like
> "ECDH+AESGCM" without hand-coding which categories each enum member falls
> into.
>
> However, the names of most of the enum members are in line with the formal
> names of the cipher suites: that is, the cipher suite that OpenSSL calls
> "ECDHE-ECDSA-AES256-GCM-SHA384" is called
> "TLS_ECDHE_ECDHSA_WITH_AES_256_GCM_SHA384" in SecureTransport.
>
> The API for configuring cipher suites inside SecureTransport is simple::
>
>     SSLCipherSuite ciphers[] = {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
> ...};
>     OSStatus status = SSLSetEnabledCiphers(context, ciphers,
> sizeof(cphers));
>
> SChannel
> ^^^^^^^^
>
> SChannel is the Windows system TLS library.
>
> SChannel has extremely restrictive support for controlling available TLS
> cipher suites, and additionally adopts a third method of expressing what
> TLS
> cipher suites are supported.
>
> Specifically, SChannel defines a set of ``ALG_ID`` constants (C unsigned
> ints).
> Each of these constants does not refer to an entire cipher suite, but
> instead
> an individual algorithm. Some examples are ``CALG_3DES`` and
> ``CALG_AES_256``,
> which refer to the bulk encryption algorithm used in a cipher suite,
> ``CALG_DH_EPHEM`` and ``CALG_RSA_KEYX`` which refer to part of the key
> exchange
> algorithm used in a cipher suite, ``CALG_SHA1`` and ``CALG_MD5`` which
> refer to
> the message authentication code used in a cipher suite, and ``CALG_ECDSA``
> and
> ``CALG_RSA_SIGN`` which refer to the signing portions of the key exchange
> algorithm.
>
> This can be thought of as the half of OpenSSL's functionality that
> SecureTransport doesn't have: SecureTransport only allows specifying exact
> cipher suites, while SChannel only allows specifying *parts* of the cipher
> suite, while OpenSSL allows both.
>
> Determining which cipher suites are allowed on a given connection is done
> by
> providing a pointer to an array of these ``ALG_ID`` constants. This means
> that
> any suitable API must allow the Python code to determine which ``ALG_ID``
> constants must be provided.
>
>
> Proposed Interface
> ^^^^^^^^^^^^^^^^^^
>
> The proposed interface for the new module is influenced by the combined
> set of
> limitations of the above implementations. Specifically, as every
> implementation
> *except* OpenSSL requires that each individual cipher be provided, there
> is no
> option but to provide that lowest-common denominator approach.
>
> The simplest approach is to provide an enumerated type that includes all
> of the
> cipher suites defined for TLS. The values of the enum members will be their
> two-octet cipher identifier as used in the TLS handshake, stored as a
> tuple of
> integers. The names of the enum members will be their IANA-registered
> cipher
> suite names.
>
> Rather than populate this enum by hand, it is likely that we'll define a
> script that can build it from Christian Heimes' `tlsdb JSON file`_
> (warning:
> large file). This also opens up the possibility of extending the API with
> additional querying function, such as determining which TLS versions
> support
> which ciphers, if that functionality is found to be useful or necessary.
>
> If users find this approach to be onerous, a future extension to this API
> can
> provide helpers that can reintroduce OpenSSL's aggregation functionality.
>
> Because this enum would be enormous, the entire enum is not provided here.
> Instead, a small sample of entries is provided to give a flavor of how it
> will
> appear.
>
> ::
>
>     class CipherSuite(Enum):
>         ...
>         TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = (0xC0, 0x12)
>         ...
>         TLS_ECDHE_ECDSA_WITH_AES_128_CCM = (0xC0, 0xAC)
>         ...
>         TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = (0xC0, 0x2B)
>         ...
>
>
> Protocol Negotiation
> ~~~~~~~~~~~~~~~~~~~~
>
> Both NPN and ALPN allow for protocol negotiation as part of the HTTP/2
> handshake. While NPN and ALPN are, at their fundamental level, built on
> top of
> bytestrings, string-based APIs are frequently problematic as they allow for
> errors in typing that can be hard to detect.
>
> For this reason, this module would define a type that protocol negotiation
> implementations can pass and be passed. This type would wrap a bytestring
> to
> allow for aliases for well-known protocols. This allows us to avoid the
> problems inherent in typos for well-known protocols, while allowing the
> full
> extensibility of the protocol negotiation layer if needed by letting users
> pass
> byte strings directly.
>
> ::
>
>     class NextProtocol(Enum):
>         H2 = b'h2'
>         H2C = b'h2c'
>         HTTP1 = b'http/1.1'
>         WEBRTC = b'webrtc'
>         C_WEBRTC = b'c-webrtc'
>         FTP = b'ftp'
>         STUN = b'stun.nat-discovery'
>         TURN = b'stun.turn'
>
> TLS Versions
> ~~~~~~~~~~~~
>
> It is often useful to be able to restrict the versions of TLS you're
> willing to
> support. There are many security advantages in refusing to use old
> versions of
> TLS, and some misbehaving servers will mishandle TLS clients advertising
> support for newer versions.
>
> The following enumerated type can be used to gate TLS versions.
> Forward-looking
> applications should almost never set a maximum TLS version unless they
> absolutely must, as a TLS backend that is newer than the Python that uses
> it
> may support TLS versions that are not in this enumerated type.
>
> Additionally, this enumerated type defines two additional flags that can
> always
> be used to request either the lowest or highest TLS version supported by an
> implementation.
>
> ::
>
>     class TLSVersion(Enum):
>         MINIMUM_SUPPORTED
>         SSLv2
>         SSLv3
>         TLSv1
>         TLSv1_1
>         TLSv1_2
>         TLSv1_3
>         MAXIMUM_SUPPORTED
>
>
> Errors
> ~~~~~~
>
> This module would define three base classes for use with error handling.
> Unlike
> many of the the other classes defined here, these classes are not
> abstract, as
> they have no behaviour. They exist simply to signal certain common
> behaviours.
> Backends should subclass these exceptions in their own packages, but
> needn't
> define any behaviour for them.
>
> In general, concrete implementations should subclass these exceptions
> rather
> than throw them directly. This makes it moderately easier to determine
> which
> concrete TLS implementation is in use during debugging of unexpected
> errors.
> However, this is not mandatory.
>
> The definitions of the errors are below::
>
>     class TLSError(Exception):
>         """
>         The base exception for all TLS related errors from any backend.
>         Catching this error should be sufficient to catch *all* TLS errors,
>         regardless of what backend is used.
>         """
>
>     class WantWriteError(TLSError):
>         """
>         A special signaling exception used only when non-blocking or
>         buffer-only I/O is used. This error signals that the requested
>         operation cannot complete until more data is written to the
> network,
>         or until the output buffer is drained.
>         """
>
>     class WantReadError(TLSError):
>         """
>         A special signaling exception used only when non-blocking or
>         buffer-only I/O is used. This error signals that the requested
>         operation cannot complete until more data is read from the
> network, or
>         until more data is available in the input buffer.
>         """
>
>
> Certificates
> ~~~~~~~~~~~~
>
> This module would define an abstract X509 certificate class. This class
> would
> have almost no behaviour, as the goal of this module is not to provide all
> possible relevant cryptographic functionality that could be provided by
> X509
> certificates. Instead, all we need is the ability to signal the source of a
> certificate to a concrete implementation.
>
> For that reason, this certificate implementation defines only
> constructors. In
> essence, the certificate object in this module could be as abstract as a
> handle
> that can be used to locate a specific certificate.
>
> Concrete implementations may choose to provide alternative constructors,
> e.g.
> to load certificates from HSMs. If a common interface emerges for doing
> this,
> this module may be updated to provide a standard constructor for this
> use-case
> as well.
>
> Concrete implementations should aim to have Certificate objects be
> hashable if
> at all possible. This will help ensure that TLSConfiguration objects used
> with
> an individual concrete implementation are also hashable.
>
> ::
>
>     class Certificate(metaclass=ABCMeta):
>         @abstractclassmethod
>         def from_buffer(cls, buffer: bytes) -> Certificate:
>             """
>             Creates a Certificate object from a byte buffer. This byte
> buffer
>             may be either PEM-encoded or DER-encoded. If the buffer is PEM
>             encoded it *must* begin with the standard PEM preamble (a
> series of
>             dashes followed by the ASCII bytes "BEGIN CERTIFICATE" and
> another
>             series of dashes). In the absence of that preamble, the
>             implementation may assume that the certificate is DER-encoded
>             instead.
>             """
>
>         @abstractclassmethod
>         def from_file(cls, path: Union[pathlib.Path, AnyStr]) ->
> Certificate:
>             """
>             Creates a Certificate object from a file on disk. This method
> may
>             be a convenience method that wraps ``open`` and
> ``from_buffer``,
>             but some TLS implementations may be able to provide
> more-secure or
>             faster methods of loading certificates that do not involve
> Python
>             code.
>             """
>
>
> Private Keys
> ~~~~~~~~~~~~
>
> This module would define an abstract private key class. Much like the
> Certificate class, this class has almost no behaviour in order to give as
> much
> freedom as possible to the concrete implementations to treat keys
> carefully.
>
> This class has all the caveats of the ``Certificate`` class.
>
> ::
>
>     class PrivateKey(metaclass=ABCMeta):
>         @abstractclassmethod
>         def from_buffer(cls,
>                         buffer: bytes,
>                         password=None: Optional[Union[Callable[[],
> Union[bytes, bytearray]], bytes, bytearray]) -> PrivateKey:
>             """
>             Creates a PrivateKey object from a byte buffer. This byte
> buffer
>             may be either PEM-encoded or DER-encoded. If the buffer is PEM
>             encoded it *must* begin with the standard PEM preamble (a
> series of
>             dashes followed by the ASCII bytes "BEGIN", the key type, and
>             another series of dashes). In the absence of that preamble, the
>             implementation may assume that the certificate is DER-encoded
>             instead.
>
>             The key may additionally be encrypted. If it is, the
> ``password``
>             argument can be used to decrypt the key. The ``password``
> argument
>             may be a function to call to get the password for decrypting
> the
>             private key. It will only be called if the private key is
> encrypted
>             and a password is necessary. It will be called with no
> arguments,
>             and it should return either bytes or bytearray containing the
>             password. Alternatively a bytes, or bytearray value may be
> supplied
>             directly as the password argument. It will be ignored if the
>             private key is not encrypted and no password is needed.
>             """
>
>         @abstractclassmethod
>         def from_file(cls,
>                       path: Union[pathlib.Path, bytes, str],
>                       password=None: Optional[Union[Callable[[],
> Union[bytes, bytearray]], bytes, bytearray]) -> PrivateKey:
>             """
>             Creates a PrivateKey object from a file on disk. This method
> may
>             be a convenience method that wraps ``open`` and
> ``from_buffer``,
>             but some TLS implementations may be able to provide
> more-secure or
>             faster methods of loading certificates that do not involve
> Python
>             code.
>
>             The ``password`` parameter behaves exactly as the equivalent
>             parameter on ``from_buffer``.
>             """
>
>
> Trust Store
> ~~~~~~~~~~~
>
> As discussed above, loading a trust store represents an issue because
> different
> TLS implementations vary wildly in how they allow users to select trust
> stores.
> For this reason, we need to provide a model that assumes very little about
> the
> form that trust stores take.
>
> This problem is the same as the one that the Certificate and PrivateKey
> types
> need to solve. For this reason, we use the exact same model, by creating an
> opaque type that can encapsulate the various means that TLS backends may
> open
> a trust store.
>
> A given TLS implementation is not required to implement all of the
> constructors. However, it is strongly recommended that a given TLS
> implementation provide the ``system`` constructor if at all possible, as
> this
> is the most common validation trust store that is used. Concrete
> implementations may also add their own constructors.
>
> Concrete implementations should aim to have TrustStore objects be hashable
> if
> at all possible. This will help ensure that TLSConfiguration objects used
> with
> an individual concrete implementation are also hashable.
>
> ::
>
>     class TrustStore(metaclass=ABCMeta):
>         @abstractclassmethod
>         def system(cls) -> TrustStore:
>             """
>             Returns a TrustStore object that represents the system trust
>             database.
>             """
>
>         @abstractclassmethod
>         def from_pem_file(cls, path: Union[pathlib.Path, bytes, str]) ->
> TrustStore:
>             """
>             Initializes a trust store from a single file full of PEMs.
>             """
>
>
> Runtime Access
> ~~~~~~~~~~~~~~
>
> A not-uncommon use case for library users is to want to allow the library
> to
> control the TLS configuration, but to want to select what backend is in
> use.
> For example, users of Requests may want to be able to select between
> OpenSSL or
> a platform-native solution on Windows and macOS, or between OpenSSL and
> NSS on
> some Linux platforms. These users, however, may not care about exactly how
> their TLS configuration is done.
>
> This poses a problem: given an arbitrary concrete implementation, how can a
> library work out how to load certificates into the trust store? There are
> two
> options: either all concrete implementations can be required to fit into a
> specific naming scheme, or we can provide an API that makes it possible to
> grab
> these objects.
>
> This PEP proposes that we use the second approach. This grants the greatest
> freedom to concrete implementations to structure their code as they see
> fit,
> requiring only that they provide a single object that has the appropriate
> properties in place. Users can then pass this "backend" object to libraries
> that support it, and those libraries can take care of configuring and
> using the
> concrete implementation.
>
> All concrete implementations must provide a method of obtaining a
> ``Backend``
> object. The ``Backend`` object can be a global singleton or can be created
> by a
> callable if there is an advantage in doing that.
>
> The ``Backend`` object has the following definition::
>
>     Backend = namedtuple(
>         'Backend',
>         ['client_context', 'server_context',
>          'certificate', 'private_key', 'trust_store']
>     )
>
> Each of the properties must provide the concrete implementation of the
> relevant
> ABC. This ensures that code like this will work for any backend::
>
>     trust_store = backend.trust_store.system()
>
>
> Changes to the Standard Library
> ===============================
>
> The portions of the standard library that interact with TLS should be
> revised
> to use these ABCs. This will allow them to function with other TLS
> backends.
> This includes the following modules:
>
> - asyncio
> - ftplib
> - http.client
> - imaplib
> - nntplib
> - poplib
> - smtplib
>
>
> Migration of the ssl module
> ---------------------------
>
> Naturally, we will need to extend the ``ssl`` module itself to conform to
> these
> ABCs. This extension will take the form of new classes, potentially in an
> entirely new module. This will allow applications that take advantage of
> the
> current ``ssl`` module to continue to do so, while enabling the new APIs
> for
> applications and libraries that want to use them.
>
> In general, migrating from the ``ssl`` module to the new ABCs is not
> expected
> to be one-to-one. This is normally acceptable: most tools that use the
> ``ssl``
> module hide it from the user, and so refactoring to use the new module
> should
> be invisible.
>
> However, a specific problem comes from libraries or applications that leak
> exceptions from the ``ssl`` module, either as part of their defined API or
> by
> accident (which is easily done). Users of those tools may have written code
> that tolerates and handles exceptions from the ``ssl`` module being raised:
> migrating to the ABCs presented here would potentially cause the exceptions
> defined above to be thrown instead, and existing ``except`` blocks will not
> catch them.
>
> For this reason, part of the migration of the ``ssl`` module would require
> that
> the exceptions in the ``ssl`` module alias those defined above. That is,
> they
> would require the following statements to all succeed::
>
>     assert ssl.SSLError is tls.TLSError
>     assert ssl.SSLWantReadError is tls.WantReadError
>     assert ssl.SSLWantWriteError is tls.WantWriteError
>
> The exact mechanics of how this will be done are beyond the scope of this
> PEP,
> as they are made more complex due to the fact that the current ``ssl``
> exceptions are defined in C code, but more details can be found in
> `an email sent to the Security-SIG by Christian Heimes`_.
>
>
> Future
> ======
>
> Major future TLS features may require revisions of these ABCs. These
> revisions
> should be made cautiously: many backends may not be able to move forward
> swiftly, and will be invalidated by changes in these ABCs. This is
> acceptable,
> but wherever possible features that are specific to individual
> implementations
> should not be added to the ABCs. The ABCs should restrict themselves to
> high-level descriptions of IETF-specified features.
>
> However, well-justified extensions to this API absolutely should be made.
> The
> focus of this API is to provide a unifying lowest-common-denominator
> configuration option for the Python community. TLS is not a static target,
> and
> as TLS evolves so must this API.
>
>
> References
> ==========
>
> .. _ssl module: https://docs.python.org/3/library/ssl.html
> .. _OpenSSL Library: https://www.openssl.org/
> .. _PyOpenSSL: https://pypi.org/project/pyOpenSSL/
> .. _certifi: https://pypi.org/project/certifi/
> .. _SSLContext: https://docs.python.org/3/library/ssl.html#ssl.SSLContext
> .. _SSLSocket: https://docs.python.org/3/library/ssl.html#ssl.SSLSocket
> .. _SSLObject: https://docs.python.org/3/library/ssl.html#ssl.SSLObject
> .. _SSLError: https://docs.python.org/3/library/ssl.html#ssl.SSLError
> .. _MSDN articles: https://msdn.microsoft.com/en-
> us/library/windows/desktop/mt490158(v=vs.85).aspx
> .. _tlsdb JSON file: https://github.com/tiran/tlsdb/blob/master/tlsdb.json
> .. _an email sent to the Security-SIG by Christian Heimes:
> https://mail.python.org/pipermail/security-sig/2017-January/000213.html
>
>
> Copyright
> =========
>
> This document has been placed in the public domain.
>
>
> ..
>    Local Variables:
>    mode: indented-text
>    indent-tabs-mode: nil
>    sentence-end-double-space: t
>    fill-column: 70
>    coding: utf-8
>    End:
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20170203/8ac16602/attachment-0001.html>


More information about the Python-ideas mailing list