summary refs log blame commit diff
path: root/tpm2/src/lib.rs
blob: e8d6a1e48d1925341e6559e74171b0d8b14f347b (plain) (tree)
1
2
3
4



                                                                         






































































































































































                                                                                                                                           
                                                                               


















































                                                                                
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::os::raw::{c_int, c_uint};
use std::ptr;
use std::slice;
use std::sync::atomic::{AtomicBool, Ordering};

static SIMULATOR_EXISTS: AtomicBool = AtomicBool::new(false);

/// A libtpm2-based TPM simulator.
///
/// At most one simulator may exist per process because libtpm2 uses a static
/// global response buffer.
///
/// # Examples
///
/// ```no_run
/// let mut simulator = tpm2::Simulator::singleton_in_current_directory();
///
/// let command = &[ /* ... */ ];
/// let response = simulator.execute_command(command);
/// println!("{:?}", response);
/// ```
pub struct Simulator {
    _priv: (),
}

impl Simulator {
    /// Initializes a TPM simulator in the current working directory.
    ///
    /// # Panics
    ///
    /// Panics if a TPM simulator has already been initialized by this process.
    pub fn singleton_in_current_directory() -> Self {
        let already_existed = SIMULATOR_EXISTS.swap(true, Ordering::SeqCst);
        if already_existed {
            panic!("libtpm2 simulator singleton already exists");
        }

        // Based on trunks:
        // https://chromium.googlesource.com/chromiumos/platform2/+/e4cf13c05773f3446bd76a13c4e37f0b80728711/trunks/tpm_simulator_handle.cc
        tpm_manufacture(true);
        plat_set_nv_avail();
        plat_signal_power_on();
        tpm_init();

        let mut simulator = Simulator { _priv: () };

        // Send TPM2_Startup(TPM_SU_CLEAR), ignore the result. This is normally
        // done by firmware.
        let startup_command = &[
            0x80, 0x01, // TPM_ST_NO_SESSIONS
            0x00, 0x00, 0x00, 0x0c, // commandSize = 12
            0x00, 0x00, 0x01, 0x44, // TPM_CC_Startup
            0x00, 0x00, // TPM_SU_CLEAR
        ];
        let _ = simulator.execute_command(startup_command);

        simulator
    }

    /// Sends a TPM command to the TPM simulator, waits for the work to be
    /// performed, and receives back the TPM response.
    ///
    /// Executing a command requires exclusive access to the TPM simulator
    /// because it mutates libtpm2 static state.
    ///
    /// The returned response buffer remains valid until the next TPM command is
    /// executed.
    #[must_use]
    pub fn execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8] {
        let request_size = command.len() as c_uint;
        let request = command.as_ptr() as *mut u8;
        let mut response_size: c_uint = 0;
        let mut response: *mut u8 = ptr::null_mut();

        // We need to provide the following guarantees in order for this block
        // of code to be safe:
        //
        //   - The TPM must have been initialized.
        //
        //   - There must not be a concurrently executing call to
        //     ExecuteCommand.
        //
        //   - The `request` pointer must be a valid pointer to `request_size`
        //     bytes of data that remain valid and constant for the full
        //     duration of the call to ExecuteCommand. The implementation may
        //     read up to `request_size` bytes of data from this address.
        //
        //   - The `response_size` pointer must be a valid pointer to a mutable
        //     unsigned int. The implementation will write the response buffer
        //     size to this address.
        //
        //   - The `response` pointer must be a valid pointer to a mutable
        //     unsigned char pointer. The implementation will write a pointer to
        //     the start of the response buffer to this address.
        //
        //   - No more than `response_size` bytes may be read from the response
        //     buffer after the call returns.
        //
        //   - Data may be read from the response buffer only until the next
        //     call to ExecuteCommand.
        //
        // The first guarantee is enforced by the public API of the Simulator
        // struct, and in particular the singleton_in_current_directory
        // constructor, which only makes a value of type Simulator available
        // outside of this module after TPM initialization has been performed.
        // Thus any Simulator on which the caller may be calling execute_command
        // from outside of this module is witness that initialization has taken
        // place.
        //
        // The second guarantee is made jointly by the &mut self reference in
        // execute_command and the singleton_in_current_directory constructor
        // which uses the SIMULATOR_EXISTS atomic flag to ensure that at most
        // one value of type Simulator is ever made available to code outside of
        // this module. Since at most one Simulator exists, and the caller is
        // holding an exclusive reference to a Simulator, we know that no other
        // code can be calling execute_command at the same time because they too
        // would need their own exclusive reference to the same Simulator. We
        // assume here that all use of libtpm2 within crosvm happens through the
        // safe bindings provided by this tpm2 crate, so that the codebase
        // contains no other unsafe calls to ExecuteCommand.
        //
        // The remaining guarantees are upheld by the signature and
        // implementation of execute_command. In particular, note the lifetime
        // 'a which ties the lifetime of the response slice we return to the
        // caller to the lifetime of their exclusively held reference to
        // Simulator. This signature looks the same to Rust as if the response
        // buffer were a field inside the Simulator struct, rather than a
        // statically allocated buffer inside libtpm2. As soon as the caller
        // "mutates" the Simulator by performing another call to
        // execute_command, the response buffer returned by the previous call is
        // assumed to be invalidated and is made inaccessible by the borrow
        // checker.
        //
        // Altogether we have guaranteed that execute_command is a safe
        // abstraction around unsafe code and is entirely safe to call from
        // outside of this module.
        //
        // Note additionally that the call to ExecuteCommand is over FFI so we
        // need to know that the signature declared by tpm2-sys is
        // ABI-compatible with the symbol provided by libtpm2.
        unsafe {
            tpm2_sys::ExecuteCommand(request_size, request, &mut response_size, &mut response);
            slice::from_raw_parts(response, response_size as usize)
        }
    }
}

fn tpm_manufacture(first_time: bool) {
    // From libtpm2 documentation:
    //
    //     This function initializes the TPM values in preparation for the TPM's
    //     first use. This function will fail if previously called. The TPM can
    //     be re-manufactured by calling TPM_Teardown() first and then calling
    //     this function again.
    //
    //     Arguments
    //
    //         firstTime: indicates if this is the first call from main()
    //
    //     Return value
    //
    //         0 = success
    //         1 = manufacturing process previously performed
    //
    // Unsafe only because this is over FFI and we need to know that the
    // signature declared by tpm2-sys is ABI-compatible with the symbol provided
    // by libtpm2. There are no other invariants to uphold.
    let ret: c_int = unsafe { tpm2_sys::TPM_Manufacture(first_time as c_int) };

    // We expect that the TPM must not already have been manufactured. The
    // SIMULATOR_EXISTS atomic flag guards calls to this function such that only
    // one call can ever be performed by a process.
    assert!(ret == 0);
}

fn plat_set_nv_avail() {
    // From libtpm2 documentation:
    //
    //     Set the current NV state to available. This function is for testing
    //     purpose only. It is not part of the platform NV logic.
    //
    // The "for testing purpose only" is unsettling but trunks performs the same
    // call during initialization so we trust that it is okay.
    //
    // Unsafe only because this is over FFI and we need to know that the
    // signature declared by tpm2-sys is ABI-compatible with the symbol provided
    // by libtpm2. There are no other invariants to uphold.
    unsafe {
        tpm2_sys::_plat__SetNvAvail();
    }
}

fn plat_signal_power_on() {
    // From libtpm2 documentation:
    //
    //     Signal platform power on.
    //
    // The libtpm2 implementation always returns 0 but does not document what
    // the return value means, so we aren't checking it.
    //
    // Unsafe only because this is over FFI and we need to know that the
    // signature declared by tpm2-sys is ABI-compatible with the symbol provided
    // by libtpm2. There are no other invariants to uphold.
    unsafe {
        let _: c_int = tpm2_sys::_plat__Signal_PowerOn();
    }
}

fn tpm_init() {
    // This function is not documented in libtpm2. Trunks performs the same call
    // during initialization so we trust that it is okay.
    //
    // Unsafe only because this is over FFI and we need to know that the
    // signature declared by tpm2-sys is ABI-compatible with the symbol provided
    // by libtpm2. There are no other invariants to uphold.
    unsafe {
        tpm2_sys::_TPM_Init();
    }
}