Skip to content
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
26 changes: 19 additions & 7 deletions src/attestation/measurements.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::attestation::{AttestationError, AttestationType, AttestationVerifier};
use crate::attestation::{AttestationError, AttestationType};
use std::{collections::HashMap, path::PathBuf};

use dcap_qvl::quote::Report;
Expand All @@ -9,11 +9,14 @@ use thiserror::Error;
/// Measurements determined by the CVM platform
#[derive(Clone, PartialEq, Debug)]
pub struct PlatformMeasurements {
/// MRTD register value
pub mrtd: [u8; 48],
/// RTMR0 register value
pub rtmr0: [u8; 48],
}

impl PlatformMeasurements {
/// Given a quote from the dcap_qvl library, extract the platform measurements
pub fn from_dcap_qvl_quote(quote: &dcap_qvl::quote::Quote) -> Result<Self, AttestationError> {
let report = match quote.report {
Report::TD10(report) => report,
Expand All @@ -36,15 +39,19 @@ impl PlatformMeasurements {
}
}

/// Measurements determined by the CVM image
/// Measurements determined by the CVM image or application
#[derive(Clone, PartialEq, Debug)]
pub struct CvmImageMeasurements {
/// RTMR1 register value
pub rtmr1: [u8; 48],
/// RTMR2 register value
pub rtmr2: [u8; 48],
/// RTMR3 register value
pub rtmr3: [u8; 48],
}

impl CvmImageMeasurements {
/// Given a quote from the dcap_qvl library, extract the CVM image / application measurements
pub fn from_dcap_qvl_quote(quote: &dcap_qvl::quote::Quote) -> Result<Self, AttestationError> {
let report = match quote.report {
Report::TD10(report) => report,
Expand All @@ -69,13 +76,15 @@ impl CvmImageMeasurements {
}
}

/// A full set of measurement register values
#[derive(Debug, Clone, PartialEq)]
pub struct Measurements {
pub platform: PlatformMeasurements,
pub cvm_image: CvmImageMeasurements,
}

impl Measurements {
/// Convert to the JSON format used in HTTP headers
pub fn to_header_format(&self) -> Result<HeaderValue, MeasurementFormatError> {
let mut measurements_map = HashMap::new();
measurements_map.insert(0, hex::encode(self.platform.mrtd));
Expand All @@ -88,6 +97,7 @@ impl Measurements {
)?)?)
}

/// Parse the JSON used in HTTP headers
pub fn from_header_format(input: &str) -> Result<Self, MeasurementFormatError> {
let measurements_map: HashMap<u32, String> = serde_json::from_str(input)?;
let measurements_map: HashMap<u32, [u8; 48]> = measurements_map
Expand Down Expand Up @@ -126,6 +136,7 @@ impl Measurements {
}
}

/// An error when converting measurements / to or from HTTP header format
#[derive(Error, Debug)]
pub enum MeasurementFormatError {
#[error("JSON: {0}")]
Expand All @@ -144,17 +155,21 @@ pub enum MeasurementFormatError {
BadLength,
}

/// An accepted measurement value given in the measurements file
#[derive(Clone, Debug)]
pub struct MeasurementRecord {
/// An identifier, for example the name and version of the corresponding OS image
pub measurement_id: String,
/// The associated attestation platform
pub attestation_type: AttestationType,
/// The expected measurement register values
pub measurements: Measurements,
}

/// Given the path to a JSON file containing measurements, return a [Vec<MeasurementRecord>]
pub async fn get_measurements_from_file(
measurement_file: PathBuf,
) -> Result<AttestationVerifier, MeasurementFormatError> {
) -> Result<Vec<MeasurementRecord>, MeasurementFormatError> {
#[derive(Debug, Deserialize)]
struct MeasurementRecordSimple {
measurement_id: String,
Expand Down Expand Up @@ -202,10 +217,7 @@ pub async fn get_measurements_from_file(
});
}

Ok(AttestationVerifier {
accepted_measurements: measurements,
pccs_url: None,
})
Ok(measurements)
}

#[cfg(test)]
Expand Down
4 changes: 2 additions & 2 deletions src/attestation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,9 @@ pub struct AttestationVerifier {
///
/// If this is empty, anything will be accepted - but measurements are always injected into HTTP
/// headers, so that they can be verified upstream
accepted_measurements: Vec<MeasurementRecord>,
pub accepted_measurements: Vec<MeasurementRecord>,
/// A PCCS service to use - defaults to Intel PCS
pccs_url: Option<String>,
pub pccs_url: Option<String>,
}

impl AttestationVerifier {
Expand Down
35 changes: 25 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,12 @@ enum CliCommand {
/// Optional path to file containing JSON measurements to be enforced on the server
#[arg(long)]
server_measurements: Option<PathBuf>,
#[arg(long)]
/// Additional CA certificate to verify against (PEM) Defaults to no additional TLS certs.
#[arg(long)]
tls_ca_certificate: Option<PathBuf>,
/// The URL of a PCCS to use when verifying DCAP attestations. Defaults to Intel PCS.
#[arg(long)]
pccs_url: Option<String>,
// TODO missing:
// Name: "dev-dummy-dcap",
// EnvVars: []string{"DEV_DUMMY_DCAP"},
Expand Down Expand Up @@ -81,6 +84,9 @@ enum CliCommand {
/// Optional path to file containing JSON measurements to be enforced on the client
#[arg(long)]
client_measurements: Option<PathBuf>,
/// The URL of a PCCS to use when verifying DCAP attestations. Defaults to Intel PCS.
#[arg(long)]
pccs_url: Option<String>,
// TODO missing:
// Name: "listen-addr-healthcheck",
// EnvVars: []string{"LISTEN_ADDR_HEALTHCHECK"},
Expand All @@ -98,6 +104,9 @@ enum CliCommand {
/// Optional path to file containing JSON measurements to be enforced on the server
#[arg(long)]
server_measurements: Option<PathBuf>,
/// The URL of a PCCS to use when verifying DCAP attestations. Defaults to Intel PCS.
#[arg(long)]
pccs_url: Option<String>,
},
}

Expand Down Expand Up @@ -132,6 +141,7 @@ async fn main() -> anyhow::Result<()> {
client_attestation_type,
server_measurements,
tls_ca_certificate,
pccs_url,
} => {
let target_addr = target_addr
.strip_prefix("https://")
Expand All @@ -153,9 +163,10 @@ async fn main() -> anyhow::Result<()> {
};

let attestation_verifier = match server_measurements {
Some(server_measurements) => {
get_measurements_from_file(server_measurements).await?
}
Some(server_measurements) => AttestationVerifier {
accepted_measurements: get_measurements_from_file(server_measurements).await?,
pccs_url,
},
None => AttestationVerifier::do_not_verify(),
};

Expand Down Expand Up @@ -199,6 +210,7 @@ async fn main() -> anyhow::Result<()> {
client_auth,
server_attestation_type,
client_measurements,
pccs_url,
} => {
let tls_cert_and_chain =
load_tls_cert_and_key(tls_certificate_path, tls_private_key_path)?;
Expand All @@ -210,9 +222,10 @@ async fn main() -> anyhow::Result<()> {
let local_attestation_generator = server_attestation_type.get_quote_generator()?;

let attestation_verifier = match client_measurements {
Some(client_measurements) => {
get_measurements_from_file(client_measurements).await?
}
Some(client_measurements) => AttestationVerifier {
accepted_measurements: get_measurements_from_file(client_measurements).await?,
pccs_url,
},
None => AttestationVerifier::do_not_verify(),
};

Expand All @@ -235,11 +248,13 @@ async fn main() -> anyhow::Result<()> {
CliCommand::GetTlsCert {
server,
server_measurements,
pccs_url,
} => {
let attestation_verifier = match server_measurements {
Some(server_measurements) => {
get_measurements_from_file(server_measurements).await?
}
Some(server_measurements) => AttestationVerifier {
accepted_measurements: get_measurements_from_file(server_measurements).await?,
pccs_url,
},
None => AttestationVerifier::do_not_verify(),
};
let cert_chain = get_tls_cert(server, attestation_verifier).await?;
Expand Down