libcros 0.6.5

A Rust library that provides easy-to-use functions for interacting with a Chrome device
Documentation
pub mod constants;

/*
  Ideally we'd want to use the constants from Tlcl, but
  there is always a chance the user could either not want
  all of Tlcl, or they're on TPM 1.2 so they wouldn't be
  able to access TPM 2.0 constants.
*/
pub mod tpm20 {
  pub mod types {
    pub const TPM_RC_BAD_TAG: u32 = 0x01E;
  }

  pub mod constants {
    pub const TPM_ST_NO_SESSIONS: u16 = 0x8001;
  }
}

use std::io::{Read, Write};

use constants::{
  CONFIG_EXTENSION_COMMAND, MAX_RX_BUF_SIZE, MAX_TX_BUF_SIZE, TPM_CC_VENDOR_BIT_MASK,
  TPM_ORDINAL_OFFSET, TPM_PKT_CMD_HEADER_SIZE, TPM_PKT_UPG_HEADER_SIZE, TPM_SUBCMD_OFFSET,
  VENDOR_RC_ERR, vendor_cmd_cc, vendor_cmd_rc,
};

use crate::{LOG_DBG, LOG_FATAL_NOEXIT};

pub fn send_command<W: Write>(
  tpm: &mut W,
  digest: u32,
  addr: u32,
  data: &[u8],
  subcmd: vendor_cmd_cc,
) -> bool {
  let mut outbuf = [0u8; MAX_TX_BUF_SIZE];
  let cc = subcmd as u32;

  outbuf[0..2].copy_from_slice(&tpm20::constants::TPM_ST_NO_SESSIONS.to_be_bytes());

  let ordinal = if cc <= vendor_cmd_cc::LAST_EXTENSION_COMMAND as u32 {
    CONFIG_EXTENSION_COMMAND
  } else {
    TPM_CC_VENDOR_BIT_MASK
  };
  outbuf[TPM_ORDINAL_OFFSET..TPM_ORDINAL_OFFSET + 4].copy_from_slice(&ordinal.to_be_bytes());
  outbuf[TPM_SUBCMD_OFFSET..TPM_SUBCMD_OFFSET + 2].copy_from_slice(&(cc as u16).to_be_bytes());

  let header_size = if subcmd == vendor_cmd_cc::EXTENSION_FW_UPGRADE {
    outbuf[TPM_PKT_CMD_HEADER_SIZE..TPM_PKT_CMD_HEADER_SIZE + 4]
      .copy_from_slice(&digest.to_ne_bytes());
    outbuf[TPM_PKT_CMD_HEADER_SIZE + 4..TPM_PKT_CMD_HEADER_SIZE + 8]
      .copy_from_slice(&addr.to_be_bytes());
    TPM_PKT_UPG_HEADER_SIZE
  } else {
    TPM_PKT_CMD_HEADER_SIZE
  };

  let len = header_size + data.len();
  outbuf[2..6].copy_from_slice(&(len as u32).to_be_bytes());
  outbuf[header_size..len].copy_from_slice(data);

  match tpm.write_all(&outbuf[..len]) {
    Ok(_) => true,
    Err(e) => {
      LOG_FATAL_NOEXIT!("Failed to write to GSC: {}", e);
      false
    }
  }
}

pub fn read_response<R: Read>(tpm: &mut R, response: &mut [u8]) -> vendor_cmd_rc {
  let mut raw = [0u8; MAX_RX_BUF_SIZE + TPM_PKT_UPG_HEADER_SIZE];
  let mut len = 0usize;

  loop {
    match tpm.read(&mut raw[len..]) {
      Ok(0) => break,
      Ok(n) => len += n,
      Err(e) => {
        LOG_FATAL_NOEXIT!("Failed to read from GSC: {}", e);
        return VENDOR_RC_ERR;
      }
    }
  }

  if len < TPM_PKT_CMD_HEADER_SIZE {
    LOG_FATAL_NOEXIT!("Problems reading from GSC, got {} bytes.", len);
    return VENDOR_RC_ERR;
  }

  let data_len = len - TPM_PKT_CMD_HEADER_SIZE;
  let copy_len = data_len.min(response.len());
  response[..copy_len]
    .copy_from_slice(&raw[TPM_PKT_CMD_HEADER_SIZE..TPM_PKT_CMD_HEADER_SIZE + copy_len]);

  let rv_bytes = [
    raw[TPM_ORDINAL_OFFSET],
    raw[TPM_ORDINAL_OFFSET + 1],
    raw[TPM_ORDINAL_OFFSET + 2],
    raw[TPM_ORDINAL_OFFSET + 3],
  ];
  let rv = u32::from_be_bytes(rv_bytes);
  let rv = if (rv & VENDOR_RC_ERR) == VENDOR_RC_ERR {
    rv & !VENDOR_RC_ERR
  } else {
    rv
  };

  LOG_DBG!("GSC response code: 0x{:x}", rv);
  rv
}