libcros 0.6.4

A Rust library that provides easy-to-use functions for interacting with a Chrome device
Documentation
#![allow(non_snake_case)]

mod backend;
pub mod bytes;
mod client;
pub mod constants;

use std::{
  fs::OpenOptions,
  io::{Error, ErrorKind, Read, Write},
  thread,
  time::Duration,
};

use crate::{LOG_FATAL, keys, keyval::KvValue, kv_get};

pub const TPM_MAX_RETRIES: u32 = 5;
pub const TPM_RETRY_DELAY_MS: u64 = 100;

fn should_retry_io(err: &Error) -> bool {
  matches!(
    err.kind(),
    ErrorKind::PermissionDenied | ErrorKind::WouldBlock | ErrorKind::Interrupted
  ) || err.raw_os_error() == Some(16)
}

pub fn tpm_xmit(
  sendbuf: *const u8,
  send_size: usize,
  recvbuf: *mut u8,
  recv_len: *mut usize,
) -> u32 {
  const TPM_HEADER_SIZE: usize = 10;

  let tpm_path = match kv_get("string", keys::TPM_PATH) {
    Some(KvValue::String(s)) => s,
    _ => LOG_FATAL!("TPM_PATH missing or wrong type!"),
  };

  if tpm_path.is_empty() {
    LOG_FATAL!("TPM_PATH not set! cannot continue!");
  }

  let send_data = unsafe { core::slice::from_raw_parts(sendbuf, send_size) };

  for attempt in 0..TPM_MAX_RETRIES {
    let mut tpm_file = match OpenOptions::new().read(true).write(true).open(&tpm_path) {
      Ok(file) => file,
      Err(err) if should_retry_io(&err) && attempt + 1 < TPM_MAX_RETRIES => {
        thread::sleep(Duration::from_millis(TPM_RETRY_DELAY_MS));
        continue;
      }
      Err(_) => return constants::TPM_E_COMMUNICATION_ERROR,
    };

    if let Err(err) = tpm_file.write_all(send_data) {
      if should_retry_io(&err) && attempt + 1 < TPM_MAX_RETRIES {
        thread::sleep(Duration::from_millis(TPM_RETRY_DELAY_MS));
        continue;
      }
      return constants::TPM_E_COMMUNICATION_ERROR;
    }

    if recvbuf.is_null() || recv_len.is_null() {
      return constants::TPM_SUCCESS;
    }

    let recv_cap = unsafe { *recv_len };
    if recv_cap < TPM_HEADER_SIZE {
      return constants::TPM_E_RESPONSE_TOO_LARGE;
    }

    let recv_data = unsafe { core::slice::from_raw_parts_mut(recvbuf, recv_cap) };
    if let Err(err) = tpm_file.read_exact(&mut recv_data[..TPM_HEADER_SIZE]) {
      if should_retry_io(&err) && attempt + 1 < TPM_MAX_RETRIES {
        thread::sleep(Duration::from_millis(TPM_RETRY_DELAY_MS));
        continue;
      }
      return constants::TPM_E_COMMUNICATION_ERROR;
    }

    let total_size =
      u32::from_be_bytes([recv_data[2], recv_data[3], recv_data[4], recv_data[5]]) as usize;
    if total_size < TPM_HEADER_SIZE {
      return constants::TPM_E_COMMUNICATION_ERROR;
    }
    if total_size > recv_cap {
      unsafe {
        *recv_len = total_size;
      }
      return constants::TPM_E_RESPONSE_TOO_LARGE;
    }

    if let Err(err) = tpm_file.read_exact(&mut recv_data[TPM_HEADER_SIZE..total_size]) {
      if should_retry_io(&err) && attempt + 1 < TPM_MAX_RETRIES {
        thread::sleep(Duration::from_millis(TPM_RETRY_DELAY_MS));
        continue;
      }
      return constants::TPM_E_COMMUNICATION_ERROR;
    }

    unsafe {
      *recv_len = total_size;
    }
    return constants::TPM_SUCCESS;
  }

  constants::TPM_E_COMMUNICATION_ERROR
}

pub fn vb2ex_tpm_send_recv(
  request: *const u8,
  request_length: u32,
  response: *mut u8,
  response_length: *mut u32,
) -> u32 {
  let mut len: usize = unsafe { *response_length } as usize;

  if tpm_xmit(request, request_length as usize, response, &mut len) != constants::TPM_SUCCESS {
    return constants::TPM_E_COMMUNICATION_ERROR;
  }

  if len > unsafe { *response_length } as usize {
    return constants::TPM_E_RESPONSE_TOO_LARGE;
  }

  unsafe {
    *response_length = len as u32;
  }
  constants::TPM_SUCCESS
}

pub mod commands;
pub mod exports;
pub mod permissions;

#[cfg(feature = "tpm1_2")]
pub mod tpm12;

#[cfg(feature = "tpm2_0")]
pub mod tpm20;

#[cfg(feature = "tpm1_2")]
pub mod v1_2;

#[cfg(feature = "tpm2_0")]
pub mod v2_0;

#[cfg(all(not(feature = "tpm1_2"), not(feature = "tpm2_0")))]
pub mod stubs;

#[allow(unused_imports)]
pub use exports::*;
#[cfg(any(feature = "tpm1_2", feature = "tpm2_0"))]
#[allow(unused_imports)]
pub use permissions::*;

#[cfg(all(feature = "tpm2_0", feature = "tpm1_2"))]
pub fn TlclGetTPMVersion() -> String {
  "2.0".to_string()
}

#[cfg(all(feature = "tpm2_0", not(feature = "tpm1_2")))]
pub fn TlclGetTPMVersion() -> String {
  "2.0".to_string()
}

#[cfg(all(feature = "tpm1_2", not(feature = "tpm2_0")))]
pub fn TlclGetTPMVersion() -> String {
  "1.2".to_string()
}

#[cfg(not(any(feature = "tpm1_2", feature = "tpm2_0")))]
pub fn TlclGetTPMVersion() -> String {
  "No TPM version was specified when compiling libcros.".to_string()
}