• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::os::raw::{c_int, c_uint};
6 use std::ptr;
7 use std::slice;
8 use std::sync::atomic::{AtomicBool, Ordering};
9 
10 static SIMULATOR_EXISTS: AtomicBool = AtomicBool::new(false);
11 
12 /// A libtpm2-based TPM simulator.
13 ///
14 /// At most one simulator may exist per process because libtpm2 uses a static
15 /// global response buffer.
16 ///
17 /// # Examples
18 ///
19 /// ```no_run
20 /// let mut simulator = tpm2::Simulator::singleton_in_current_directory();
21 ///
22 /// let command = &[ /* ... */ ];
23 /// let response = simulator.execute_command(command);
24 /// println!("{:?}", response);
25 /// ```
26 pub struct Simulator {
27     _priv: (),
28 }
29 
30 impl Simulator {
31     /// Initializes a TPM simulator in the current working directory.
32     ///
33     /// # Panics
34     ///
35     /// Panics if a TPM simulator has already been initialized by this process.
singleton_in_current_directory() -> Self36     pub fn singleton_in_current_directory() -> Self {
37         let already_existed = SIMULATOR_EXISTS.swap(true, Ordering::SeqCst);
38         if already_existed {
39             panic!("libtpm2 simulator singleton already exists");
40         }
41 
42         // Based on trunks:
43         // https://chromium.googlesource.com/chromiumos/platform2/+/e4cf13c05773f3446bd76a13c4e37f0b80728711/trunks/tpm_simulator_handle.cc
44         tpm_manufacture(true);
45         plat_set_nv_avail();
46         plat_signal_power_on();
47         tpm_init();
48 
49         let mut simulator = Simulator { _priv: () };
50 
51         // Send TPM2_Startup(TPM_SU_CLEAR), ignore the result. This is normally
52         // done by firmware.
53         let startup_command = &[
54             0x80, 0x01, // TPM_ST_NO_SESSIONS
55             0x00, 0x00, 0x00, 0x0c, // commandSize = 12
56             0x00, 0x00, 0x01, 0x44, // TPM_CC_Startup
57             0x00, 0x00, // TPM_SU_CLEAR
58         ];
59         let _ = simulator.execute_command(startup_command);
60 
61         simulator
62     }
63 
64     /// Sends a TPM command to the TPM simulator, waits for the work to be
65     /// performed, and receives back the TPM response.
66     ///
67     /// Executing a command requires exclusive access to the TPM simulator
68     /// because it mutates libtpm2 static state.
69     ///
70     /// The returned response buffer remains valid until the next TPM command is
71     /// executed.
72     #[must_use]
execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8]73     pub fn execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8] {
74         let request_size = command.len() as c_uint;
75         let request = command.as_ptr() as *mut u8;
76         let mut response_size: c_uint = 0;
77         let mut response: *mut u8 = ptr::null_mut();
78 
79         // We need to provide the following guarantees in order for this block
80         // of code to be safe:
81         //
82         //   - The TPM must have been initialized.
83         //
84         //   - There must not be a concurrently executing call to
85         //     ExecuteCommand.
86         //
87         //   - The `request` pointer must be a valid pointer to `request_size`
88         //     bytes of data that remain valid and constant for the full
89         //     duration of the call to ExecuteCommand. The implementation may
90         //     read up to `request_size` bytes of data from this address.
91         //
92         //   - The `response_size` pointer must be a valid pointer to a mutable
93         //     unsigned int. The implementation will write the response buffer
94         //     size to this address.
95         //
96         //   - The `response` pointer must be a valid pointer to a mutable
97         //     unsigned char pointer. The implementation will write a pointer to
98         //     the start of the response buffer to this address.
99         //
100         //   - No more than `response_size` bytes may be read from the response
101         //     buffer after the call returns.
102         //
103         //   - Data may be read from the response buffer only until the next
104         //     call to ExecuteCommand.
105         //
106         // The first guarantee is enforced by the public API of the Simulator
107         // struct, and in particular the singleton_in_current_directory
108         // constructor, which only makes a value of type Simulator available
109         // outside of this module after TPM initialization has been performed.
110         // Thus any Simulator on which the caller may be calling execute_command
111         // from outside of this module is witness that initialization has taken
112         // place.
113         //
114         // The second guarantee is made jointly by the &mut self reference in
115         // execute_command and the singleton_in_current_directory constructor
116         // which uses the SIMULATOR_EXISTS atomic flag to ensure that at most
117         // one value of type Simulator is ever made available to code outside of
118         // this module. Since at most one Simulator exists, and the caller is
119         // holding an exclusive reference to a Simulator, we know that no other
120         // code can be calling execute_command at the same time because they too
121         // would need their own exclusive reference to the same Simulator. We
122         // assume here that all use of libtpm2 within crosvm happens through the
123         // safe bindings provided by this tpm2 crate, so that the codebase
124         // contains no other unsafe calls to ExecuteCommand.
125         //
126         // The remaining guarantees are upheld by the signature and
127         // implementation of execute_command. In particular, note the lifetime
128         // 'a which ties the lifetime of the response slice we return to the
129         // caller to the lifetime of their exclusively held reference to
130         // Simulator. This signature looks the same to Rust as if the response
131         // buffer were a field inside the Simulator struct, rather than a
132         // statically allocated buffer inside libtpm2. As soon as the caller
133         // "mutates" the Simulator by performing another call to
134         // execute_command, the response buffer returned by the previous call is
135         // assumed to be invalidated and is made inaccessible by the borrow
136         // checker.
137         //
138         // Altogether we have guaranteed that execute_command is a safe
139         // abstraction around unsafe code and is entirely safe to call from
140         // outside of this module.
141         //
142         // Note additionally that the call to ExecuteCommand is over FFI so we
143         // need to know that the signature declared by tpm2-sys is
144         // ABI-compatible with the symbol provided by libtpm2.
145         unsafe {
146             tpm2_sys::ExecuteCommand(request_size, request, &mut response_size, &mut response);
147             slice::from_raw_parts(response, response_size as usize)
148         }
149     }
150 }
151 
tpm_manufacture(first_time: bool)152 fn tpm_manufacture(first_time: bool) {
153     // From libtpm2 documentation:
154     //
155     //     This function initializes the TPM values in preparation for the TPM's
156     //     first use. This function will fail if previously called. The TPM can
157     //     be re-manufactured by calling TPM_Teardown() first and then calling
158     //     this function again.
159     //
160     //     Arguments
161     //
162     //         firstTime: indicates if this is the first call from main()
163     //
164     //     Return value
165     //
166     //         0 = success
167     //         1 = manufacturing process previously performed
168     //
169     // Unsafe only because this is over FFI and we need to know that the
170     // signature declared by tpm2-sys is ABI-compatible with the symbol provided
171     // by libtpm2. There are no other invariants to uphold.
172     let ret: c_int = unsafe { tpm2_sys::TPM_Manufacture(first_time as c_int) };
173 
174     // We expect that the TPM must not already have been manufactured. The
175     // SIMULATOR_EXISTS atomic flag guards calls to this function such that only
176     // one call can ever be performed by a process.
177     assert!(ret == 0);
178 }
179 
plat_set_nv_avail()180 fn plat_set_nv_avail() {
181     // From libtpm2 documentation:
182     //
183     //     Set the current NV state to available. This function is for testing
184     //     purpose only. It is not part of the platform NV logic.
185     //
186     // The "for testing purpose only" is unsettling but trunks performs the same
187     // call during initialization so we trust that it is okay.
188     //
189     // Unsafe only because this is over FFI and we need to know that the
190     // signature declared by tpm2-sys is ABI-compatible with the symbol provided
191     // by libtpm2. There are no other invariants to uphold.
192     unsafe {
193         tpm2_sys::_plat__SetNvAvail();
194     }
195 }
196 
plat_signal_power_on()197 fn plat_signal_power_on() {
198     // From libtpm2 documentation:
199     //
200     //     Signal platform power on.
201     //
202     // The libtpm2 implementation always returns 0 but does not document what
203     // the return value means, so we aren't checking it.
204     //
205     // Unsafe only because this is over FFI and we need to know that the
206     // signature declared by tpm2-sys is ABI-compatible with the symbol provided
207     // by libtpm2. There are no other invariants to uphold.
208     unsafe {
209         let _: c_int = tpm2_sys::_plat__Signal_PowerOn();
210     }
211 }
212 
tpm_init()213 fn tpm_init() {
214     // This function is not documented in libtpm2. Trunks performs the same call
215     // during initialization so we trust that it is okay.
216     //
217     // Unsafe only because this is over FFI and we need to know that the
218     // signature declared by tpm2-sys is ABI-compatible with the symbol provided
219     // by libtpm2. There are no other invariants to uphold.
220     unsafe {
221         tpm2_sys::_TPM_Init();
222     }
223 }
224