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