rustls / rustls-ffi

Use Rustls from any language
Other
127 stars 30 forks source link

Add client handshake example #134

Open kevinburke opened 3 years ago

kevinburke commented 3 years ago

The current model in tests/client.c has a workflow that looks like:

  1. Create a rustls_connection (which does not involve reading or writing from the socket)
  2. Write unencrypted bytes to the connection
  3. In a for loop, alternate writes and reads based on the socket state and connection state. The handshake is performed implicitly, followed by the write of the unencrypted bytes.

Some of the C libraries I've seen have a different workflow - they have API's that look like

  1. Socket connection and TLS handshake (cr_connect_nonblocking in curl)
  2. API for writing data (cr_send in curl, pgtls_write in Postgres libpq/fe-secure.c)
  3. API for reading data (cr_recv in curl, pgtls_read in Postgres)

It might be helpful to add a second example, or break out the logic in the current example so the handshake is performed separately. Especially since as implemented in vtls/rustls.c, the logic to perform a handshake is not obvious (call the write method with an empty buffer), and there's no explicit "handshake" method in crustls.h

Even if it's the same workflow under the hood, conceptually this might help when porting code that currently exists in C.

kevinburke commented 3 years ago

More broadly: I had just assumed that if I was implementing a method named write or send or tls_write, I would be sending data on the socket in one direction only. I'm not sure if I'm weird because I've never done C or socket programming before and most folks intuit this, or what the right fix for this would be to help folks mental models.

Some ideas though:

jsha commented 2 years ago

It seems like this a relevant comment in lib/vtls/rustls.c in the curl repo:

    /*
    * Connection has been established according to rustls. Set send/recv
    * handlers, and update the state machine.
    * This check has to come last because is_handshaking starts out false,
    * then becomes true when we first write data, then becomes false again
    * once the handshake is done.
    */

That is indeed pretty unintuitive behavior. We should at a minimum document that on rustls_connection_is_handshaking. And I think you're right - we should have a nice example in-repo of how to do it.

Weirdly, some of the other getters like negotiated_cipher_suite may have more intuitive behavior to check for handshake completion - they return None until the handshake is complete, rather than false, true, false.

add a tls_handshake method to rustls-ffi that invokes read/write on the socket with an empty buffer, similar to what cr_connect_nonblocking is doing.

I don't see where cr_connect_nonblocking writes to an empty buffer. Can you point it out to me?

jsha commented 2 years ago
  • This check has to come last because is_handshaking starts out false,
  • then becomes true when we first write data, then becomes false again
  • once the handshake is done.

I checked, and this is not accurate. is_handshaking starts out true: https://github.com/rustls/rustls-ffi/blob/d7a8692ce60ae0b54776d3f4dd064011ac45244e/src/client.rs#L615-L620.

I don't think that behavior changed. I suspect when I wrote that comment, I had just misinterpreted something I'd observed. Additionally, the comment is on a check that is actually first in its loop, so it's not even doing what it says. I'll send a PR.

kevinburke commented 2 years ago

I don't see where cr_connect_nonblocking writes to an empty buffer. Can you point it out to me?

I think what I meant by this is, while it is reading and writing data, it's all metadata and connection overhead, it's not writing any of the data that the client wants to send to the server.