|
| 1 | +//! Data Center Attestation Primitives (DCAP) evidence generation and verification |
1 | 2 | use crate::attestation::{ |
2 | | - compute_report_input, generate_quote, get_quote_input_data, |
| 3 | + compute_report_input, |
3 | 4 | measurements::{CvmImageMeasurements, Measurements, PlatformMeasurements}, |
4 | | - AttestationError, AttestationType, QuoteGenerator, QuoteVerifier, PCS_URL, |
| 5 | + AttestationError, |
5 | 6 | }; |
6 | 7 |
|
7 | | -use dcap_qvl::{collateral::get_collateral_for_fmspc, quote::Quote}; |
| 8 | +use configfs_tsm::QuoteGenerationError; |
| 9 | +use dcap_qvl::{ |
| 10 | + collateral::get_collateral_for_fmspc, |
| 11 | + quote::{Quote, Report}, |
| 12 | +}; |
8 | 13 | use tokio_rustls::rustls::pki_types::CertificateDer; |
9 | 14 |
|
10 | | -/// Quote generation using configfs_tsm |
11 | | -#[derive(Clone)] |
12 | | -pub struct DcapTdxQuoteGenerator { |
13 | | - pub attestation_type: AttestationType, |
14 | | -} |
15 | | - |
16 | | -impl QuoteGenerator for DcapTdxQuoteGenerator { |
17 | | - /// Type of attestation used |
18 | | - fn attestation_type(&self) -> AttestationType { |
19 | | - self.attestation_type |
20 | | - } |
| 15 | +/// For fetching collateral directly from Intel, if no PCCS is specified |
| 16 | +const PCS_URL: &str = "https://api.trustedservices.intel.com"; |
21 | 17 |
|
22 | | - async fn create_attestation( |
23 | | - &self, |
24 | | - cert_chain: &[CertificateDer<'_>], |
25 | | - exporter: [u8; 32], |
26 | | - ) -> Result<Vec<u8>, AttestationError> { |
27 | | - let quote_input = compute_report_input(cert_chain, exporter)?; |
| 18 | +/// Quote generation using configfs_tsm |
| 19 | +pub async fn create_dcap_attestation( |
| 20 | + cert_chain: &[CertificateDer<'_>], |
| 21 | + exporter: [u8; 32], |
| 22 | +) -> Result<Vec<u8>, AttestationError> { |
| 23 | + let quote_input = compute_report_input(cert_chain, exporter)?; |
28 | 24 |
|
29 | | - Ok(generate_quote(quote_input)?) |
30 | | - } |
| 25 | + Ok(generate_quote(quote_input)?) |
31 | 26 | } |
32 | 27 |
|
33 | | -/// Verify DCAP TDX quotes, allowing them if they have one of a given set of platform-specific and |
34 | | -/// OS image specific measurements |
35 | | -#[derive(Clone)] |
36 | | -pub struct DcapTdxQuoteVerifier { |
37 | | - pub attestation_type: AttestationType, |
38 | | - /// Platform specific allowed Measurements |
39 | | - /// Currently an option as this may be determined internally on a per-platform basis (Eg: GCP) |
40 | | - pub accepted_platform_measurements: Option<Vec<PlatformMeasurements>>, |
41 | | - /// OS-image specific allows measurement - this is effectively a list of allowed OS images |
42 | | - pub accepted_cvm_image_measurements: Vec<CvmImageMeasurements>, |
43 | | - /// URL of a PCCS (defaults to Intel PCS) |
44 | | - pub pccs_url: Option<String>, |
45 | | -} |
| 28 | +/// Verify a DCAP TDX quote, and return the measurement values |
| 29 | +pub async fn verify_dcap_attestation( |
| 30 | + input: Vec<u8>, |
| 31 | + cert_chain: &[CertificateDer<'_>], |
| 32 | + exporter: [u8; 32], |
| 33 | + pccs_url: Option<String>, |
| 34 | +) -> Result<Measurements, AttestationError> { |
| 35 | + let quote_input = compute_report_input(cert_chain, exporter)?; |
| 36 | + let (platform_measurements, image_measurements) = if cfg!(not(test)) { |
| 37 | + let now = std::time::SystemTime::now() |
| 38 | + .duration_since(std::time::UNIX_EPOCH)? |
| 39 | + .as_secs(); |
| 40 | + let quote = Quote::parse(&input)?; |
46 | 41 |
|
47 | | -impl QuoteVerifier for DcapTdxQuoteVerifier { |
48 | | - /// Type of attestation used |
49 | | - fn attestation_type(&self) -> AttestationType { |
50 | | - self.attestation_type |
51 | | - } |
| 42 | + let ca = quote.ca()?; |
| 43 | + let fmspc = hex::encode_upper(quote.fmspc()?); |
| 44 | + let collateral = get_collateral_for_fmspc( |
| 45 | + &pccs_url.clone().unwrap_or(PCS_URL.to_string()), |
| 46 | + fmspc, |
| 47 | + ca, |
| 48 | + false, // Indicates not SGX |
| 49 | + ) |
| 50 | + .await?; |
52 | 51 |
|
53 | | - async fn verify_attestation( |
54 | | - &self, |
55 | | - input: Vec<u8>, |
56 | | - cert_chain: &[CertificateDer<'_>], |
57 | | - exporter: [u8; 32], |
58 | | - ) -> Result<Option<Measurements>, AttestationError> { |
59 | | - let quote_input = compute_report_input(cert_chain, exporter)?; |
60 | | - let (platform_measurements, image_measurements) = if cfg!(not(test)) { |
61 | | - let now = std::time::SystemTime::now() |
62 | | - .duration_since(std::time::UNIX_EPOCH)? |
63 | | - .as_secs(); |
64 | | - let quote = Quote::parse(&input)?; |
| 52 | + let _verified_report = dcap_qvl::verify::verify(&input, &collateral, now)?; |
65 | 53 |
|
66 | | - let ca = quote.ca()?; |
67 | | - let fmspc = hex::encode_upper(quote.fmspc()?); |
68 | | - let collateral = get_collateral_for_fmspc( |
69 | | - &self.pccs_url.clone().unwrap_or(PCS_URL.to_string()), |
70 | | - fmspc, |
71 | | - ca, |
72 | | - false, |
73 | | - ) |
74 | | - .await?; |
75 | | - |
76 | | - let _verified_report = dcap_qvl::verify::verify(&input, &collateral, now)?; |
| 54 | + let measurements = ( |
| 55 | + PlatformMeasurements::from_dcap_qvl_quote("e)?, |
| 56 | + CvmImageMeasurements::from_dcap_qvl_quote("e)?, |
| 57 | + ); |
| 58 | + if get_quote_input_data(quote.report) != quote_input { |
| 59 | + return Err(AttestationError::InputMismatch); |
| 60 | + } |
| 61 | + measurements |
| 62 | + } else { |
| 63 | + // In tests we use mock quotes which will fail to verify |
| 64 | + let quote = tdx_quote::Quote::from_bytes(&input)?; |
| 65 | + if quote.report_input_data() != quote_input { |
| 66 | + return Err(AttestationError::InputMismatch); |
| 67 | + } |
77 | 68 |
|
78 | | - let measurements = ( |
79 | | - PlatformMeasurements::from_dcap_qvl_quote("e)?, |
80 | | - CvmImageMeasurements::from_dcap_qvl_quote("e)?, |
81 | | - ); |
82 | | - if get_quote_input_data(quote.report) != quote_input { |
83 | | - return Err(AttestationError::InputMismatch); |
84 | | - } |
85 | | - measurements |
86 | | - } else { |
87 | | - // In tests we use mock quotes which will fail to verify |
88 | | - let quote = tdx_quote::Quote::from_bytes(&input)?; |
89 | | - if quote.report_input_data() != quote_input { |
90 | | - return Err(AttestationError::InputMismatch); |
91 | | - } |
| 69 | + ( |
| 70 | + PlatformMeasurements::from_tdx_quote("e), |
| 71 | + CvmImageMeasurements::from_tdx_quote("e), |
| 72 | + ) |
| 73 | + }; |
92 | 74 |
|
93 | | - ( |
94 | | - PlatformMeasurements::from_tdx_quote("e), |
95 | | - CvmImageMeasurements::from_tdx_quote("e), |
96 | | - ) |
97 | | - }; |
| 75 | + Ok(Measurements { |
| 76 | + platform: platform_measurements, |
| 77 | + cvm_image: image_measurements, |
| 78 | + }) |
| 79 | +} |
98 | 80 |
|
99 | | - if let Some(accepted_platform_measurements) = &self.accepted_platform_measurements |
100 | | - && !accepted_platform_measurements.contains(&platform_measurements) |
101 | | - { |
102 | | - return Err(AttestationError::UnacceptablePlatformMeasurements); |
103 | | - } |
| 81 | +/// Create a mock quote for testing on non-confidential hardware |
| 82 | +#[cfg(test)] |
| 83 | +fn generate_quote(input: [u8; 64]) -> Result<Vec<u8>, QuoteGenerationError> { |
| 84 | + let attestation_key = tdx_quote::SigningKey::random(&mut rand_core::OsRng); |
| 85 | + let provisioning_certification_key = tdx_quote::SigningKey::random(&mut rand_core::OsRng); |
| 86 | + Ok(tdx_quote::Quote::mock( |
| 87 | + attestation_key.clone(), |
| 88 | + provisioning_certification_key.clone(), |
| 89 | + input, |
| 90 | + b"Mock cert chain".to_vec(), |
| 91 | + ) |
| 92 | + .as_bytes()) |
| 93 | +} |
104 | 94 |
|
105 | | - if !self |
106 | | - .accepted_cvm_image_measurements |
107 | | - .contains(&image_measurements) |
108 | | - { |
109 | | - return Err(AttestationError::UnacceptableOsImageMeasurements); |
110 | | - } |
| 95 | +/// Create a quote |
| 96 | +#[cfg(not(test))] |
| 97 | +fn generate_quote(input: [u8; 64]) -> Result<Vec<u8>, QuoteGenerationError> { |
| 98 | + configfs_tsm::create_quote(input) |
| 99 | +} |
111 | 100 |
|
112 | | - Ok(Some(Measurements { |
113 | | - platform: platform_measurements, |
114 | | - cvm_image: image_measurements, |
115 | | - })) |
| 101 | +/// Given a [Report] get the input data regardless of report type |
| 102 | +fn get_quote_input_data(report: Report) -> [u8; 64] { |
| 103 | + match report { |
| 104 | + Report::TD10(r) => r.report_data, |
| 105 | + Report::TD15(r) => r.base.report_data, |
| 106 | + Report::SgxEnclave(r) => r.report_data, |
116 | 107 | } |
117 | 108 | } |
0 commit comments