On Wednesday, January 11, 2017, Christian Heimes <christian@cheimes.de> wrote:

On 2017-01-11 20:01, Cory Benfield wrote:

> The ``Context`` abstract base class defines an object that allows configuration
> of TLS. It can be thought of as a factory for ``TLSWrappedSocket`` and
> ``TLSWrappedBuffer`` objects.
>
> The ``Context`` abstract base class has the following class definition::
>
>     TLSBufferObject = Union[TLSWrappedSocket, TLSWrappedBuffer]
>     ServerNameCallback = Callable[[TLSBufferObject, Optional[str], Context], Any]
>
>     class _BaseContext(metaclass=ABCMeta):
>
>         @property
>         @abstractmethod
>         def validate_certificates(self) -> 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``.
>             """
>
>         @validate_certificates.setter
>         @abstractmethod
>         def validate_certificates(self, val: bool) -> None:
>           pass


For 3.7 I'm planning to replace ssl.match_hostname() with OpenSSL
1.0.2's API. For now the one flag is enough. Later we can discuss
settings for wildcard, IP address and CN matching.

>
>         @abstractmethod
>         def register_certificates(self,
>                                   certificates: str,
>                                   key=None: Optional[str],
>                                   password=None: Optional[Callable[[], Union[AnyStr, bytearray]]]) -> None:
>             """
>             Loads a certificate, a number of intermediate certificates, and the
>             corresponding private key. These certificates will be offered to
>             the remote peer during the handshake if required.
>
>             The ``certificates`` argument must be a bytestring containing the
>             PEM-encoded certificates. The first PEM-encoded certificate must be
>             the leaf certificate. All subsequence certificates will be offered
>             as intermediate additional certificates.
>
>             The ``key`` argument, if present, must contain the PEM-encoded
>             private key associated with the leaf certificate. If not present,
>             the private key will be extracted from ``certificates``.
>
>             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 a string, bytes,
>             or bytearray. If the return value is a string it will be encoded as
>             UTF-8 before using it to decrypt the key. Alternatively a string,
>             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.
>             """

I don't think this function works for all libraries and use cases. For
some implementations the order of certificates is very important. For
NSS and PKCS#11 we rather need to specify the slot or nick name of the
cert. For 3.7 I also like to introduce X509 objects and EVP_Key wrapper,
so this function would need to consume a stack of certificates.

Since this function is only required for TLS servers and TLS client cert
authentication, I'd rather mark this function provisional or not define
it in the first version.



This may be a bit of a different use case (and possibly worth having in the first version of a new tls module):

"Hitless TLS Certificate Rotation in Go"
https://diogomonica.com/2017/01/11/hitless-tls-certificate-rotation-in-go/

- Can/could this be done with only set_sni_callback ?

- VerifyPeerCertificate
  https://github.com/golang/go/issues/16363
 

>         @abstractmethod
>         def set_ciphers(self, ciphers: List[Ciphers]) -> None:
>             """
>             Set the available ciphers for TLS connections created with this
>             context. ``ciphers`` should be a list of ciphers from the
>             ``Cipher`` registry. If none of the ``ciphers`` provided to this
>             object are supported or available, a ``TLSError`` will be raised.
>             """

Implementors should initial context with sensible default settings,
preferable system-wide settings. For example Fedora is currently
implementing https://fedoraproject.org/wiki/Changes/CryptoPolicy for
OpenSSL, NSS and GnuTLS.



>
>         @abstractmethod
>         def set_inner_protocols(self, protocols: List[NextProtocol]) -> None:
>             """
>             Specify which protocols the socket should advertise as supported
>             during the TLS handshake. This may be advertised using either or
>             both of ALPN or NPN.
>
>             ``protocols`` should be a list of acceptable protocols in the form
>             of ``NextProtocol`` objects, such as ``[H2, HTTP1]``, ordered by
>             preference. The selection of the protocol will happen during the
>             handshake, and will use whatever protocol negotiation mechanisms
>             are available and supported by both peers.
>
>             If the TLS implementation doesn't support protocol negotiation,
>             this method will raise ``NotImplementedError``.
>             """
>
>         @abstractmethod
>         def set_sni_callback(self, callback: Optional[ServerNameCallback]) -> None:
>             """
>             Register 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 ``Context``. If ``callback`` is
>             ``None`` then the callback is disabled. Calling this function a
>             subsequent time will disable the previously registered callback.
>
>             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 ``None`` 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.
>             """
>
>         @abstractmethod
>         def set_version_range(self, lower_bound=None: Optional[TLSVersion],
>                               upper_bound=None: Optional[TLSVersion]) -> None:
>             """
>             Set the minumum and maximum versions of TLS that should be allowed
>             on TLS connections made by this context.
>
>             If present, ``lower_bound`` will set the lowest acceptable TLS
>             version. If present, ``upper_bound`` will set the highest
>             acceptable TLS version. If either argument is ``None``, this will
>             leave that bound unchanged.
>             """

So, with

```
class TLSVersion(Enum):
        MINIMUM_SUPPORTED
        SSLv2
        SSLv3
        TLSv1
        TLSv1_1
        TLSv1_2
        TLSv1_3
        MAXIMUM_SUPPORTED
```

What are the (signed?) integer values?