[Security-sig] Unified TLS API for Python

Christian Heimes christian at cheimes.de
Wed Jan 11 16:23:51 EST 2017


On 2017-01-11 21:20, Cory Benfield wrote:
> 
>> On 11 Jan 2017, at 19:49, Christian Heimes <christian at cheimes.de> wrote:
>>
>> AFAIK PyOpenSSL doesn't support this mode. How do we deal with
>> unconnected sockets, UDP/DTLS and other transports? Are sockets limited
>> to AF_INET / AF_INET6 and SOCK_STREAM?
> 
> To begin with, I think we need to restrict ourselves to SOCK_STREAM. There’s no need to get specific about address family I don’t think. As for unconnected sockets, I think I’d like to delay that concern unless someone wants to propose the API.

* +1 for SOCK_STREAM
* Although it's obvious to us, we should to state that IPAddress SAN
validation is only defined for AF_INET and AF_INET6.
* Do we need to define blocking / non blocking state of the socket?
* What about STARTTLS support, do we need to define how to deal with
data that has been transmitted before?

Later:

* unconnected socket and tlssocket.connect().
* Python's ssl module has unwrap (STOPTLS) to shutdown a TLS socket and
return to non-encrypted communcation.


> 
>> In the not-so-distant future SRV-ID validation will become relevant. In
>> order to support dNSName, IPAddress, and SRV-ID validation, the TLS
>> socket needs the hostname (if available), IP address, port and service
>> type (e.g. http, ldap, xmpp-server, …).
> 
> The advantage of this API is that it would be extensible. We can extend it as needed over time, we don’t need to shove everything in at once. So I’m inclined to want to defer this until we see what the implementations actually do.

Maybe we can get away without any extension to the API. SRV-ID uses
underline to signal a service identifier, e.g. _http.www.example.org. In
the future an application could pass down a service identifier as
server_hostname argument and let the TLS library deal with it like this:

if server_hostname.startswith('_'):
    srvid = server_hostname
    service, hostname = server_hostname.split('.', 1)
    service = service[1:]
else:
   service = srvid = None
   hostname = server_hostname


>> For hostname validation we should also define how we are going to deal
>> with encodings. Is the hostname always a IDN U-label, A-label or can it
>> be both?
> 
> Pass. I think you have a better idea of what is required here than I do: do you have a suggestion?

OpenSSL's hostname verification code accept IDN A-labels. SubjecAltNames
are encoded as IA5String containing IDN A-labels, too. For most
flexibility the wrap functions should accept server_hostnames as both
U-label and A-label. If you add an encode_hostname() method to the
context, than users can easily override the method to switch between
IDNA 2003, 2008 or perform custom checks to prevent homoglyphic
confusion attacks.


class _BaseContext:
    def encode_hostname(self, hostname: str) -> str:
        """Convert IDN U-label to A-label
        """

        @abstractmethod
        def wrap_socket(self, socket: socket.socket,
                        auto_handshake=True: bool,
                        server_hostname=None: Optional[str]):
            if server_hostname is not None:
                server_hostname = self.context.encode_hostname(
                    server_hostname)

Christian





More information about the Security-SIG mailing list