(This post assumes a basic familiarity with network protocols and the idea of a TLS handshake.)

Every summer, The Innovation Lab at PES University engages some of the brightest students at the university in summer projects pitched by senior students and alumni. As a mentor, it’s always exciting to see a team take on a challenging project and learn skills that’ll last them a lifetime.

In the summer of ‘24, I had the pleasure of guiding the (c)early data team. Our objective? To make the most ubiquitously used network transfer tool (cURL) a little faster.

cearly data

Why this project?

One of my more significant pieces of work as an engineer at Akamai was implementing TLS 1.3 0-RTT Data (more on what this is in a bit!) over Akamai’s edge network. I had the opportunity to both design and largely implement the functionality myself, which gave me significant insight into how TLS, HTTP(S), QUIC, and a bunch of other network security protocols worked.

One of the biggest challenges I faced while implementing 0-RTT was the lack of any popular network transfer clients/tools actually supported sending 0-RTT requests, making validating and testing the feature a whole lot harder.

cURL

If you’ve ever downloaded a file from the command line, chances are you’ve used cURL. It’s a command line tool for transferring data built on top of libcurl, its corresponding library. With an estimated 20 billion installations worldwide, it runs on everything from cars and IoT devices to the cloud services you use daily.

cURL is a stickler for standards - almost always supporting the latest in secure communication, including TLS 1.3. Yet, even it didn’t support the usage of 0-RTT Data!

So what exactly IS this 0-RTT Data?

To answer that, we’re going to need a little background information, so bear with me. 😁

In a typical secure TLS 1.3 connection, the client and server have to perform a ‘handshake’ to arrive at a shared symmetric key to encrypt the connection with.

The process of arriving at a shared symmetric key without actually sending the key itself over the air is a computationally expensive one. On top of this, the process of arriving at this shared key takes at least one round trip of communication.

TLS 1.3 1-RTT

To make the process of arriving at the shared key faster next time, the server can send a ‘session ticket’ after the handshake is complete, which contains (and I’m handwaving a lot here for simplicity) another key that you can either straight up use to encrypt communication from the get go next time, or make the process of arriving at a new shared key a lot less computationally expensive. Session tickets are a characteristic of both TLS 1.2 and 1.3.

TLS 1.3 however introduced “early data” to eke out as much performance as one possibly can while still keeping communication encrypted. When you’re reconnecting to a server you’ve visited before, this feature lets the client send data along with its very first message, piggybacking on the trust established in the previous session by encrypting what that other key provided by the server with the session ticket.

TLS 1.3 0-RTT

This shaves off an entire round trip, which might not sound like much, but in the world of networking, it’s a significant performance win.

The goal of the (c)early data project was to bring this enhancement to cURL.

The Challenge

Adding a feature to a project as massive, mature, and meticulously designed as cURL is like performing surgery; you have to understand the anatomy completely before making an incision. cURL’s code is intentionally modular and agnostic, with abstraction layers to handle different application layer protocols like HTTP/1.x and HTTP/2, and TLS/SSL backends like OpenSSL, wolfSSL, and GnuTLS (this was called the ‘vtls’ layer). The team’s solution had to work seamlessly across multiple backends and multiple application layer protocols.

Looking at the vtls component reveals that cURL performs requests in a non-blocking manner, requiring asynchronous capabilities. Despite being written in C, cURL achieves this using state machines. These meticulously devised state machines enable cURL to perform non-blocking network transfers efficiently.

The core technical challenge, at the start atleast, looked to be that the existing connection logic bundled session resumption and the handshake into a single step. To send early data, we would need to get in between those two actions. This led us designing a new ‘deferred state’ when sending early data.

MORE COMING SOON! (Spoiler - we opened a draft PR! Unfortunately, we simply couldn’t refine the implementation enough over 8 weeks to be a merge-able PR. However, another PR, building on our work, was opened and then merged, adding the long-awaited support for 0-RTT to cURL!)

Thanks for watching! :D