underhill_core/emuplat/
tpm.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use guest_emulation_transport::GuestEmulationTransportClient;
5use guest_emulation_transport::api::EventLogId;
6use openhcl_attestation_protocol::igvm_attest::get::AK_CERT_RESPONSE_BUFFER_SIZE;
7use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationVmConfig;
8use std::sync::Arc;
9use thiserror::Error;
10use tpm::ak_cert::RequestAkCert;
11use tpm::logger::TpmLogEvent;
12use tpm::logger::TpmLogger;
13use underhill_attestation::AttestationType;
14
15#[derive(Debug, Error)]
16pub enum TpmAttestationError {
17    #[error("failed to get a hardware attestation report")]
18    GetAttestationReport(#[source] tee_call::Error),
19    #[error("failed to create the IgvmAttest AK_CERT request")]
20    CreateAkCertRequest(#[source] underhill_attestation::IgvmAttestError),
21}
22
23/// An implementation of [`RequestAkCert`].
24pub struct TpmRequestAkCertHelper {
25    get_client: GuestEmulationTransportClient,
26    tee_call: Option<Arc<dyn tee_call::TeeCall>>,
27    attestation_type: AttestationType,
28    attestation_vm_config: AttestationVmConfig,
29    attestation_agent_data: Option<Vec<u8>>,
30}
31
32impl TpmRequestAkCertHelper {
33    pub fn new(
34        get_client: GuestEmulationTransportClient,
35        tee_call: Option<Arc<dyn tee_call::TeeCall>>,
36        attestation_type: AttestationType,
37        attestation_vm_config: AttestationVmConfig,
38        attestation_agent_data: Option<Vec<u8>>,
39    ) -> Self {
40        Self {
41            get_client,
42            tee_call,
43            attestation_type,
44            attestation_vm_config,
45            attestation_agent_data,
46        }
47    }
48}
49
50#[async_trait::async_trait]
51impl RequestAkCert for TpmRequestAkCertHelper {
52    fn create_ak_cert_request(
53        &self,
54        ak_pub_modulus: &[u8],
55        ak_pub_exponent: &[u8],
56        ek_pub_modulus: &[u8],
57        ek_pub_exponent: &[u8],
58        guest_input: &[u8],
59    ) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
60        let tee_type = match self.attestation_type {
61            AttestationType::Snp => Some(tee_call::TeeType::Snp),
62            AttestationType::Tdx => Some(tee_call::TeeType::Tdx),
63            AttestationType::Host => None,
64        };
65        let ak_cert_request_helper =
66            underhill_attestation::IgvmAttestRequestHelper::prepare_ak_cert_request(
67                tee_type,
68                ak_pub_exponent,
69                ak_pub_modulus,
70                ek_pub_exponent,
71                ek_pub_modulus,
72                &self.attestation_vm_config,
73                guest_input,
74            );
75
76        let attestation_report = if let Some(tee_call) = &self.tee_call {
77            tee_call
78                .get_attestation_report(ak_cert_request_helper.get_runtime_claims_hash())
79                .map_err(TpmAttestationError::GetAttestationReport)?
80                .report
81        } else {
82            vec![]
83        };
84
85        let request = ak_cert_request_helper
86            .create_request(&attestation_report)
87            .map_err(TpmAttestationError::CreateAkCertRequest)?;
88
89        // The request will be exposed to the guest (via nv index) for isolated VMs.
90        Ok(request)
91    }
92
93    async fn request_ak_cert(
94        &self,
95        request: Vec<u8>,
96    ) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync + 'static>> {
97        let agent_data = self.attestation_agent_data.clone().unwrap_or_default();
98        let result = self
99            .get_client
100            .igvm_attest(agent_data, request, AK_CERT_RESPONSE_BUFFER_SIZE)
101            .await?;
102        let payload = if !result.response.is_empty() {
103            underhill_attestation::parse_ak_cert_response(&result.response)?
104        } else {
105            // Let the caller to handle the empty response.
106            vec![]
107        };
108
109        Ok(payload)
110    }
111}
112
113/// An implementation of [`TpmLogger`].
114pub struct GetTpmLogger {
115    get_client: GuestEmulationTransportClient,
116}
117
118impl GetTpmLogger {
119    pub fn new(get_client: GuestEmulationTransportClient) -> Self {
120        Self { get_client }
121    }
122}
123
124fn convert_to_get_event_id(event: TpmLogEvent) -> EventLogId {
125    match event {
126        TpmLogEvent::AkCertRenewalFailed => EventLogId::CERTIFICATE_RENEWAL_FAILED,
127        TpmLogEvent::IdentityChangeFailed => EventLogId::TPM_IDENTITY_CHANGE_FAILED,
128        TpmLogEvent::InvalidState => EventLogId::TPM_INVALID_STATE,
129    }
130}
131
132#[async_trait::async_trait]
133impl TpmLogger for GetTpmLogger {
134    async fn log_event_and_flush(&self, event: TpmLogEvent) {
135        self.get_client
136            .event_log_fatal(convert_to_get_event_id(event))
137            .await
138    }
139
140    fn log_event(&self, event: TpmLogEvent) {
141        self.get_client.event_log(convert_to_get_event_id(event));
142    }
143}
144
145pub mod resources {
146    use super::GetTpmLogger;
147    use super::TpmRequestAkCertHelper;
148    use async_trait::async_trait;
149    use guest_emulation_transport::resolver::GetClientKind;
150    use mesh::MeshPayload;
151    use openhcl_attestation_protocol::igvm_attest::get::runtime_claims::AttestationVmConfig;
152    use std::sync::Arc;
153    use tpm::ak_cert::ResolvedRequestAkCert;
154    use tpm::logger::ResolvedTpmLogger;
155    use tpm_resources::RequestAkCertKind;
156    use tpm_resources::TpmLoggerKind;
157    use underhill_attestation::AttestationType;
158    use vm_resource::AsyncResolveResource;
159    use vm_resource::IntoResource;
160    use vm_resource::PlatformResource;
161    use vm_resource::ResolveError;
162    use vm_resource::ResourceId;
163    use vm_resource::ResourceResolver;
164    use vm_resource::declare_static_async_resolver;
165
166    #[derive(MeshPayload)]
167    pub struct GetTpmRequestAkCertHelperHandle {
168        attestation_type: AttestationType,
169        attestation_vm_config: AttestationVmConfig,
170        attestation_agent_data: Option<Vec<u8>>,
171    }
172
173    impl GetTpmRequestAkCertHelperHandle {
174        pub fn new(
175            attestation_type: AttestationType,
176            attestation_vm_config: AttestationVmConfig,
177            attestation_agent_data: Option<Vec<u8>>,
178        ) -> Self {
179            Self {
180                attestation_type,
181                attestation_vm_config,
182                attestation_agent_data,
183            }
184        }
185    }
186
187    impl ResourceId<RequestAkCertKind> for GetTpmRequestAkCertHelperHandle {
188        const ID: &'static str = "request_ak_cert";
189    }
190
191    pub struct GetTpmRequestAkCertHelperResolver;
192
193    declare_static_async_resolver! {
194        GetTpmRequestAkCertHelperResolver,
195        (RequestAkCertKind, GetTpmRequestAkCertHelperHandle)
196    }
197
198    #[async_trait]
199    impl AsyncResolveResource<RequestAkCertKind, GetTpmRequestAkCertHelperHandle>
200        for GetTpmRequestAkCertHelperResolver
201    {
202        type Output = ResolvedRequestAkCert;
203        type Error = ResolveError;
204
205        async fn resolve(
206            &self,
207            resolver: &ResourceResolver,
208            handle: GetTpmRequestAkCertHelperHandle,
209            _: &(),
210        ) -> Result<Self::Output, Self::Error> {
211            let get = resolver
212                .resolve::<GetClientKind, _>(PlatformResource.into_resource(), ())
213                .await?;
214
215            let tee_call: Option<Arc<dyn tee_call::TeeCall>> = match handle.attestation_type {
216                AttestationType::Snp => Some(Arc::new(tee_call::SnpCall)),
217                AttestationType::Tdx => Some(Arc::new(tee_call::TdxCall)),
218                AttestationType::Host => None,
219            };
220
221            Ok(TpmRequestAkCertHelper::new(
222                get,
223                tee_call,
224                handle.attestation_type,
225                handle.attestation_vm_config,
226                handle.attestation_agent_data,
227            )
228            .into())
229        }
230    }
231
232    #[derive(MeshPayload)]
233    pub struct GetTpmLoggerHandle;
234
235    impl ResourceId<TpmLoggerKind> for GetTpmLoggerHandle {
236        const ID: &'static str = "tpm_logger";
237    }
238
239    pub struct GetTpmLoggerResolver;
240
241    declare_static_async_resolver! {
242        GetTpmLoggerResolver,
243        (TpmLoggerKind, GetTpmLoggerHandle)
244    }
245
246    #[async_trait]
247    impl AsyncResolveResource<TpmLoggerKind, GetTpmLoggerHandle> for GetTpmLoggerResolver {
248        type Output = ResolvedTpmLogger;
249        type Error = ResolveError;
250
251        async fn resolve(
252            &self,
253            resolver: &ResourceResolver,
254            GetTpmLoggerHandle: GetTpmLoggerHandle,
255            _: &(),
256        ) -> Result<Self::Output, Self::Error> {
257            let get = resolver
258                .resolve::<GetClientKind, _>(PlatformResource.into_resource(), ())
259                .await?;
260
261            Ok(GetTpmLogger::new(get).into())
262        }
263    }
264}