On Thursday, January 19, 2017, Cory Benfield <cory@lukasa.co.uk> wrote:

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.

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',
    ]

Thanks! TLSConfiguration looks much easier to review; and should make other implementations easier.

I read a great (illustrated) intro to TLS1.3 the other day:

https://temen.io/resources/tls-gets-an-upgrade:-welcome-1.3/

- 1-RTT and 0-RTT look useful.
- There's a reduced set of cipher suites 

https://tlswg.github.io/tls13-spec/ #rfc.section.4.3

- Are there additional parameters relevant to TLS1.3 for the TLSConfiguration object?
- If necessary, how should TLSConfiguration parameter fields be added?
 


    _DEFAULT_VALUE = object()


    class TLSConfiguration(namedtuple('TLSConfiguration', _configuration_fields)):
        """
        An imutable 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
            )