IRCv3 tls extension experiment

❝Trying to implement TLS extension as specified by OTRv3.❞
Contents

... as you would say on Facebook: "It's complicated"

So there is this IRCv3 3.1 optional extension called tls. IRC servers have been offering a separate port for establishing secure connections for a long time. An TLS-secured connection is established and IRC traffic flows through that. However, the tls extension is not that.

tls extension offers a way to upgrade an existing, non-secure connection to a secure connection by initiating a TLS tunnel immediately after IRC client and server agree on switching to a secure mode. The secure tunnel is established inside the existing connection. In general, such an extension is known as STARTTLS.

As noted, this is a fairly common approach to upgrading a connection’s confidentiality level. It has some weaknesses, but in general this approach works fine.

In the specific case of IRC however, there’s a caveat. During an IRC registration process, the user name and password are sent immediately with the optional message for requesting capability negotiation. tls is an example of such a capability. As a consequence, when you first get the opportunity to negotiate for a secure connection, we have already sent confidential data (the password) in plain text. Or, for that matter, when you first get the opportunity to ask for supported extensions in order to check for ‘tls’ support.

In the tls extension specification they have tried to account for this by instructing to immediately send the STARTTLS message after the connection is established, even before the capability negotiation request and identification and authentication messages. So, that’s before you know if tls is actually supported. Furthermore, we should not send any of the standard message of the IRC protocol registration process, as we would be sending these over a plain text connection.

Taking this approach should work if the IRC server supports tls. However, you may not get a response if the server does not support it. The only message we sent, might just be ignored by the server. In this case we should wait for a sufficiently long time such that the server has time to respond, but not so long that the connection gets closed by the IRC server. After either the connection is upgraded or we’ve been waiting in vain for a server response, we can continue sending messages for capability negotiation, identification and authentication to continue the registration process.

The original IRC protocol was not designed with these kinds of extensions in mind. Both capability negotiation and tls have been implemented in a way that relies on the assumption that older, unsupporting IRC servers will simply ignore bad messages. As such they have no way to determine whether an IRC server does not support a particular feature or that it simply hasn’t responded yet.

Unfortunately, this is not the only curiosity. While implementing (as of now incomplete) tls extension support in irc-api as an experiment, I decided to upgrade the connection during the capability negotiation phase - as this fits nicely within the existing CAP NEG process. It turns out that even if an IRC server supports tls, it is not clear in what state the IRC connection should be after the TLS upgrade completes. In the experiment, the server stopped the capability negotiation process as well as the registration process. I suspect that this server completely resetted the connection and waits for me to send the first messages according to the way a connection is registered in IRC. This seems contradictory to the tls specification which seems to indicate that a client can send STARTTLS at any time. It doesn’t make sense to first finish the registration and then send STARTTLS if your connection will be reset completely afterwards.

As I said, it’s complicated. The extension is quite straight-forward and the specification is pretty good. The difficulty is caused mainly by the IRC protocol itself, as it wasn’t originally designed for this purpose.

Of course, it is even better to connect to an IRC server via its TLS port, by default port 6697, if available. Doing this means a secure connection is established from the start and you’re not subject to the weaknesses of STARTTLS or the complications of the tls extension. The tls extension is only meant as an improvement over a plain text connection, in case TLS ports are not an option.