Skip to content
Open
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
105 changes: 81 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,6 @@ impl ProxyServer {
attestation_verifier: AttestationVerifier,
client_auth: bool,
) -> Result<Self, ProxyError> {
if attestation_verifier.has_remote_attestion() && !client_auth {
return Err(ProxyError::NoClientAuth);
}

let mut server_config = if client_auth {
let root_store =
RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
Expand Down Expand Up @@ -204,7 +200,7 @@ impl ProxyServer {
None, // context
)?;

let input_data = compute_report_input(&cert_chain, exporter)?;
let input_data = compute_report_input(Some(&cert_chain), exporter)?;

// Get the TLS certficate chain of the client, if there is one
let remote_cert_chain = connection.peer_certificates().map(|c| c.to_owned());
Expand Down Expand Up @@ -234,10 +230,7 @@ impl ProxyServer {

// If we expect an attestaion from the client, verify it and get measurements
let measurements = if attestation_verifier.has_remote_attestion() {
let remote_input_data = compute_report_input(
&remote_cert_chain.ok_or(ProxyError::NoClientAuth)?,
exporter,
)?;
let remote_input_data = compute_report_input(remote_cert_chain.as_deref(), exporter)?;

attestation_verifier
.verify_attestation(remote_attestation_message, remote_input_data)
Expand Down Expand Up @@ -352,12 +345,6 @@ impl ProxyClient {
attestation_verifier: AttestationVerifier,
remote_certificate: Option<CertificateDer<'static>>,
) -> Result<Self, ProxyError> {
// If we will provide attestation, we must also use client auth
if attestation_generator.attestation_type != AttestationType::None && cert_and_key.is_none()
{
return Err(ProxyError::NoClientAuth);
}

// If a remote CA cert was given, use it as the root store, otherwise use webpki_roots
let root_store = match remote_certificate {
Some(remote_certificate) => {
Expand Down Expand Up @@ -620,7 +607,7 @@ impl ProxyClient {
.ok_or(ProxyError::NoCertificate)?
.to_owned();

let remote_input_data = compute_report_input(&remote_cert_chain, exporter)?;
let remote_input_data = compute_report_input(Some(&remote_cert_chain), exporter)?;

// Read a length prefixed attestation from the proxy-server
let mut length_bytes = [0; 4];
Expand All @@ -640,8 +627,7 @@ impl ProxyClient {

// If we are in a CVM, provide an attestation
let attestation = if attestation_generator.attestation_type != AttestationType::None {
let local_input_data =
compute_report_input(&cert_chain.ok_or(ProxyError::NoClientAuth)?, exporter)?;
let local_input_data = compute_report_input(cert_chain.as_deref(), exporter)?;
attestation_generator
.generate_attestation(local_input_data)
.await?
Expand Down Expand Up @@ -731,7 +717,7 @@ async fn get_tls_cert_with_config(

let remote_attestation_message = AttestationExchangeMessage::decode(&mut &buf[..])?;

let remote_input_data = compute_report_input(&remote_cert_chain, exporter)?;
let remote_input_data = compute_report_input(Some(&remote_cert_chain), exporter)?;

let _measurements = attestation_verifier
.verify_attestation(remote_attestation_message, remote_input_data)
Expand All @@ -743,12 +729,14 @@ async fn get_tls_cert_with_config(
/// Given a certificate chain and an exporter (session key material), build the quote input value
/// SHA256(pki) || exporter
pub fn compute_report_input(
cert_chain: &[CertificateDer<'_>],
cert_chain: Option<&[CertificateDer<'_>]>,
exporter: [u8; 32],
) -> Result<[u8; 64], AttestationError> {
let mut quote_input = [0u8; 64];
let pki_hash = get_pki_hash_from_certificate_chain(cert_chain)?;
quote_input[..32].copy_from_slice(&pki_hash);
if let Some(cert_chain) = cert_chain {
let pki_hash = get_pki_hash_from_certificate_chain(cert_chain)?;
quote_input[..32].copy_from_slice(&pki_hash);
}
quote_input[32..].copy_from_slice(&exporter);
Ok(quote_input)
}
Expand All @@ -770,8 +758,6 @@ fn get_pki_hash_from_certificate_chain(
/// An error when running a proxy client or server
#[derive(Error, Debug)]
pub enum ProxyError {
#[error("Client auth is required when the client is running in a CVM")]
NoClientAuth,
#[error("Failed to get server ceritifcate")]
NoCertificate,
#[error("TLS: {0}")]
Expand Down Expand Up @@ -1011,6 +997,77 @@ mod tests {
assert_eq!(measurements, mock_dcap_measurements());
}

// Server has no attestation, client has mock DCAP but no client auth
#[tokio::test]
async fn http_proxy_client_attestation_no_client_auth() {
let target_addr = example_http_service().await;

let (server_cert_chain, server_private_key) =
generate_certificate_chain("127.0.0.1".parse().unwrap());
let (server_config, client_config) =
generate_tls_config(server_cert_chain.clone(), server_private_key);

let proxy_server = ProxyServer::new_with_tls_config(
server_cert_chain,
server_config,
"127.0.0.1:0",
target_addr,
AttestationGenerator::with_no_attestation(),
AttestationVerifier::mock(),
)
.await
.unwrap();

let proxy_addr = proxy_server.local_addr().unwrap();

tokio::spawn(async move {
// Accept one connection, then finish
proxy_server.accept().await.unwrap();
});

let proxy_client = ProxyClient::new_with_tls_config(
client_config,
"127.0.0.1:0",
proxy_addr.to_string(),
AttestationGenerator::new_not_dummy(AttestationType::DcapTdx).unwrap(),
AttestationVerifier::expect_none(),
None,
)
.await
.unwrap();

let proxy_client_addr = proxy_client.local_addr().unwrap();

tokio::spawn(async move {
// Accept two connections, then finish
proxy_client.accept().await.unwrap();
proxy_client.accept().await.unwrap();
});

let res = reqwest::get(format!("http://{}", proxy_client_addr.to_string()))
.await
.unwrap();

// We expect no measurements from the server
let headers = res.headers();
assert!(headers.get(MEASUREMENT_HEADER).is_none());

let attestation_type = headers
.get(ATTESTATION_TYPE_HEADER)
.unwrap()
.to_str()
.unwrap();
assert_eq!(attestation_type, AttestationType::None.as_str());

let res_body = res.text().await.unwrap();

// The response body shows us what was in the request header (as the test http server
// handler puts them there)
let measurements =
MultiMeasurements::from_header_format(&res_body, AttestationType::DcapTdx).unwrap();
assert_eq!(measurements, mock_dcap_measurements());
}

// Server has mock DCAP, client has mock DCAP and client auth
#[tokio::test]
async fn http_proxy_mutual_attestation() {
Expand Down