Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bindings): add renegotiate to the rust bindings #4668

Merged
merged 6 commits into from
Jul 31, 2024

Conversation

lrstewart
Copy link
Contributor

@lrstewart lrstewart commented Jul 27, 2024

Description of changes:

Add the Rust code necessary to enable the renegotiate feature from the Rust bindings.

Callouts

I put the code in its own module. That required me to mark some of the connection / config private methods pub(crate), but I think keeping all the renegotiate code in one place is worth it.

Testing:

Testing this was tricky. s2n-tls doesn't actually support renegotiation on the server side. In the C unit tests we work around that limitation by doing things like manually constructing the hello request message, but that isn't possible from outside the C library (the message must be encrypted properly). Instead, I had to use openssl as my server, and even openssl didn't actually surface its negotiate methods from its Rust bindings.

I reused TestPair to setup my s2n connection, then discarded the TestPair's server and used the TestPair IO to configure my openssl server. I also had to refactor how the testing framework handles certs-- none of our test code had ever used non-default certs before, and the struct really wasn't very usable.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@lrstewart lrstewart changed the title save feat(bindings): add renegotiate to the rust bindings Jul 27, 2024
@github-actions github-actions bot added the s2n-core team label Jul 27, 2024
@lrstewart lrstewart marked this pull request as ready for review July 27, 2024 01:04
Comment on lines +397 to +403
/// # Safety
///
/// The `context` pointer must live at least as long as the connection
pub unsafe fn set_send_context(&mut self, context: *mut c_void) -> Result<&mut Self, Error> {
s2n_connection_set_send_ctx(self.connection.as_ptr(), context).into_result()?;
Ok(self)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just moved this bc it was weird that it wasn't with the other IO methods

Comment on lines +209 to +211

// std::task::ready is unstable
fn unwrap_poll<T>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there really not a better way to do this? :/

bindings/rust/s2n-tls/Cargo.toml Outdated Show resolved Hide resolved
bindings/rust/s2n-tls/src/renegotiate.rs Outdated Show resolved Hide resolved
bindings/rust/s2n-tls/src/renegotiate.rs Outdated Show resolved Hide resolved
struct ServerTestStream(TestPairIO);

// For server testing purposes, we read from the client output stream
impl Read for ServerTestStream {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about implementing these on the underlying TestPairIO? Since it's such generic functionality. Also the name ServerTestStream feels a little unclear because I don't think there's anything Server specific about it?

Then you'd be able to

let ossl_stream = SslStream::new(openssl_ssl, test_pair.io)?;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's server specific. You have to implement the reverse stream for a client, because you read and write from the opposite buffers. So we'd need a separate ClientTestStream if I wanted to create an openssl client.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or I suppose you could have a generic in/out, but then you really can't use the same struct for the TestPair because the TestPair needs them specific for the client/server.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's specifically useful in this context, but if we find ourselves doing stuff like this relatively frequently it would probably be worth investigating what it would look like to unify the individual harnesses and the benchmark harness.

So we'd just have a single harness that lives in bindings/s2n-tls, and then the benchmarks could take a dependency on that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk, we'd have to be real careful. Our testing framework was so difficult to write tests with before your recent refactors because it was written for benchmarking, not for testing.

bindings/rust/s2n-tls/src/callbacks/pkey.rs Outdated Show resolved Hide resolved
@@ -542,7 +546,7 @@ impl Connection {
// Poll the connection future if it exists.
//
// If the future returns Pending, then re-set it back on the Connection.
fn poll_async_task(&mut self) -> Option<Poll<Result<(), Error>>> {
pub(crate) fn poll_async_task(&mut self) -> Option<Poll<Result<(), Error>>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the visibility is being expanded because of the implementation of poll_renegotiate and friends in the sibling renegotiate module?

I think I'd instead prefer to have the connection impls inside the connection module, and just gate them behind the unstable-renegotiate flag. That let's us keep the private visibility for internal functions, and also keeps all of the direct connection functionality in one module.

And I'd vote for the same treatment for the renegotiate config::Builder impl.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't want renegotiate polluting our main modules. The average developer should never know renegotiate exists. Also, while the renegotiate source code was only like ~200 lines, I had ~500 lines of testing. connection.rs and config.rs are already very large.

But I'm not a fan of all the random pub(crate)s either. I've done a bit of a refactor, see if you like this better:

  • I made wipe_for_renegotiate and renegotiate share the same logic as wipe and negotiate. This means that we only have two well-define pub(crate) methods, AND that we won't accidentally forget to update the renegotiation logic when we update the main logic.
  • I surfaced a new context_mut method for the config builder instead of piecemeal slapping pub(crate) on things. That'll also give us a clear path to other modules defining callback setters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I do like it better, but I'd still prefer it to be part of the main connection/config implementation. But this is a two way door, so I'm also fine with merging this the way it is.

As for the connection.rs and friend being very large, I agree but we'll need to fix that eventually anyways 😅.

bindings/rust/s2n-tls/src/renegotiate.rs Outdated Show resolved Hide resolved
bindings/rust/s2n-tls/src/testing.rs Outdated Show resolved Hide resolved
bindings/rust/s2n-tls/src/testing.rs Show resolved Hide resolved
bindings/rust/s2n-tls/src/testing.rs Show resolved Hide resolved
bindings/rust/s2n-tls/src/renegotiate.rs Show resolved Hide resolved
bindings/rust/s2n-tls/src/renegotiate.rs Outdated Show resolved Hide resolved
bindings/rust/s2n-tls/src/renegotiate.rs Outdated Show resolved Hide resolved
@lrstewart lrstewart requested a review from goatgoose July 30, 2024 16:40
@lrstewart lrstewart enabled auto-merge (squash) July 30, 2024 18:48
@lrstewart lrstewart merged commit ffe7b35 into aws:main Jul 31, 2024
34 checks passed
@lrstewart lrstewart deleted the bindings_renegotiate branch July 31, 2024 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants