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

Sever client networking cryptographic provider initialisation #670

Merged
merged 13 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions client/swimos_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ version = "0.1.0"
edition = "2021"

[features]
default = []
tls = ["swimos_remote/tls"]
default = ["aws_lc_rs_provider"]
deflate = ["runtime/deflate"]
trust_dns = ["swimos_runtime/trust_dns"]
ring_provider = ["swimos_remote/ring_provider"]
aws_lc_rs_provider = ["swimos_remote/aws_lc_rs_provider"]

[dependencies]
runtime = { path = "../runtime" }
Expand All @@ -25,4 +26,4 @@ tokio = { workspace = true, features = ["sync"] }
futures = { workspace = true }
futures-util = { workspace = true }
tracing = { workspace = true }

rustls = { workspace = true }
117 changes: 76 additions & 41 deletions client/swimos_client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,101 +12,136 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#[cfg(not(feature = "deflate"))]
use ratchet::NoExtProvider;
use ratchet::WebSocketStream;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
use swimos_remote::websocket::RatchetClient;
use std::{marker::PhantomData, num::NonZeroUsize, sync::Arc};

use futures_util::future::BoxFuture;
#[cfg(feature = "deflate")]
use ratchet::deflate::{DeflateConfig, DeflateExtProvider};
use ratchet::{
deflate::{DeflateConfig, DeflateExtProvider},
WebSocketStream,
};
use rustls::crypto::CryptoProvider;
use tokio::{sync::mpsc, sync::mpsc::error::SendError, sync::oneshot::error::RecvError};
pub use url::Url;

use runtime::{
start_runtime, ClientConfig, DownlinkRuntimeError, RawHandle, Transport, WebSocketConfig,
};
pub use runtime::{CommandError, Commander, RemotePath};
use std::sync::Arc;
pub use swimos_client_api::DownlinkConfig;
pub use swimos_downlink::lifecycle::{
BasicEventDownlinkLifecycle, BasicMapDownlinkLifecycle, BasicValueDownlinkLifecycle,
EventDownlinkLifecycle, MapDownlinkLifecycle, ValueDownlinkLifecycle,
pub use swimos_downlink::{
lifecycle::BasicEventDownlinkLifecycle, lifecycle::BasicMapDownlinkLifecycle,
lifecycle::BasicValueDownlinkLifecycle, lifecycle::EventDownlinkLifecycle,
lifecycle::MapDownlinkLifecycle, lifecycle::ValueDownlinkLifecycle,
};
use swimos_downlink::{
ChannelError, DownlinkTask, EventDownlinkModel, MapDownlinkHandle, MapDownlinkModel, MapKey,
MapValue, NotYetSyncedError, ValueDownlinkModel, ValueDownlinkSet,
};
use swimos_form::Form;
use swimos_remote::dns::Resolver;
use swimos_remote::plain::TokioPlainTextNetworking;
#[cfg(feature = "tls")]
use swimos_remote::tls::{ClientConfig as TlsConfig, RustlsClientNetworking, TlsError};
use swimos_remote::ClientConnections;
pub use swimos_remote::tls::ClientConfig as TlsConfig;
use swimos_remote::tls::TlsError;
use swimos_remote::{
dns::Resolver,
plain::TokioPlainTextNetworking,
tls::{CryptoProviderConfig, RustlsClientNetworking},
websocket::RatchetClient,
ClientConnections,
};
use swimos_runtime::downlink::{DownlinkOptions, DownlinkRuntimeConfig};
use swimos_utilities::trigger;
use swimos_utilities::trigger::promise;
use tokio::sync::mpsc;
use tokio::sync::mpsc::error::SendError;
use tokio::sync::oneshot::error::RecvError;
pub use url::Url;
use swimos_utilities::{trigger, trigger::promise};

pub type DownlinkOperationResult<T> = Result<T, DownlinkRuntimeError>;

#[derive(Debug, Default)]
#[derive(Default)]
pub struct SwimClientBuilder {
config: ClientConfig,
client_config: ClientConfig,
}

impl SwimClientBuilder {
pub fn new(config: ClientConfig) -> SwimClientBuilder {
SwimClientBuilder { config }
pub fn new(client_config: ClientConfig) -> SwimClientBuilder {
SwimClientBuilder { client_config }
}

/// Sets the websocket configuration.
pub fn set_websocket_config(mut self, to: WebSocketConfig) -> SwimClientBuilder {
self.config.websocket = to;
self.client_config.websocket = to;
self
}

/// Size of the buffers to communicate with the socket.
pub fn set_remote_buffer_size(mut self, to: NonZeroUsize) -> SwimClientBuilder {
self.config.remote_buffer_size = to;
self.client_config.remote_buffer_size = to;
self
}

/// Sets the buffer size between the runtime and transport tasks.
pub fn set_transport_buffer_size(mut self, to: NonZeroUsize) -> SwimClientBuilder {
self.config.transport_buffer_size = to;
self.client_config.transport_buffer_size = to;
self
}

/// Sets the deflate extension configuration for WebSocket connections.
#[cfg(feature = "deflate")]
pub fn set_deflate_config(mut self, to: DeflateConfig) -> SwimClientBuilder {
self.config.websocket.deflate_config = Some(to);
self.client_config.websocket.deflate_config = Some(to);
self
}

/// Enables TLS support.
pub fn set_tls_config(self, tls_config: TlsConfig) -> SwimClientTlsBuilder {
SwimClientTlsBuilder {
client_config: self.client_config,
tls_config,
crypto_provider: Default::default(),
}
}

/// Builds the client.
pub async fn build(self) -> (SwimClient, BoxFuture<'static, ()>) {
let SwimClientBuilder { config } = self;
let SwimClientBuilder { client_config } = self;
open_client(
config,
client_config,
TokioPlainTextNetworking::new(Arc::new(Resolver::new().await)),
)
.await
}
}

pub struct SwimClientTlsBuilder {
client_config: ClientConfig,
tls_config: TlsConfig,
crypto_provider: CryptoProviderConfig,
}

impl SwimClientTlsBuilder {
/// Uses the process-default [`CryptoProvider`] for any TLS connections.
///
/// This is only used if the TLS configuration has been set.
pub fn with_default_crypto_provider(mut self) -> Self {
self.crypto_provider = CryptoProviderConfig::ProcessDefault;
self
}

/// Uses the provided [`CryptoProvider`] for any TLS connections.
pub fn with_crypto_provider(mut self, provider: Arc<CryptoProvider>) -> Self {
self.crypto_provider = CryptoProviderConfig::Provided(provider);
self
}

/// Builds the client using the provided TLS configuration.
#[cfg(feature = "tls")]
pub async fn build_tls(
self,
tls_config: TlsConfig,
) -> Result<(SwimClient, BoxFuture<'static, ()>), TlsError> {
let SwimClientBuilder { config } = self;
pub async fn build(self) -> Result<(SwimClient, BoxFuture<'static, ()>), TlsError> {
let SwimClientTlsBuilder {
client_config,
tls_config,
crypto_provider,
} = self;
Ok(open_client(
config,
RustlsClientNetworking::try_from_config(Arc::new(Resolver::new().await), tls_config)?,
client_config,
RustlsClientNetworking::build(
Arc::new(Resolver::new().await),
tls_config,
crypto_provider.try_build()?,
)?,
)
.await)
}
Expand Down
2 changes: 2 additions & 0 deletions runtime/swimos_remote/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ edition = "2021"
[features]
default = []
tls = ["rustls", "webpki", "webpki-roots", "tokio-rustls", "rustls-pemfile"]
ring_provider = []
aws_lc_rs_provider = []

[dependencies]
ratchet = { workspace = true, features = ["deflate", "split"] }
Expand Down
18 changes: 1 addition & 17 deletions runtime/swimos_remote/src/tls/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use rustls::crypto::CryptoProvider;
use std::sync::Arc;

/// Supported certificate formats for TLS connections.
pub enum CertFormat {
Pem,
Expand Down Expand Up @@ -87,18 +84,14 @@ pub struct ServerConfig {
/// `SSLKEYLOGFILE` environment variable, and writes keys into it. While this may be enabled,
/// if `SSLKEYLOGFILE` is not set, it will do nothing.
pub enable_log_file: bool,
/// Process-wide [`CryptoProvider`] that must already have been installed as the default
/// provider.
pub provider: Arc<CryptoProvider>,
}

impl ServerConfig {
pub fn new(chain: CertChain, key: PrivateKey, provider: Arc<CryptoProvider>) -> Self {
pub fn new(chain: CertChain, key: PrivateKey) -> Self {
ServerConfig {
chain,
key,
enable_log_file: false,
provider,
}
}
}
Expand All @@ -117,12 +110,3 @@ impl ClientConfig {
}
}
}

impl Default for ClientConfig {
fn default() -> Self {
Self {
use_webpki_roots: true,
custom_roots: vec![],
}
}
}
6 changes: 6 additions & 0 deletions runtime/swimos_remote/src/tls/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@ pub enum TlsError {
/// Performing the TLS handshake failed.
#[error("TLS handshake failed: {0}")]
HandshakeFailed(std::io::Error),
/// User specified that a cryptographic provider had been installed but none was found.
#[error("No default cryptographic provider has been installed")]
NoCryptoProviderInstalled,
/// User specified more than one cryptographic provider feature flag. Only one may be specified.
#[error("Ambiguous cryptographic provider feature flags specified. Only \"ring_provider\" or \"aws_lc_rs_provider\" may be specified")]
InvalidCryptoProvider,
}
37 changes: 37 additions & 0 deletions runtime/swimos_remote/src/tls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,40 @@ pub use config::{
pub use errors::TlsError;
pub use maybe::MaybeTlsStream;
pub use net::{RustlsClientNetworking, RustlsListener, RustlsNetworking, RustlsServerNetworking};
use rustls::crypto::CryptoProvider;
use std::sync::Arc;

#[derive(Default)]
pub enum CryptoProviderConfig {
ProcessDefault,
#[default]
FromFeatureFlags,
Provided(Arc<CryptoProvider>),
}

impl CryptoProviderConfig {
pub fn try_build(self) -> Result<Arc<CryptoProvider>, TlsError> {
match self {
CryptoProviderConfig::ProcessDefault => CryptoProvider::get_default()
.ok_or(TlsError::NoCryptoProviderInstalled)
.cloned(),
CryptoProviderConfig::FromFeatureFlags => {
#[cfg(all(feature = "ring_provider", not(feature = "aws_lc_rs_provider")))]
{
return Arc::new(rustls::crypto::ring::default_provider());
}

#[cfg(all(feature = "aws_lc_rs_provider", not(feature = "ring_provider")))]
{
return Arc::new(rustls::crypto::aws_lc_rs::default_provider());
}

#[allow(unreachable_code)]
{
Err(TlsError::InvalidCryptoProvider)
}
}
CryptoProviderConfig::Provided(provider) => Ok(provider),
}
}
}
7 changes: 5 additions & 2 deletions runtime/swimos_remote/src/tls/net/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use std::{net::SocketAddr, sync::Arc};

use futures::{future::BoxFuture, FutureExt};
use rustls::crypto::CryptoProvider;
use rustls::pki_types::ServerName;
use rustls::RootCertStore;

Expand All @@ -40,9 +41,10 @@ impl RustlsClientNetworking {
}
}

pub fn try_from_config(
pub fn build(
resolver: Arc<Resolver>,
config: ClientConfig,
provider: Arc<CryptoProvider>,
) -> Result<Self, TlsError> {
let ClientConfig {
use_webpki_roots,
Expand All @@ -59,7 +61,8 @@ impl RustlsClientNetworking {
}
}

let config = rustls::ClientConfig::builder()
let config = rustls::ClientConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()?
.with_root_certificates(root_store)
.with_no_client_auth();

Expand Down
11 changes: 5 additions & 6 deletions runtime/swimos_remote/src/tls/net/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use futures::{
stream::{unfold, BoxStream, FuturesUnordered},
Future, FutureExt, Stream, StreamExt, TryStreamExt,
};
use rustls::crypto::CryptoProvider;
use rustls::pki_types::PrivateKeyDer;
use rustls::KeyLogFile;
use rustls_pemfile::Item;
Expand Down Expand Up @@ -64,17 +65,15 @@ impl RustlsServerNetworking {
pub fn new(acceptor: TlsAcceptor) -> Self {
RustlsServerNetworking { acceptor }
}
}

impl TryFrom<ServerConfig> for RustlsServerNetworking {
type Error = TlsError;

fn try_from(config: ServerConfig) -> Result<Self, Self::Error> {
pub fn build(
config: ServerConfig,
provider: Arc<CryptoProvider>,
) -> Result<RustlsServerNetworking, TlsError> {
let ServerConfig {
chain: CertChain(certs),
key,
enable_log_file,
provider,
} = config;

let mut chain = vec![];
Expand Down
Loading
Loading