devices/
vtpm_proxy.rs

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
7use std::time::Duration;
8
9use base::error;
10use protobuf::Message;
11use remain::sorted;
12use system_api::client::OrgChromiumVtpm;
13use system_api::vtpm_interface::SendCommandRequest;
14use system_api::vtpm_interface::SendCommandResponse;
15use thiserror::Error;
16
17use super::virtio::TpmBackend;
18
19// 5 minutes is the default timeout for tpm commands.
20const VTPM_DBUS_TIMEOUT: Duration = Duration::from_secs(300);
21
22// The response of TPM_RC_INSUFFICIENT
23const 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
30const 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.
37pub struct VtpmProxy {
38    dbus_connection: Option<dbus::blocking::Connection>,
39    buf: Vec<u8>,
40}
41
42impl VtpmProxy {
43    /// Returns a proxy that can be connected to the vtpmd.
44    pub fn new() -> Self {
45        VtpmProxy {
46            dbus_connection: None,
47            buf: Vec::new(),
48        }
49    }
50
51    fn get_or_create_dbus_connection(
52        &mut self,
53    ) -> anyhow::Result<&dbus::blocking::Connection, dbus::Error> {
54        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                self.get_or_create_dbus_connection()
60            }
61        }
62    }
63
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
91impl Default for VtpmProxy {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97impl TpmBackend for VtpmProxy {
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)]
114enum Error {
115    #[error("D-Bus failure: {0:#}")]
116    DBusError(dbus::Error),
117    #[error("protocol buffers failure: {0:#}")]
118    ProtobufError(protobuf::Error),
119}