xref: /aosp_15_r20/external/crosvm/devices/src/vtpm_proxy.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! vTPM Proxy backend using the vtpmd on ChromeOS to virtualize TPM commands.
6 
7 use std::time::Duration;
8 
9 use base::error;
10 use protobuf::Message;
11 use remain::sorted;
12 use system_api::client::OrgChromiumVtpm;
13 use system_api::vtpm_interface::SendCommandRequest;
14 use system_api::vtpm_interface::SendCommandResponse;
15 use thiserror::Error;
16 
17 use super::virtio::TpmBackend;
18 
19 // 5 minutes is the default timeout for tpm commands.
20 const VTPM_DBUS_TIMEOUT: Duration = Duration::from_secs(300);
21 
22 // The response of TPM_RC_INSUFFICIENT
23 const TPM_RC_INSUFFICIENT_RESPONSE: &[u8] = &[
24     0x80, 0x01, // TPM_ST_NO_SESSIONS
25     0x00, 0x00, 0x00, 0x0A, // Header Size = 10
26     0x00, 0x00, 0x00, 0x9A, // TPM_RC_INSUFFICIENT
27 ];
28 
29 // The response of TPM_RC_FAILURE
30 const TPM_RC_FAILURE_RESPONSE: &[u8] = &[
31     0x80, 0x01, // TPM_ST_NO_SESSIONS
32     0x00, 0x00, 0x00, 0x0A, // Header Size = 10
33     0x00, 0x00, 0x01, 0x01, // TPM_RC_FAILURE
34 ];
35 
36 /// A proxy object that connects to the vtpmd on ChromeOS.
37 pub struct VtpmProxy {
38     dbus_connection: Option<dbus::blocking::Connection>,
39     buf: Vec<u8>,
40 }
41 
42 impl VtpmProxy {
43     /// Returns a proxy that can be connected to the vtpmd.
new() -> Self44     pub fn new() -> Self {
45         VtpmProxy {
46             dbus_connection: None,
47             buf: Vec::new(),
48         }
49     }
50 
get_or_create_dbus_connection( &mut self, ) -> anyhow::Result<&dbus::blocking::Connection, dbus::Error>51     fn get_or_create_dbus_connection(
52         &mut self,
53     ) -> anyhow::Result<&dbus::blocking::Connection, dbus::Error> {
54         return match self.dbus_connection {
55             Some(ref dbus_connection) => Ok(dbus_connection),
56             None => {
57                 let dbus_connection = dbus::blocking::Connection::new_system()?;
58                 self.dbus_connection = Some(dbus_connection);
59                 return self.get_or_create_dbus_connection();
60             }
61         };
62     }
63 
try_execute_command(&mut self, command: &[u8]) -> anyhow::Result<(), Error>64     fn try_execute_command(&mut self, command: &[u8]) -> anyhow::Result<(), Error> {
65         let dbus_connection = self
66             .get_or_create_dbus_connection()
67             .map_err(Error::DBusError)?;
68 
69         let proxy = dbus_connection.with_proxy(
70             "org.chromium.Vtpm",
71             "/org/chromium/Vtpm",
72             VTPM_DBUS_TIMEOUT,
73         );
74 
75         let mut proto = SendCommandRequest::new();
76         proto.set_command(command.to_vec());
77 
78         let bytes = proto.write_to_bytes().map_err(Error::ProtobufError)?;
79 
80         let resp_bytes = proxy.send_command(bytes).map_err(Error::DBusError)?;
81 
82         let response =
83             SendCommandResponse::parse_from_bytes(&resp_bytes).map_err(Error::ProtobufError)?;
84 
85         self.buf = response.response().to_vec();
86 
87         Ok(())
88     }
89 }
90 
91 impl Default for VtpmProxy {
default() -> Self92     fn default() -> Self {
93         Self::new()
94     }
95 }
96 
97 impl TpmBackend for VtpmProxy {
execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8]98     fn execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8] {
99         match self.try_execute_command(command) {
100             Ok(()) => &self.buf,
101             Err(e) => {
102                 error!("{:#}", e);
103                 match e {
104                     Error::ProtobufError(_) => TPM_RC_INSUFFICIENT_RESPONSE,
105                     Error::DBusError(_) => TPM_RC_FAILURE_RESPONSE,
106                 }
107             }
108         }
109     }
110 }
111 
112 #[sorted]
113 #[derive(Error, Debug)]
114 enum Error {
115     #[error("D-Bus failure: {0:#}")]
116     DBusError(dbus::Error),
117     #[error("protocol buffers failure: {0:#}")]
118     ProtobufError(protobuf::Error),
119 }
120