Skip to content

Commit

Permalink
Ability to store peer certificate chain (#362)
Browse files Browse the repository at this point in the history
Ability to store peer certificate chain
  • Loading branch information
soundofspace authored Dec 16, 2024
1 parent c7f10e9 commit d8eaa74
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 12 deletions.
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")]
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()
{
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

0 comments on commit d8eaa74

Please sign in to comment.