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

Abstraction for synchronous esp-tls #288

Merged
merged 14 commits into from
Aug 24, 2023
Merged

Abstraction for synchronous esp-tls #288

merged 14 commits into from
Aug 24, 2023

Conversation

torkleyy
Copy link
Contributor

@torkleyy torkleyy commented Aug 23, 2023

I think this should be pretty much good as-is, these TODOs remain:

  • Figure out how to map crt_bundle_attach
  • Figure out how to map ds_data (not implemented by this PR)
  • Is the naming okay / how could async be added in the future?
  • Module structure / #[cfg(esp_idf_comp_esp_tls_enabled)]
  • non-block config

Let me know if this PR is headed into the right direction.

Copy link
Collaborator

@ivmarkov ivmarkov left a comment

Choose a reason for hiding this comment

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

That's a great idea!

I've left a bunch of (mostly stylistic / code reshuffling) comments, but the approach is solid.

Maybe the one important topic is whether it would be possible to also expose an API, that "adopts" an already existing socket, and does the TLS negotiation on top of it. But we can survive without this initially, I believe.

src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
@ivmarkov
Copy link
Collaborator

  • Is the naming okay / how could async be added in the future?

For the naming - yes, that's fine.

As for async, adding it - as in - async read / async write should be somehow possible, but I'm not clear on details:

  • The easy part: setting cfg.non_block to true is likely to switch the underlying socket to O_NONBLOCK mode post initial TLS negotiation (which itself is either sync or async, but for the async TLS negotiation we can think later).
  • The question is more about how to plug the underlying sock_fd into the smol reactor (async-io + polling) or into the upcoming support for tokio's reactor. If your EspTls implementation was capable of "adopting" an already opened socket, that might have been easier, as we would be using e.g. Async<std::net::TcpSocket> (or rather, its T: OwnedFd trait) which would be already registered with smol's reactor and in non-blocking mode, so the TLS traffic would just flow on top then?

@ivmarkov
Copy link
Collaborator

One more thing: you need to replace use esp_idf_sys::* with use crate::sys::*, as in the meantime, both esp-idf-hal and esp-idf-svc re-export their esp-idf-sys dependency as sys. (And esp-idf-svc re-exports esp-idf-hal as hal.)

Long term this means most folks would depend only on esp-idf-svc (as they almost always would need a service or two from it anyway), and would use esp-idf-hal and esp-idf-sys via the esp_idf_svc::sys and esp_idf_svc::hal re-exports.

* Allow splitting into EspTlsRead and EspTlsWrite
* Don't use std
* Use str and do alloc-free conversion
@torkleyy torkleyy requested a review from ivmarkov August 23, 2023 13:47
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
src/tls.rs Outdated Show resolved Hide resolved
Copy link
Contributor Author

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

I think there's one thing remaining, most of the tls module should be guarded by a cfg, but we can't do it for the full module because X509 is exposed unconditionally.

I could split EspTls etc into a separate module and then reexport it into tls, what do you think?

@torkleyy torkleyy changed the title Abstraction for blocking esp-tls Abstraction for synchronous esp-tls Aug 23, 2023
src/tls.rs Outdated
pub client_cert: Option<X509<'a>>,
pub client_key: Option<X509<'a>>,
pub client_key_password: Option<&'a str>,
pub non_block: bool,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If this is true, should we call conn_new_async instead?

Copy link
Collaborator

@ivmarkov ivmarkov Aug 24, 2023

Choose a reason for hiding this comment

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

Not necessarily, but VERY IMPORTANT: I was looking at the code and I think I just figured out a way for us to pass an already opened socket!

I think we still should keep the existing new, but we should introduce (maybe post-merge) a new STD-only method along the lines of

#[cfg(feature = "std")]
pub fn wrap(hostname: &str/*We still need this as the hostname is part of the SSL validation*/, socket: std::net::TcpSocket, cfg: &Config) -> Result<EspError, Self> {
    let tls = esp!(unsafe { esp_tls_init(); })?;
    esp!(unsafe { esp_tls_set_conn_state(tls, esp_tls_conn_state_t_ESP_TLS_CONNECTING) })?;
    esp!(unsafe { esp_tls_set_conn_sockfd(tls, socket.fd)? })?;
    esp!(unsafe { esp_tls_conn_new_sync(<hostname_converted_to_char*>, <hostname_len>, 0, cfg, tls) })?;

    Ok(...)
}

The above ^^^ will not try to open a new socket and will re-use ours, as the state of the connection is esp_tls_conn_state_t_ESP_TLS_CONNECTING.

The above approach is important, because it might be a way to implement async as well. Again, to do async in a way which is independent of the concrete async Reactor, we needed a way to pass-in an already opened socket handle, and the above might be just this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

To not make the above STD-only, we can even pass to the method a raw file descriptor, where the assumption is that the file descriptor is a socket descriptor, and also it is already opened.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, I can give it a try in a follow-up PR.

@torkleyy torkleyy requested a review from ivmarkov August 24, 2023 09:29
@ivmarkov
Copy link
Collaborator

@torkleyy Build fails because you are still using aliases from the std:: module. Switch these to core::.

@ivmarkov
Copy link
Collaborator

I think there's one thing remaining, most of the tls module should be guarded by a cfg, but we can't do it for the full module because X509 is exposed unconditionally.

I could split EspTls etc into a separate module and then reexport it into tls, what do you think?

Yeah, just don't create extra files. Surround your code with

[cfg("esp_idf_comp_esp_tls_enabled")]
mod esptls {
...
}

... and then at the top, do

[cfg("esp_idf_comp_esp_tls_enabled")]
pub use esptls::*;

@torkleyy
Copy link
Contributor Author

Build is looking good, finally 😅

@ivmarkov ivmarkov merged commit 2605747 into esp-rs:master Aug 24, 2023
8 checks passed
@torkleyy torkleyy deleted the tls branch August 24, 2023 16:02
@ivmarkov
Copy link
Collaborator

ivmarkov commented Aug 26, 2023

@torkleyy JFYI I'll be pushing changes to the API in the next hours. Two reasons:

  • I found a way to "adopt" an existing socket, including for async, so I have an async implementation ready and working (kind of, there is a bug/something not well thought out in the API of esp_tls_connect - it returns 0 where it should return ESP_TLS_WANT READ or ESP_TLS_WANT_WRITE)
  • split is unsound and broken. Turns out, you cannot do overlapping blocking read/write with mbedtls, because the mbedtls SSL context is not thread-safe. The only way to do overlapping read/write with mbedtls is not-blocking (and thus also async). Sounds weird, but this also means the only way to do blocking read/write is to use the async implementation, and then "synchronize" it by using block_on

@torkleyy
Copy link
Contributor Author

@ivmarkov Alright, thanks for letting me know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants