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

Ability to store peer certificate chain #362

Merged
merged 6 commits into from
Dec 16, 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
2 changes: 2 additions & 0 deletions rama-net/src/tls/client/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub struct ClientConfig {
pub client_auth: Option<ClientAuth>,
/// key log intent
pub key_logger: Option<KeyLogIntent>,
/// if enabled server certificates will be stored in [`NegotiatedTlsParameters`]
pub store_server_certificate_chain: bool,
}

impl ClientConfig {
Expand Down
4 changes: 3 additions & 1 deletion rama-net/src/tls/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mod config;
#[doc(inline)]
pub use config::{ClientAuth, ClientAuthData, ClientConfig, ServerVerifyMode};

use super::{ApplicationProtocol, ProtocolVersion};
use super::{ApplicationProtocol, DataEncoding, ProtocolVersion};

#[derive(Debug, Clone)]
/// Indicate (some) of the negotiated tls parameters that
Expand All @@ -35,6 +35,8 @@ pub struct NegotiatedTlsParameters {
///
/// e.g. [`ApplicationProtocol::HTTP_2`]
pub application_layer_protocol: Option<ApplicationProtocol>,
/// Certificate chain provided the peer (only stored if config requested this)
pub peer_certificate_chain: Option<DataEncoding>,
}

/// Merge extension lists A and B, with
Expand Down
55 changes: 55 additions & 0 deletions rama-net/src/tls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,58 @@ pub enum DataEncoding {
/// Privacy Enhanced Mail (PEM) (plain text)
Pem(NonEmptyString),
}

#[cfg(feature = "boring")]
soundofspace marked this conversation as resolved.
Show resolved Hide resolved
mod boring {
use super::*;
use ::boring::stack::StackRef;
use ::boring::x509::X509;
use rama_core::error::{ErrorContext, OpaqueError};

impl TryFrom<&X509> for DataEncoding {
type Error = OpaqueError;

fn try_from(value: &X509) -> Result<Self, Self::Error> {
let der = value.to_der().context("boring X509 to Der DataEncoding")?;
Ok(DataEncoding::Der(der))
}
}

impl TryFrom<&StackRef<X509>> for DataEncoding {
type Error = OpaqueError;

fn try_from(value: &StackRef<X509>) -> Result<Self, Self::Error> {
let der = value
.into_iter()
.map(|cert| {
cert.to_der()
.context("boring X509 stackref to DerStack DataEncoding")
})
.collect::<Result<Vec<Vec<u8>>, _>>()?;
Ok(DataEncoding::DerStack(der))
}
}
}

#[cfg(feature = "rustls")]
mod rustls {
use super::*;
use ::rustls::pki_types::CertificateDer;

impl From<&CertificateDer<'static>> for DataEncoding {
fn from(value: &CertificateDer<'static>) -> Self {
DataEncoding::Der(value.as_ref().into())
}
}

impl From<&[CertificateDer<'static>]> for DataEncoding {
fn from(value: &[CertificateDer<'static>]) -> Self {
DataEncoding::DerStack(
value
.into_iter()
.map(|cert| Into::<Vec<u8>>::into(cert.as_ref()))
.collect(),
)
}
}
}
4 changes: 4 additions & 0 deletions rama-net/src/tls/server/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ pub struct ServerConfig {

/// key log intent
pub key_logger: KeyLogIntent,

/// store client certificate chain
pub store_client_certificate_chain: bool,
}

impl ServerConfig {
Expand All @@ -40,6 +43,7 @@ impl ServerConfig {
application_layer_protocol_negotiation: None,
client_verify_mode: ClientVerifyMode::default(),
key_logger: KeyLogIntent::default(),
store_client_certificate_chain: false,
}
}
}
Expand Down
16 changes: 15 additions & 1 deletion rama-tls/src/boring/client/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,8 @@ impl<S, K> TlsConnector<S, K> {
where
T: Stream + Unpin,
{
let client_config_data = match connector_data.as_ref().or(self.connector_data.as_ref()) {
let connector_data = connector_data.as_ref().or(self.connector_data.as_ref());
let client_config_data = match connector_data {
Some(connector_data) => connector_data.try_to_build_config()?,
None => TlsConnectorData::new_http_auto()?.try_to_build_config()?,
};
Expand Down Expand Up @@ -437,9 +438,22 @@ impl<S, K> TlsConnector<S, K> {
if let Some(ref proto) = application_layer_protocol {
tracing::trace!(%proto, "boring client (connector) has selected ALPN");
}

let store_server_cert_chain = connector_data
.is_some_and(|data| data.connect_config_input.store_server_certificate_chain);

let server_certificate_chain = match store_server_cert_chain
.then(|| stream.ssl().peer_cert_chain())
.flatten()
soundofspace marked this conversation as resolved.
Show resolved Hide resolved
{
Some(chain) => Some(chain.try_into()?),
None => None,
};

NegotiatedTlsParameters {
protocol_version,
application_layer_protocol,
peer_certificate_chain: server_certificate_chain,
}
}
None => {
Expand Down
5 changes: 5 additions & 0 deletions rama-tls/src/boring/client/connector_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(super) struct ConnectConfigurationInput {
pub(super) verify_algorithm_prefs: Option<Vec<SslSignatureAlgorithm>>,
pub(super) server_verify_mode: Option<ServerVerifyMode>,
pub(super) client_auth: Option<ConnectorConfigClientAuth>,
pub(super) store_server_certificate_chain: bool,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -233,6 +234,9 @@ impl TlsConnectorData {
.client_auth
.clone()
.or_else(|| self.connect_config_input.client_auth.clone()),
store_server_certificate_chain: other
.connect_config_input
.store_server_certificate_chain,
}),
server_name: other
.server_name
Expand Down Expand Up @@ -491,6 +495,7 @@ impl TryFrom<rama_net::tls::client::ClientConfig> for TlsConnectorData {
verify_algorithm_prefs,
server_verify_mode: value.server_verify_mode,
client_auth,
store_server_certificate_chain: value.store_server_certificate_chain,
}),
server_name,
})
Expand Down
3 changes: 3 additions & 0 deletions rama-tls/src/boring/server/acceptor_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ pub(super) struct TlsConfig {
pub(super) protocol_versions: Option<Vec<ProtocolVersion>>,
/// optionally define client certificates in case client auth is enabled
pub(super) client_cert_chain: Option<Vec<X509>>,
/// store client certificate chain if true and client provided this
pub store_client_certificate_chain: bool,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -320,6 +322,7 @@ impl TryFrom<rama_net::tls::server::ServerConfig> for TlsAcceptorData {
keylog_intent: value.key_logger,
protocol_versions: value.protocol_versions.clone(),
client_cert_chain,
store_client_certificate_chain: value.store_client_certificate_chain,
}),
})
}
Expand Down
29 changes: 28 additions & 1 deletion rama-tls/src/boring/server/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rama_core::{
use rama_net::{
http::RequestContext,
stream::Stream,
tls::{client::NegotiatedTlsParameters, ApplicationProtocol},
tls::{client::NegotiatedTlsParameters, ApplicationProtocol, DataEncoding},
transport::TransportContext,
};
use rama_utils::macros::define_inner_service_accessors;
Expand Down Expand Up @@ -214,9 +214,36 @@ where
.ssl()
.selected_alpn_protocol()
.map(ApplicationProtocol::from);

let client_certificate_chain = if let Some(certificate) = tls_config
.store_client_certificate_chain
.then(|| stream.ssl().peer_certificate())
.flatten()
{
// peer_cert_chain doesn't contain the leaf certificate in a server ctx
let mut chain = stream.ssl().peer_cert_chain().map_or(Ok(vec![]), |chain| {
chain
.into_iter()
.map(|cert| {
cert.to_der()
.context("boring ssl session: failed to convert peer certificates to der")
})
.collect::<Result<Vec<Vec<u8>>, _>>()
})?;

let certificate = certificate
.to_der()
.context("boring ssl session: failed to convert peer certificate to der")?;
chain.insert(0, certificate);
Some(DataEncoding::DerStack(chain))
} else {
None
};

ctx.insert(NegotiatedTlsParameters {
protocol_version,
application_layer_protocol,
peer_certificate_chain: client_certificate_chain,
});
}
None => {
Expand Down
13 changes: 12 additions & 1 deletion rama-tls/src/rustls/client/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,8 @@ impl<S, K> TlsConnector<S, K> {
where
T: Stream + Unpin,
{
let client_config_data = match connector_data.as_ref().or(self.connector_data.as_ref()) {
let connector_data = connector_data.as_ref().or(self.connector_data.as_ref());
let client_config_data = match connector_data {
Some(connector_data) => connector_data.try_to_build_config()?,
None => TlsConnectorData::new_http_auto()?.try_to_build_config()?,
};
Expand All @@ -428,6 +429,15 @@ impl<S, K> TlsConnector<S, K> {

let (_, conn_data_ref) = stream.get_ref();

let store_server_cert_chain = connector_data
.is_some_and(|data| data.client_config_input.store_server_certificate_chain);

let server_certificate_chain = if store_server_cert_chain {
conn_data_ref.peer_certificates().map(Into::into)
} else {
None
};

let params = NegotiatedTlsParameters {
protocol_version: conn_data_ref
.protocol_version()
Expand All @@ -436,6 +446,7 @@ impl<S, K> TlsConnector<S, K> {
application_layer_protocol: conn_data_ref
.alpn_protocol()
.map(ApplicationProtocol::from),
peer_certificate_chain: server_certificate_chain,
};

Ok((stream, params))
Expand Down
21 changes: 13 additions & 8 deletions rama-tls/src/rustls/client/connector_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ use tracing::trace;
/// Created by converting a [`rustls::ClientConfig`] into it directly,
/// or by trying to turn the _rama_ opiniated [`rama_net::tls::client::ClientConfig`] into it.
pub struct TlsConnectorData {
client_config_input: Arc<ClientConfigInput>,
server_name: Option<Host>,
pub(super) client_config_input: Arc<ClientConfigInput>,
pub(super) server_name: Option<Host>,
}

#[derive(Debug, Default)]
struct ClientConfigInput {
protocol_versions: Option<Vec<&'static SupportedProtocolVersion>>,
client_auth: Option<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)>,
key_logger: Option<String>,
alpn_protos: Option<Vec<Vec<u8>>>,
cert_verifier: Option<Arc<dyn ServerCertVerifier>>,
pub(super) struct ClientConfigInput {
pub(super) protocol_versions: Option<Vec<&'static SupportedProtocolVersion>>,
pub(super) client_auth: Option<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)>,
pub(super) key_logger: Option<String>,
pub(super) alpn_protos: Option<Vec<Vec<u8>>>,
pub(super) cert_verifier: Option<Arc<dyn ServerCertVerifier>>,
pub(super) store_server_certificate_chain: bool,
}

impl TlsConnectorData {
Expand Down Expand Up @@ -171,6 +172,9 @@ impl TlsConnectorData {
.cert_verifier
.clone()
.or_else(|| self.client_config_input.cert_verifier.clone()),
store_server_certificate_chain: other
.client_config_input
.store_server_certificate_chain,
}),
server_name: other
.server_name
Expand Down Expand Up @@ -304,6 +308,7 @@ impl TryFrom<rama_net::tls::client::ClientConfig> for TlsConnectorData {
.into_file_path(),
alpn_protos,
cert_verifier,
store_server_certificate_chain: value.store_server_certificate_chain,
}),
server_name,
})
Expand Down
2 changes: 2 additions & 0 deletions rama-tls/src/rustls/server/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ where
application_layer_protocol: conn_data_ref
.alpn_protocol()
.map(ApplicationProtocol::from),
// Currently not supported as this would mean we need to wrap rustls config
peer_certificate_chain: None,
});

ctx.insert(secure_transport);
Expand Down
Loading