Skip to content

Commit

Permalink
Merge pull request #670 from swimos/client_crypto_provider
Browse files Browse the repository at this point in the history
Sever client networking cryptographic provider initialisation
  • Loading branch information
SirCipher authored Jun 26, 2024
2 parents 4e0981d + 95fcce3 commit cabecec
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 88 deletions.
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

0 comments on commit cabecec

Please sign in to comment.